1.我的页面
1.代码
// pages/my/my.js
Page({
/**
* 页面的初始数据
*/
data: {
orderItem: [
{
url: '/pages/order/list/list',
title: '商品订单',
iconfont: 'icon-dingdanshangpindingdan'
},
{
url: '/pages/order/list/list',
title: '礼品卡订单',
iconfont: 'icon-lipinka'
},
{
url: '/pages/order/list/list',
title: '退款/售后',
iconfont: 'icon-tuikuan'
}
]
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
{
"usingComponents": {},
"navigationStyle": "custom"
}
/* pages/my/my.wxss */
.container{
background-color: whitesmoke;
height: 100vh;
.top-show{
width: 100%;
height: 360rpx;
.top-show-img{
width: 100%;
height: 100%;
}
}
.bottom-show{
position: relative;
top: -150rpx;
border-radius: 20rpx;
margin: 0 auto;
width: 96%;
background-color: white;
.user-container{
display: flex;
align-items: center;
.avatar-container{
margin: 20rpx;
.avatar{
height: 120rpx;
width: 120rpx;
}
}
.no-login{
display: flex;
flex-direction: column;
font-size: 24rpx;
color: gray;
text:first-child {
font-size:28rpx
}
}
}
}
.order{
border-radius: 20rpx;
margin: 0 auto;
width: 96%;
background-color: white;
margin-top: -80rpx;
.order_title{
display: flex;
// justify-content: 设置主轴上的排列方式;
justify-content: space-between;
padding: 40rpx;
.more{
font-size: 30rpx;
color: #ccc;
}
}
.order-content{
display: flex;
justify-content: space-evenly;
text-align: center;
padding-bottom: 24rpx;
.iconfont{
font-size: 60rpx;
}
text{
font-size: 26rpx;
}
}
}
.after-scale{
border-radius: 20rpx;
margin: 0 auto;
width: 96%;
background-color: white;
margin-top: 20rpx;
padding-bottom: 25rpx;
.order-title-wrap{
padding: 20rpx 0 0 20rpx;
}
.after-scale-item{
display: flex;
margin: 30rpx 25rpx;
text{
font-size: 25rpx;
color: #999;
margin-left: 20rpx;
}
.iconfont{
color: #a2b364;
}
}
}
.info-footer{
text-align: center;
font-size: 24rpx;
color: #999;
margin-top: 30rpx;
}
}
<view class="container">
<!-- 顶部展示图 -->
<view class="top-show">
<image src="../../assets/images/banner.jpg" mode="widthFix" class="top-show-img"/>
</view>
<!-- 白色背景面板 -->
<view class="bottom-show">
<!-- 未登录面板 -->
<view class="user-container">
<!-- 左边头像区域 -->
<view class="avatar-container">
<image src="../../assets/images/avatar.png" mode="" class="avatar"/>
</view>
<!-- 右边头像区域 -->
<view class="no-login">
<view>未登录</view>
<view>点击授权登录</view>
</view>
</view>
</view>
<!-- 已登陆面板 -->
<!-- 订单面板 -->
<view class="order">
<!-- 订单面板标题部分 -->
<view class="order_title">
<text class="title">我的订单</text>
<text class="more">查看更多></text>
</view>
<!-- 订单面板内容部分 -->
<view class="order-content">
<view class="order-content-item" wx:for="{{orderItem}}" wx:key="index">
<!-- item指数组内的项 -->
<navigator url="/pages/tist/tist">
<view class="iconfont {{item.iconfont}}"></view>
<text>{{item.title}}</text>
</navigator>
</view>
</view>
</view>
<!-- 关于售前售后服务面板 -->
<view class="after-scale section">
<view class="order-title-wrap">
<text class="title">关于售前售后服务</text>
</view>
<view class="after-scale-item">
<view class="iconfont icon-kefufenxiermaikefu"></view>
<text>可与小程序客服实时聊天或电话咨询</text>
</view>
<view class="after-scale-item">
<view class="iconfont icon-shijian"></view>
<text>小程序客服工作时间为: 8:30 ~ 20:30</text>
</view>
<view class="after-scale-item">
<view class="iconfont icon-dizhiguanli"></view>
<text>鲜花制作完毕情况下暂不支持退款</text>
</view>
<view class="after-scale-item">
<view class="iconfont icon-zhangben"></view>
<text>鲜花可以提前7-15天预订重大节假日不支持定时配送</text>
</view>
</view>
<!-- 底部面板 -->
<view class="info-footer">智谷星图技术支持</view>
</view>
2.效果图
3.app.json引入图标
{
"pages": [
"pages/my/my",
"pages/index/index",
"pages/cart/cart",
"pages/goods/goods",
"pages/category/category",
"pages/index/banner/banner",
"pages/index/entrance/entrance",
"components/goods-card/goods-card",
"components/goods-list/goods-list",
"pages/list/list",
"pages/tist/tist",
"pages/test/test"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "慕尚花坊",
"navigationBarBackgroundColor": "#FF734C"
},
"tabBar": {
"color": "#252933",
"selectedColor": "#FF734C",
"backgroundColor": "#ffffff",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "assets/tabbar/index.png",
"selectedIconPath": "assets/tabbar/index-active.png"
},
{
"pagePath": "pages/category/category",
"text": "分类",
"iconPath": "assets/tabbar/cate.png",
"selectedIconPath": "assets/tabbar/cate-active.png"
},
{
"pagePath": "pages/cart/cart",
"text": "购物车",
"iconPath": "assets/tabbar/cart.png",
"selectedIconPath": "assets/tabbar/cart-active.png"
},
{
"pagePath": "pages/my/my",
"text": "我的",
"iconPath": "assets/tabbar/my.png",
"selectedIconPath": "assets/tabbar/my-active.png"
}
]
},
"sitemapLocation": "sitemap.json",
"lazyCodeLoading": "requiredComponents",
"usingComponents": {
"van-button": "@vant/weapp/button/index",
"van-card": "@vant/weapp/card/index",
"van-submit-bar": "@vant/weapp/submit-bar/index",
"van-checkbox": "@vant/weapp/checkbox/index",
"van-checkbox-group": "@vant/weapp/checkbox-group/index",
"van-stepper": "@vant/weapp/stepper/index",
"van-empty": "@vant/weapp/empty/index",
"van-goods-action": "@vant/weapp/goods-action/index",
"van-goods-action-icon": "@vant/weapp/goods-action-icon/index",
"van-goods-action-button": "@vant/weapp/goods-action-button/index",
"van-action-sheet": "@vant/weapp/action-sheet/index",
"van-panel": "@vant/weapp/panel/index"
}
}
2. 购物车页面
1.代码
Page({
/**
* 页面的初始数据
*/
data:{
carList:[1]
},
navigateBtn:function(){
wx.navigateTo({
url: '/miniprogram/components/good',
success:function(res){
console.log(res);
},
fail:function(){
},
complete:function(){
}
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
{
"component": true,
"styleIsolation": "shared",
"usingComponents": {}
}
.container {
background-color: whitesmoke;
height: 100vh;
}
.carList-container{
.carList-container-cell {
.goods-info {
display: flex;
background-color: white;
border-radius: 16rpx;
margin: 20rpx 20rpx 10rpx 20rpx;
padding: 24rpx 16rpx;
.left{
display: flex;
align-items: center;
}
.mid {
width: 300rpx;
height: 300rpx;
image {
width: 100%;
height: 100%;
}
}
}
.right {
display: flex;
flex-direction: column;
height: 300rpx;
justify-content: space-between;
padding-left: 20rpx;
.title{
font-size: 26rpx;
}
.buy{
display: flex;
justify-content: space-between;
.price{
font-size: 30rpx;
color: #fa4126;
}
}
}
}
}
.van-empty__bottom{
display: flex;
height: 250rpx;
flex-direction: column;
justify-content: space-between;
}
<view class="container">
<!-- 购物车列表区域 -->
<view class="carList-container" wx:if="{{carList.length}}">
<view class="carList-container-cell" wx:for="{{carList}}" wx:key="index">
<van-swipe-cell right-width="{{ 65 }}" left-width="{{ 65 }}">
<van-cell-group>
<view class="goods-info">
<view class="left">
<van-checkbox value="{{ false }}" checked-color="#e60017" bind:change="onChange">
</van-checkbox>
</view>
<view class="mid">
<image src="../../assets/images/floor-img.jpg" mode="" />
</view>
<view class="right">
<view class="title">【11支红玫瑰】买花就送女友送爱人送老婆11111111111</view>
<view class="buy">
<view class="price">¥99.99</view>
<view class="buy-btn">
<van-stepper value="{{ 1 }}" bind:change="onChange" />
</view>
</view>
</view>
</view>
</van-cell-group>
</van-swipe-cell>
</view>
</view>
<!-- 购物车列表为空的情况 -->
<van-empty description="还没有商品,快去添加吧~" wx:else="">
<navigator url="">
<van-button type="danger" round bindtap="navigateBtn">去购物</van-button>
</navigator>
<navigator url="">
<van-button type="danger" round>去登录</van-button>
</navigator>
</van-empty>
<!-- 提交订单栏区域 -->
<van-submit-bar price="{{ 3050 }}" button-text="提交订单" bind:submit="onClickButton" tip="{{ true }}">
<van-checkbox value="{{ true }}" checked-color="#e60017" bind:change="onChange">
全选
</van-checkbox>
</van-submit-bar>
</view>
2.效果图
3.app.json引入图标
{
"pages": [
"pages/my/my",
"pages/index/index",
"pages/cart/cart",
"pages/goods/goods",
"pages/category/category",
"pages/index/banner/banner",
"pages/index/entrance/entrance",
"components/goods-card/goods-card",
"components/goods-list/goods-list",
"pages/list/list",
"pages/tist/tist",
"pages/test/test"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "慕尚花坊",
"navigationBarBackgroundColor": "#FF734C"
},
"tabBar": {
"color": "#252933",
"selectedColor": "#FF734C",
"backgroundColor": "#ffffff",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "assets/tabbar/index.png",
"selectedIconPath": "assets/tabbar/index-active.png"
},
{
"pagePath": "pages/category/category",
"text": "分类",
"iconPath": "assets/tabbar/cate.png",
"selectedIconPath": "assets/tabbar/cate-active.png"
},
{
"pagePath": "pages/cart/cart",
"text": "购物车",
"iconPath": "assets/tabbar/cart.png",
"selectedIconPath": "assets/tabbar/cart-active.png"
},
{
"pagePath": "pages/my/my",
"text": "我的",
"iconPath": "assets/tabbar/my.png",
"selectedIconPath": "assets/tabbar/my-active.png"
}
]
},
"sitemapLocation": "sitemap.json",
"lazyCodeLoading": "requiredComponents",
"usingComponents": {
"van-button": "@vant/weapp/button/index",
"van-card": "@vant/weapp/card/index",
"van-submit-bar": "@vant/weapp/submit-bar/index",
"van-checkbox": "@vant/weapp/checkbox/index",
"van-checkbox-group": "@vant/weapp/checkbox-group/index",
"van-stepper": "@vant/weapp/stepper/index",
"van-empty": "@vant/weapp/empty/index",
"van-goods-action": "@vant/weapp/goods-action/index",
"van-goods-action-icon": "@vant/weapp/goods-action-icon/index",
"van-goods-action-button": "@vant/weapp/goods-action-button/index",
"van-action-sheet": "@vant/weapp/action-sheet/index",
"van-panel": "@vant/weapp/panel/index"
}
}
3.商品列表页面
1.代码
// pages/list/list.js
Page({
/**
* 页面的初始数据
*/
data: {
isFinish:"true"
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
{
"usingComponents": {
"goods-card":"../../components/goods-card/goods-card"
}
}
/* pages/list/list.wxss */
.container{
.goods-list{
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
}
.finish{
font-size: 28rpx;
text-align: center;
color: #f3514f;
line-height: 80rpx;
border-top: 1px solid black;
}
}
<!--pages/list/list.wxml-->
<view class="container">
<!-- 用户列表区域 -->
<view class="goods-list">
<block>
<goods-card></goods-card>
<goods-card></goods-card>
<goods-card></goods-card>
<goods-card></goods-card>
</block>
</view>
<!-- 文字区域 -->
<!-- hidden属性控制文字的显示与隐藏 -->
<!-- 当数据加载完毕,isFinish为true,!isFinish为false,则这句话不隐藏,用户能看到这句话 -->
<view class="finish" hidden="{{!isFinish}}">数据加载完毕了~~</view>
</view>
2.效果图
4.商品细节页面
1.代码
// pages/detail/detail.js
Page({
/**
* 页面的初始数据
*/
data: {
},
// 点击按钮事件,控制面板显示。
handelSheet(){
this.setData ({
show:true
})
},
onClose(){
this.setData ({
show:false
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
.container {
background-color: whitesmoke;
height: 100vh;
.banner-img {
height: 800rpx;
image {
height: 100%;
}
}
// 商品基本信息
.content {
background-color: white;
// margin 设置外边距
margin: 0 20rpx;
// padding 设置内边距
padding: 40rpx;
// 设置圆角弧度
border-radius: 20rpx;
// 相对于原本的位置发生改变是相对定位
// 相对于父盒子位置发生改变是绝对定位
position: relative; //相对定位
top: -200rpx;
.price {
display: flex;
.price-num {
color: #fa4126;
font-weight: bolder;
font-size: 18px;
}
.price-origin-num {
color: #b4babf;
text-decoration: line-through;
font-size: 12px;
margin-left: 15px;
margin-top: 4px;
}
}
.title {
// 溢出隐藏
overflow: hidden;
// 超过一行就算溢出
white-space: nowrap;
// 溢出之后的文本
text-overflow: ellipsis;
font-size: 16px;
font-weight: bolder;
margin: 20rpx 0;
}
.info {
// 溢出隐藏
overflow: hidden;
// 超过一行就算溢出
white-space: nowrap;
// 溢出之后的文本
text-overflow: ellipsis;
font-size: 12px;
color: #9999;
}
}
// 商品的详细信息
.detail {
background-color: white;
padding: 20rpx;
margin: -160rpx 20rpx 0 20rpx;
border-radius: 20rpx;
image {
width: 100%;
height: 700rpx;
}
}
.sheet-container {
margin: 20rpx;
padding: 20rpx;
border-radius: 20rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
.img {
height: 400rpx;
width: 350rpx;
image {
width: 100%;
height: 100%;
}
}
.buy {
display: flex;
justify-content: space-between;
.price {
display: flex;
color: #fa4126;
}
}
.baseInfo {
display: flex;
// 设置主轴方向
flex-direction: column;
// 设置主轴上的排列方式
justify-content: space-between;
margin-left: 20rpx;
.title {
font-size: 14px;
}
}
}
.textArea {
background-color: white;
margin: 20rpx 20rpx;
.title {
margin-right: 30rpx;
margin-bottom: 20rpx;
}
.box {
background-color: whitesmoke;
width: 92%;
margin: 0 auto;
border-radius: 20rpx;
padding: 20rpx;
}
}
.btn{
width: 90%;
margin: 20rpx auto;
}
}
<!--pages/goods/goods.wxml-->
<view class="container">
<!-- 商品大图 -->
<view class="banner-img">
<image src="../../assets/images/floor.jpg" mode="" />
</view>
<!-- 商品的基本信息 -->
<view class="content">
<!-- 商品标题 -->
<view class="title">亲爱的/情人节网红款/19枝</view>
<!-- 详细信息 -->
<view class=" info">情⼈节新品情⼈节新品情⼈节新品情⼈节新品</view>
<!-- 商品价格 -->
<view class="price">
<view class="price-num">¥299</view>
<view class="price-origin-num">¥399</view>
</view>
</view>
<!-- 商品的详细信息 -->
<view class="detail">
<image src="../../assets/images/floor-img.jpg" mode="" />
<image src="../../assets/images/floor-img.jpg" mode="" />
<image src="../../assets/images/floor-img.jpg" mode="" />
</view>
<!-- 商品的底部导航栏 -->
<van-goods-action>
<!-- 如果navigator标签跳转到tabber栏 则需要加open-type -->
<navigator url="/pages/index/index" open-type="'switchTab">
<van-goods-action-icon icon="wap-home-o" text="首页" />
</navigator>
<van-goods-action-icon icon="chat-o" text="客服" dot />
<van-goods-action-icon icon="cart-o" text="购物车" info="5" />
<van-goods-action-button text="加入购物车" type="warning" bind:tap="handelSheet" />
<!-- handelSheel为点击按钮事件 -->
<van-goods-action-button text="立即购买" bind:tap="handelSheet" />
</van-goods-action>
<!-- 自定义面板 -->
<!-- bind:close为面板关闭事件 -->
<van-action-sheet show="{{ show }}" bind:close="onClose" position="bottom">
<view class="sheet-container">
<view class="img">
<image src="../../assets/images/floor-img.jpg" mode="" />
</view>
<!-- 商品基本信息区域 -->
<view class="baseInfo">
<!-- 商品名称 -->
<view class="title">亲爱的/情人节网红款/19支玫瑰</view>
<!-- 商品价值区域 -->
<view class="buy">
<view class="price">
<view class="symbol">¥</view>
<view class="num">100</view>
</view>
<view class="buyBtn">
<van-stepper value="{{1}}" bind:change="onChange" />
</view>
</view>
</view>
</view>
<!-- 商品祝福语区域 -->
<view class="textArea">
<view class="title">祝福语</view>
<textarea value="" placeholder="请输入你的祝福" class="box" />
</view>
<view class="btn">
<van-button type="primary" round size="large">确定</van-button>
</view>
</van-action-sheet>
</view>
2.效果图
5.订单页面
1.代码
// pages/tist/tist.js
Page({
/**
* 页面的初始数据
*/
data: {
orderList:[1,2,3]
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
/* pages/tist/tist.wxss */
.container {
background-color: whitesmoke;
height: 100vh;
.order-list {
.order-item {
width: 90%;
height: 380rpx;
margin: 20rpx auto;
padding: 20rpx;
border-radius: 20rpx;
background-color: white;
position: relative;
.top {
display: flex;
justify-content: space-between;
margin-bottom: 10rpx;
font-size: 28rpx;
font-weight: normal;
color: #333333;
}
.middle {
display: flex;
.img {
height: 200rpx;
width: 250rpx;
image {
width: 100%;
}
}
.text {
margin: 0 220rpx 0 20rpx;
.goods-name {
font-size: 28rpx;
color: #333;
line-height: 40rpx;
font-weight: 400;
}
.goods-blessing {
font-size: 24rpx;
height: 32rpx;
line-height: 32rpx;
color: #999999;
margin: 8rpx 0;
}
}
.number {
.goods-price {
white-space: nowrap;
color: #fa4126;
font-size: 24rpx;
line-height: 48rpx;
}
.goods-count {
white-space: nowrap;
order: 4;
text-align: right;
font-size: 24rpx;
color: #999;
margin: 20rpx 0 0 auto;
}
}
}
.bottom {
position: absolute;
right: 50rpx;
bottom: 50rpx;
.total-price {
display: flex;
.text {
font-size: 28rpx;
color: #333333;
margin-right: 10px;
}
.price {
font-size: 32rpx;
color: #fa4126;
font-weight: bold;
}
}
}
}
}
}
<!--pages/tist/tist.wxml-->
<view class="container" wx:if="{{ orderList.length }}">
<view class="order-list" wx:for="{{orderList}}" wx:key="index">
<view class="order-item">
<view class="top">
<view class="order-num">订单号<text class="num">679246470200</text></view>
<view class="order-status">已支付</view>
</view>
<view class="middle">
<view class="img">
<image src="../../assets/images/floor-img.jpg" mode="widthFix" />
</view>
<view class="text">
<view class="goods-name">不变的承诺</view>
<view class="goods-blessing">不变的承诺</view>
</view>
<view class="number">
<view class="goods-price">¥100</view>
<view class="goods-count">x 1</view>
</view>
<view class="bottom">
<view class="total-price">
<view class="text">实付</view>
<view class="price"><text>¥</text>666</view>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 购物车列表为空的情况 -->
<van-empty description="还没有购买商品,快去购买吧~" wx:else>
</van-empty>
2.效果图
6.通用模块的封装
1.1消息模块封装
1.消息弹出框的基本使用
App({
//页面显示生命周期函数,每打开页面就会调用一次
onShow(){
wx.showToast({
title: '消息提示框',
// 提示图标:默认成功(一个对勾)
// error:(失败) loading(加载) none(不显示图标)
icon:'success',
duration:2000,
// 是否显示透明蒙层
mask:true
}
})
})
2、封装模块
1.在utils文件夹中新建一个extendApI.js文件,
2.创建⼀个 toast ⽅法对 wx.showToast() ⽅法进⾏封装,
3.使用箭头函数;
// 普通函数
// function name(params){}
// 箭头函数
// name => (参数) {函数体}
// toast = ( { 默认参数 } = {实际参数}) => {函数体}
// ()里为参数
// ()等号左边为函数toast默认的参数
// ()等号右边为调用函数toast传入的参数
const toast = ({title="消息提示框",icon='success',duration=2000,mask=true} = {}) => {
wx.showToast({
title,
icon,
duration,
mask
})
}
// 局部暴露toast方法
export{ toast }
// 全局挂载方法
wx.toast = toast
anlgad:页面加载时生命周期的数。
onshow:页面显示生命周期函数 。每次打开页面时都会调用一次。页面显示/切入前台前触发。gnReady:页面初次渲染完成生命周期函数。页面初次渲染完成时触发。gdids:页面隐藏生命周期函数。如页面之间跳转或通过底部 Tab 切换到其他页面,小程序切入后
gnungad:页面卸载生命周期函数。页面卸载时触发,如页面跳转或者返回到之前的页面时。
首先在appj上创建一个 toast 方法对 wx·&hgwTa&&.()方法进行封装对象中包含 title、icon、dur ation、mask 参数,并给参数设置默认值
// wx:showToast({
// title: "消息提示框",
// // success(成功)、error(失败)、loading(加载)、none(不显示图标)
// icon:'success',
// duration:2000,
// // 是否显示透明蒙层
// mask:true
// })
duration:是否显出透明蒙层
局部导入又分为不传入参数导入和传入参数导入
不传入参数导入
// 不计入参数的局部导入
// toast()
不传入参数就会一直成功,不传递参数,使用默认参值。
传入参数导入
传入参数的局部导入
// toast({title:'数据加载完毕'})
传入部分参数,覆盖默认的参数
不传入参数的全局挂载方法wx. toast()
传入全局参数的挂载方法
// wx.toast({icon:'none'})
3.在app.js中调用
import { modal } from '/utils/extendApi'
1.2 模块对话框封装
wx.showModal() 模态对话框也是在项⽬中频繁使⽤的⼀个⼩程序 API ,通常⽤于向⽤户询问是否执 ⾏⼀些操作,例如:询问⽤户是否真的需要退出、是否确认删除等等。
1 // exendApi.js
2
3 // function toast () {}
4
5 // 在调⽤modal⽅法时,可以传递参数,也可以不传递参数。
6 // 如果不传递参数,默认值就是空对象。
7 // 如果传递参数,参数需要时⼀个对象,对象中的属性需要和wx.showModal参数保持⼀致。
8 const modal = (options = {}) => {
9 // 在⽅法内部需要通过Promise返回⽤户的操作
10 // 如果⽤户点击了确认,需要通过resolve返回true
11 // 如果⽤户点击了取消,需要通过resolve返回false
12
13 return new Promise((resolve) => {
14 // 默认的参数
15 const defaultOpt = {
16 title: '提示', // 提示的标题
17 content: '您确定执⾏该操作吗?', // 提示的内容
18 confirmColor: '#f3514f',
19 // 接⼝调⽤结束的回调函数(调⽤成功、失败都会执⾏)
20 complete({ confirm, cancel }) {
21 confirm && console.log('点击了确定')
22 cancel && console.log('点击了取消')
23 }
24 }
25
26 // 通过object.assgin⽅法将参数进⾏合并
27 // 需要使⽤传⼊的参数覆盖默认的参数
28 // 为了不影响默认的参数,需要将合并以后的参数赋值给⼀个空对象
29 const opts = Object.assign({}, defaultOpt, options)
30
31 wx.showModal({32 // 将合并以后的参数通过展开运算符赋值给wx.showModal对象
33 ...opts,
34 complete({ confirm, cancel }) {
35 // 如果⽤户点击了确定,通过 resolve 抛出 true
36 // 如果⽤户点击了取消,通过 resolve 抛出 false
37 confirm && resolve(true)
38 cancel && resolve(false)
39 }
40 })
41 })
42 }
43
44 export { modal }
45
46 wx.modal = modal
47
1 // app.js
2
3 // app.js
4 // import { toast } from './utils/extendApi'
5 import './utils/extendApi'
6
7 App({
8 // ⻚⾯显示⽣命周期函数
9 async onShow() {
10
11 // wx.showModal({
12 // title: '提示', // 提示的标题
13 // content: '您确定执⾏该操作吗?', // 提示的内容
14 // confirmColor: '#f3514f',
15 // // 接⼝调⽤结束的回调函数(调⽤成功、失败都会执⾏)
16 // complete({ confirm, cancel }) {
17 // confirm && console.log('点击了确定')
18 // cancel && console.log('点击了取消')
19 // }
20 // })在⼩程序中,经常需要将⼀些数据存储到本地,⽅便多个⻚⾯的读取使⽤,例如:将⽤户的登录状
态、⽤户的个⼈信息存储到本地。
⼩程序提供了同步、异步两类 API 来实现本地存储操作。例如: wx.setStorageSync 、 wx.setStorage
等⽅法。
21
22 // 不传⼊参数
23 // const res = await wx.modal()
24
25 // 传⼊参数
26 const res = await wx.modal({
27 title:'新的提示',
28 showCancel:false
29 })
30
31 console.log(res);
32
33 }
34
35 })
// 再调用modal方法是,可以传递参数,也可以不传递参数
// 如果不传递参数,默认值就是空对象
// 如果传递参数,参数需要是一个对象,对象中的属性需要和wxwx.showModal参数保持一致
const modal = (options = {}) => {
// 通过new关键字构建一个promise对象
// 在方法内部通过promise返回用户的操作
// resolve为用户点击确认框的结果
return new Promise((resolve) => {
// 用户使用model函数时,使用showModal的默认参数
const defaultOpt = {
title:'提示',
content:'你确定要执行该操作吗',
confirmColor:'#f2514f',
// 接口调用结束后的回调函数
complete({confirm,cancel}){
confirm && console.log('点击了确定');
cancel && console.log('点击了取消');
}
}
// Object.assign()方法用于将对象复制到目标对象
// 通过Object.assign()方法将参数合并
// 如果用户传递进来了options,就会覆盖默认参数
// 第一个空对象参数会被赋值合并后的参数
const opts = Object.assign({},defaultOpt,options)
wx.showModal({
// ...扩展运算符
// 将合并后的参数通过展开运算符赋值给wx.showModal对象。
...opts,
complete({confirm,cancel}){
// 如果用户点击了确认,通过resolve抛出true
confirm && console.log('点击了确定');
// 如果用户点击了取消,通过resolve抛出false
cancel && console.log("点击了取消");
}
})
})
}
// 局部暴露modal方法
export{ modal }
在调用modal方法时,可以传递参数,也可以不传递参数
如果不传递参数,默认值就是空对象如果传递参数,参数需要是一个对象,对象中的属性需要和wx,showModal参数保持一致通过new关键字构建-个promise对象在方法内部通关promise返回用户的操作
resolve为用户点击确认框的结果
日户使用modal函数时,使用showModal的默认参数
Dbiect.assiqn0方法用于将对象复制到目标对象。
通过Obiect.assiqn0方法将参数合并
如果用户传递进来了options,就会覆盖默认参数
局部暴露:先在extendApi.js中暴露modal,然后在appjs中使用import引入modal,最后在onShow()中写入modal0)完成局部暴露
全局暴露:先在extendApijs中完成全局挂载,然后在app,js中的onShow0)中写入wx.modal0)完成全局暴露
1.3封装本地存储 API
在 utils ⽬录下新建 storage.js ⽂件 在该⽂件中,封装对本地数据进⾏ 存储、获取、删除、清除的⽅法
// wx.setStorage(option)是微信小程序存储数据的api
// setStorage()是自定义函数
// key 本地存储中的变量名
// value 变量名对应的变量值
// 以同步方法存储数据
export const setStorage = (key,value) => {
try {
wx.setStorageSync(key,value)
}catch (error){
console.log("存储失败");
}
}
// 以同步方法获取数据
export const getStorage = (key) => {
try{
const value = wx.getStorageSync(key)
if(value){
return value
}
}catch (error){
console.log("存储失败");
}
}
// 删除数据
export const removeStorage = (key) => {
try {
wx.removeStorageSync(key)
}catch(e){
console.log("删除失败");
}
}
// 删除全部数据
export const clearStorage = () => {
try{
wx.clearStorageSync()
}catch(e){
console.log("清除失败");
}
}
7.网络请求封装
1.1请求封装-request⽅法
1.request⽅法封装
// 1.使用了微信自带的请求api wx.request
// 2.将wx.request封装到了一个名为request的函数中
// 3.将函数封装到了一个名为wxRequest的类中
// 创建一个WxRequest类
// 通过类的方式进行封装,会让代码更具有复用性
// 也可以方便添加新的属性和方法
class WxRequest{
// 用于创建和初始化类的属性和方法
constructor(){}
// request实例方法接受一个对象类型的参数
request(options){
return new Promise((resolve,reject) => {
wx.request({
// 使用拓展运算符将request函数传来的对象参数展开
...options,
// 当接口调用成功就会触发success回调函数
success:(res) => {
resolve(res)
},
//当接口调用失败时会触发fail回调函数
fail:(err)=>{
reject(err)
}
})
})
}
}
//对wxRequest进行实例化
const instance = new WxRequest()
//将WxRequest实例暴露出去,方便在其他文件中进行使用
export default instance
// 导入在request页面中封装并暴露的instance实例
import instance from '../../utils/http'
// pages/test/test.js
Page({
// 具体阐述按钮点击事件要做什么事情
async handler(){
// 第一种调用方法:通过.then和.catch接受返回的值
// instance
// .request({
// // 请求地址
// url:'https://gmall-prod.atguigu.cn/mall-api/index/findBanner',
// // 请求方式
// method:"GET"
// })
// .then((res) => {
// console.log(res);
// })
// .catch((err) => {
// console.log(err);
// })
// 第二种调用方法:通过await和async接受返回的值
// const res = await instance.request({
// url:'/index/findBanner',
// method:"GET"
// })
// 第三种调用方式:通过调用快捷方式接受返回的值
// const res = await instance.get('/index/findBanner')
// 调用并传参
// const res = await instance.get('/index/findBanner',{test:111},
// {timeout:20000})
//当前请求地址是获取购物车数据,需要token才行
const res = await instance.get('/cart/getCartList')
console.log(res);
},
})
/* pages/test/test.wxss */
.box{
display: flex;
// 主轴方向上居中
justify-content: center;
// 副轴方向上居中
align-items: center;
// 将盒子高度设置为页面高度
height: 100vh;
}
<!--pages/test/test.wxml-->
<!-- <text>pages/test/test.wxml</text> -->
<view class="box">
<!-- 设置一个按钮 并给按钮设置一个名为handler的点击事件 点击按钮则会发送请求 -->
<button type="warn" size="mini" plain bindtap="handler">测试发送请求</button>
</view>
1.2请求封装-设置请求参数
1.创建一个WxRequest类,通过类的方式进行封装 会让代码更具有复用性,也可以方便添加新的属性和方法。
2.通过WxRequest来设定默认参数对象,请求基地址,服务器接口地址,请求方式、参数和请求头,设置数据的交互格式并且设置默认超时时间。
3.定义拦截对象包括请求拦截和响应拦截方法,方便在请求或响应之前进行处理。请求拦截器:request,响应拦截器:response
4.创建params为用户传入的请求配置项,使用Object.assign方法合并默认参数以及传递的请求参数,需要传入的参数,会覆盖默认的参数,因此传入的参数放在最后。
5.request需要接受一个对象类型的参数, 拼接完整的请求路径options.url,合并请求参数options ={...this.defaults,...options},在发送请求之前调用请求拦截器options = this.interceptors.request(options)
6. 使用拓展运算符将request函数传来的对象参数展开 ...options, 当接口调用成功就会触发success回调函数。
不管接口成功还是失败,都需要调用响应拦截器;响应拦截器需要接受服务器响应的数据,然后对数据进行逻辑处理,处理好后进行返回;在给响应拦截器传递参数时 需要将请求参数也一起上传;方便进行代码的调式或者其他逻辑处理,所以需要合并数据;然后将合并的参数给响应拦截器。
当接口调用失败时会触发fail回调函数
7.第一个参数:需要合并的目标对象;第二个参数:服务器响应的数据;第三个参数:请求配置以及自定义属性
8.GET POST PUT DELETE四个封装方法
9.对WxRequest进行实例化
10.配置请求拦截器并且发送请求;响应拦截器了解发送前需要做什么
11.将WxRequest实例暴露出去 方便在其他文件中进行使用 export default instance
// 1.使用了微信自带的请求api wx.request
// 2.将wx.request封装到了一个名为request的函数中
// 3.将函数封装到了一个名为wxRequest的类中
// 创建一个WxRequest类
// 通过类的方式进行封装,会让代码更具有复用性
// 也可以方便添加新的属性和方法
class WxRequest{
// 默认参数对象
defaults = {
// 请求基地址
baseURL:'',
// 服务器接口地址
url:'',
// 请求方式
method:'GET',
// 请求参数
data:null,
// 请求头
header: {
'Content-type': 'application/json' // 设置数据的交互格式
},
// 默认超时时间为一分钟
timeout:100
}
// 定义拦截对象,包括请求拦截和响应拦截,方便在请求或响应之前进行处理
interceptors = {
// 请求拦截
request:(config) => config,
// 响应拦截
response:(response) => response
}
// 用于创建和初始化类的属性和方法
// params为用户传入的请求配置项
constructor(params = {}){
// 使用Object.assign方法合并默认参数以及传递的请求参数
// 需要传入的参数,会覆盖默认的参数,因此传入的参数放在最后
this.defaults = Object.assign({},this.defaults,params)
}
// request实例方法接受一个对象类型方法
request(options){
// 拼接完整的请求路径
options.url = this.defaults.baseURL + options.url
// 合并请求参数
options = {...this.defaults,...options}
// 发送请求之前添加loading
wx.showLoading({})
// 在发送请求之前调用请求拦截器
options = this.interceptors.request(options)
console.log(options);
return new Promise((resolve,reject) => {
wx.request({
// 使用拓展运算符将request函数传来的对象参数展开
...options,
// 当接口调用成功就会触发success回调函数
success:(res) => {
// 不管接口成功还是失败,都需要调用响应拦截器
// 响应拦截器需要接受服务器响应的数据,然后对数据进行逻辑处理,处理好后进行返回
// 在响应拦截器传递参数时,需要将请求参数也一起上传
// 方便进行代码的调试或者其他逻辑处理,所以需要合并参数
// 然后将合并的参数给响应拦截器
// 第一个参数:需要合并的目标对象
// 第二个参数:服务器响应的数据
// 第三个参数:请求配置以及自定义属性
// 不管是请求失败还是请求成功,都会将响应的数据传递给响应拦截器
// 这个时候合并参数时,就需要追加一个属性:isSuccess
// 如果属性值为true说明执行了success回调函数
const mergetRes = Object.assign({},res,{config:options,isSuccess:true})
// resolve(res)
resolve(this.interceptors.response(mergetRes))
},
// 当接口调用失败就会触发fail回调函数
fail:(err) => {
// 如果属性值为false,说明执行了fail回调函数
const mergetErr = Object.assign({},err,{config:options,isSuccess:false})
// reject(err)
reject(this.interceptors.response(mergetErr))
},
// 不管promisse请求是否成功
// 都会执行complete里面的内容
complete:()=>{
// 接口调用完成后隐藏loading
wx.hideLoading()
}
})
})
}
1.3封装请求快捷⽅法
⽬前已经完成了 request() 请求⽅法的封装,同时处理了请求参数。 每次发送请求时都使⽤ request() ⽅法即可,但是项⽬中的接⼝地址有很多,不是很简洁。所以我们在 request() 基础上封装⼀些快捷⽅法,简化 request() 的调⽤。
// 封装GET方法
get(url,data={},config={}){
return this.request(Object.assign({url,data,method:'GET',config}))
}
// 封装POST实例方法
post(url,data={},config={}){
return this.request(Object.assign({url,data,method:'POST',config}))
}
// 封装PUT方法
put(url,data={},config={}){
return this.request(Object.assign({url,data,method:'PUT',config}))
}
// 封装DELETE方法
delete(url,data={},config={}){
return this.request(Object.assign({url,data,method:'DELETE',config}))
}
// 对类进行实例化
const instance = new WxRequest({
baseURL: 'https://gmall-prod.atguigu.cn/mall-api',
timeout: 15000
})
// 将 WxRequest 实例进⾏暴露出去,⽅便在其他⽂件中进⾏使⽤
export default instance
// 导入在request页面中封装并暴露的instance实例
import instance from '../../utils/http'
// pages/test/test.js
Page({
// 具体阐述按钮点击事件要做什么事情
async handler(){
// 第一种调用方法:通过.then和.catch接受返回的值
// instance
// .request({
// // 请求地址
// url:'https://gmall-prod.atguigu.cn/mall-api/index/findBanner',
// // 请求方式
// method:"GET"
// })
// .then((res) => {
// console.log(res);
// })
// .catch((err) => {
// console.log(err);
// })
// 第二种调用方法:通过await和async接受返回的值
// const res = await instance.request({
// url:'/index/findBanner',
// method:"GET"
// })
// 第三种调用方式:通过调用快捷方式接受返回的值
// const res = await instance.get('/index/findBanner')
// 调用并传参
// const res = await instance.get('/index/findBanner',{test:111},
// {timeout:20000})
//当前请求地址是获取购物车数据,需要token才行
const res = await instance.get('/cart/getCartList')
console.log(res);
},
}
1.4 定义请求/响应拦截器
为了⽅便统⼀处理请求参数以及服务器响应结果,为 WxRequest 添加拦截器功能,拦截器包括 请求 拦截器 和 响应拦截器
请求拦截器本质上是在请求之前调⽤的函数,⽤来对请求参数进⾏新增和修改
响应拦截器本质上是在响应之后调⽤的函数,⽤来对响应数据做点什么
// 1.使用了微信自带的请求api wx.request
// 2.将wx.request封装到了一个名为request的函数中
// 3.将函数封装到了一个名为wxRequest的类中
// 创建一个WxRequest类
// 通过类的方式进行封装,会让代码更具有复用性
// 也可以方便添加新的属性和方法
class WxRequest{
// 默认参数对象
defaults = {
// 请求基地址
baseURL:'',
// 服务器接口地址
url:'',
// 请求方式
method:'GET',
// 请求参数
data:null,
// 请求头
header: {
'Content-type': 'application/json' // 设置数据的交互格式
},
// 默认超时时间为一分钟
timeout:100
}
// 定义拦截对象,包括请求拦截和响应拦截,方便在请求或响应之前进行处理
interceptors = {
// 请求拦截
request:(config) => config,
// 响应拦截
response:(response) => response
}
// 用于创建和初始化类的属性和方法
// params为用户传入的请求配置项
constructor(params = {}){
// 使用Object.assign方法合并默认参数以及传递的请求参数
// 需要传入的参数,会覆盖默认的参数,因此传入的参数放在最后
this.defaults = Object.assign({},this.defaults,params)
}
// request实例方法接受一个对象类型方法
request(options){
// 拼接完整的请求路径
options.url = this.defaults.baseURL + options.url
// 合并请求参数
options = {...this.defaults,...options}
// 发送请求之前添加loading
wx.showLoading({})
// 在发送请求之前调用请求拦截器
options = this.interceptors.request(options)
console.log(options);
return new Promise((resolve,reject) => {
wx.request({
// 使用拓展运算符将request函数传来的对象参数展开
...options,
// 当接口调用成功就会触发success回调函数
success:(res) => {
// 不管接口成功还是失败,都需要调用响应拦截器
// 响应拦截器需要接受服务器响应的数据,然后对数据进行逻辑处理,处理好后进行返回
// 在响应拦截器传递参数时,需要将请求参数也一起上传
// 方便进行代码的调试或者其他逻辑处理,所以需要合并参数
// 然后将合并的参数给响应拦截器
// 第一个参数:需要合并的目标对象
// 第二个参数:服务器响应的数据
// 第三个参数:请求配置以及自定义属性
// 不管是请求失败还是请求成功,都会将响应的数据传递给响应拦截器
// 这个时候合并参数时,就需要追加一个属性:isSuccess
// 如果属性值为true说明执行了success回调函数
const mergetRes = Object.assign({},res,{config:options,isSuccess:true})
// resolve(res)
resolve(this.interceptors.response(mergetRes))
},
// 当接口调用失败就会触发fail回调函数
fail:(err) => {
// 如果属性值为false,说明执行了fail回调函数
const mergetErr = Object.assign({},err,{config:options,isSuccess:false})
// reject(err)
reject(this.interceptors.response(mergetErr))
},
// 不管promisse请求是否成功
// 都会执行complete里面的内容
complete:()=>{
// 接口调用完成后隐藏loading
wx.hideLoading()
}
})
})
}
// 封装GET方法
get(url,data={},config={}){
return this.request(Object.assign({url,data,method:'GET',config}))
}
// 封装POST实例方法
post(url,data={},config={}){
return this.request(Object.assign({url,data,method:'POST',config}))
}
// 封装PUT方法
put(url,data={},config={}){
return this.request(Object.assign({url,data,method:'PUT',config}))
}
// 封装DELETE方法
delete(url,data={},config={}){
return this.request(Object.assign({url,data,method:'DELETE',config}))
}
}
export default WxRequest
不同的请求方式区别
GET请求:
用途:用于从服务器请求数据。
数据传递:通过URL的查询字符串传递参数,参数在URL中可见。
安全性:由于参数在URL中可见,因此不适合传输敏感信息。
缓存:GET请求可以被缓存。
POST请求:
用途:用于向服务器提交数据,常用于提交表单或上传文件。
数据传递:数据包含在请求体中,不会在URL中显示。
安全性:相比GET请求,POST请求在传输敏感信息时更为安全。
缓存:POST请求不会被缓存。
用途:用于更新服务器上的资源。
DELETE请求:
用途:用于删除服务器上的资源。
幂等性:DELETE请求也是幂等的。
HEAD请求:
用途:与GET请求类似,但只返回响应头,不返回响应体。通常用于检查资源的存在性、获取资源的元信息等。
OPTIONS请求:
用途:用于获取目标资源所支持的通信选项。例如,客户端可以使用它来查看服务器支持的HTTP方法。
// 添加请求拦截器 (在请求发送之前对请求参数进行新增或者修改)
instance.interceptors.request = (config) => {
// 在实际开发中,有一些接口需要使用访问令牌 token
// 访问令牌 token 通常是存储到本地
// 需要先从本地获取到存储的 token
const token = getStorage('token')
// 如果本地存在 token,这时候就需要在请求头中添加 token 字段
if (token) {
config.header['token'] = token
}
// 在发送请求之前做些什么
return config
}
// 添加响应拦截器 (在服务器响应数据以后,对返回的数据进行逻辑处理)
instance.interceptors.response = async (response) => {
// 从 response 对象中解构两个数据
const { isSuccess, data } = response
// response 服务器响应的数据,只不过数据被 wx.request 进行了一层包装
// console.log(response)
// response.config 封装的包里面提供的 config 属性,是请求的参数信息
// 可以使用请求参数进行代码的调试
// response.data 服务器真正响应的数据
// response.isSuccess 判断代码执行了哪一个回调函数
// isSuccess = true,说明代码执行了 wx.request 方法的 success 回调函数
// isSuccess = false,说明代码执行了 wx.request 方法的 fail 回调函数
}
1.5完善请求/响应拦截器
在响应拦截器,我们需要判断是请求成功,还是请求失败,然后进⾏不同的业务逻辑处理。 例如:请求成功以后将数据简化返回,⽹络出现异常则给⽤户进⾏⽹络异常提示。 ⽬前不管请求成功 (success),还是请求失败(fail),都会执⾏响应拦截器 那么怎么判断是请求成功,还是请求失败呢 ?
封装需求: 1.在实例调⽤的响应拦截中,根据传递的数据进⾏以下的处理:
如果请求成功,将响应成功的数据传递给响应拦截器,同时在传递的数据中新增 isSuccess: true 字段,表示请求成功
如果请求失败,将响应失败的数据传递给响应拦截器,同时在传递的数据中新增 isSuccess: false 字段,表示请求失败
请求拦截器:请求之前调用的函数,用来对请求参数进行新增和修改
响应拦截器:响应之后调用的函数,用来对响应数据进行操作
1 // utils/request.js
2
3 class WxRequest {
4
5 // coding....
6
7 request(options) {
8 // coding....
9
10 // 使⽤ Promise 封装异步请求
11 return new Promise((resolve, reject) => {
12 // 使⽤ wx.request 发起请求
13 wx.request({
14 ...options,
15
16 // 接⼝调⽤成功的回调函数
17 success: (res) => {
18 // 响应成功以后触发响应拦截器
19 if (this.interceptors.response) {
20 + // 调⽤响应拦截器⽅法,获取到响应拦截器内部返回数据
21 + // success: true 表示服务器成功响应了结果,我们需要对业务状态码进⾏判断
+ res = this.interceptors.response({ response: res, isSuccess: true
})
22
23 }
24
25 // 将数据通过 resolve 进⾏返回即可
1.
2.26 resolve(res)
27 },
28
29 // 接⼝调⽤失败的回调函数
30 fail: (err) => {
31 // 响应失败以后也要执⾏响应拦截器
32 if (this.interceptors.response) {
33 + // isSuccess: false 表示是⽹络超时或其他问题
+ err = this.interceptors.response({ response: err, isSuccess: false
})
34
35 }
36
37 // 当请求失败以后,通过 reject 返回错误原因
38 reject(err)
39 }
40
41 })
42 })
43 }
// 添加请求拦截器 (在请求发送之前对请求参数进行新增或者修改)
instance.interceptors.request = (config) => {
// 在实际开发中,有一些接口需要使用访问令牌 token
// 访问令牌 token 通常是存储到本地
// 需要先从本地获取到存储的 token
const token = getStorage('token')
// 如果本地存在 token,这时候就需要在请求头中添加 token 字段
if (token) {
config.header['token'] = token
}
// 在发送请求之前做些什么
return config
}
// 添加响应拦截器 (在服务器响应数据以后,对返回的数据进行逻辑处理)
instance.interceptors.response = async (response) => {
// 从 response 对象中解构两个数据
const { isSuccess, data } = response
// response 服务器响应的数据,只不过数据被 wx.request 进行了一层包装
// console.log(response)
// response.config 封装的包里面提供的 config 属性,是请求的参数信息
// 可以使用请求参数进行代码的调试
// response.data 服务器真正响应的数据
// response.isSuccess 判断代码执行了哪一个回调函数
// isSuccess = true,说明代码执行了 wx.request 方法的 success 回调函数
// isSuccess = false,说明代码执行了 wx.request 方法的 fail 回调函数
// 如果 isSuccess = false,说明网络出现了问题
if (!isSuccess) {
toast({
title: '网络异常请重试',
icon: 'error'
})
return Promise.reject(response)
}
// 对响应数据做点什么
// return response
return data
}
// 将 WxRequest 的实例通过模块化的⽅式暴露出去
export default instance
1.6使⽤请求/响应拦截器
使⽤响应拦截器: 在使⽤ wx.request 发送⽹络请求时。只要成功接收到服务器返回,⽆论 statusCode 是多少,都会进 ⼊ success 回调。 因此开发者根据业务逻辑对返回值进⾏判断。
11 // 响应拦截器
12 instance.interceptors.response = async (response) => {
13
14 console.log(response);
15
16 // 从response中结构isSuccess
17 // const { isSuccess } = response
18 const { isSuccess, data } = response
1.
2.
3.19
20 // 如果isSuccess为false,说明执⾏了fail回调函数
21 // 这时候说明⽹络异常,需要给⽤户提示⽹络异常
22 if (!isSuccess) {
23 wx.showToast({
24 title: '⽹络异常请重试',
25 icon: 'error'
26 })
27
28 return response
29 }
30
31 // 判断服务器响应的业务状态码
32 switch(data.code){
33
34 // 如果后端返回的业务状态码等于200,说明请求成功,服务器成功响应了数据
35 case 200:
36 // 对服务器响应数据做点什么
37 return data
38
39 // 如果返回的业务状态码等于208,说明没有token,或者token失效
40 // 就需要让⽤户登录或者重新登录
41 case 208:
42 const res = await modal({
43 content:'鉴权失败,请重新登录',
44 showCancel:false // 不显示取消按钮
45 })
46 if(res){
47 // 清除之前失效的token,同时要清除本地存储的全部信息
48 clearStorage()
49 wx.navigateTo({
50 url: '/pages/login/login',
51 })
52 }
53 return Promise.reject(response)
54
55 default:
56 toast({
57 title:'程序出现异常,请联系客服或稍后重试'
58 })
59 return Promise.reject(response)
60 }
61
62 // 对响应数据做点什么
63 // return response
64 // return data
65 }
66
67 // 将 WxRequest 的实例通过模块化的⽅式暴露出去
68 export default instance
1.8请求封装-添加并发请求
前端并发请求是指在前端⻚⾯同时向后端发起多个请求的情况。当⼀个⻚⾯需要请求多个接⼝获取数 据时,为了提⾼⻚⾯的加载速度和⽤户体验,可以同时发起多个请求,这些请求之间就是并发的关 系。
1使⽤ async 和 await ⽅式
2使⽤ Promise.all() ⽅式
1 <!--pages/test/test.wxml-->
2
3 <view class="box">
<button type="warn" size="mini" plain bindtap="AllHandler">测试并发请求
</button>
4
5 </view>
1 // test/test.js
2
3 page({
4 // 演示通过 async 和 await ⽅式同步发起多个请求
5 // async 和 await 能够控制异步任务以同步的流程来执⾏// async和awiat⽅式发起多个请求
6 // 当第⼀个请求结束以后,才能够发起第⼆个请求
7 // 当前⼀个请求结束以后,才能够发起下⼀个请求
8 // 会造成请求的阻塞,从⽽影响⻚⾯的渲染速度
9 async AllHandler(){
10 await instance.get('/index/findBanner')
11 await instance.get('/index/findCategory1')
12 await instance.get('/index/findBanner')
13 await instance.get('/index/findCategory1')
14
15 // 演示通过 Promise.all 同时发送多个请求
16 // Promise.all 能够将多个请求同时进⾏发送
17 // Promise.all 能够将多个异步请求同时进⾏发售,也就是并⾏发送
18 // 并不会造成请求的阻塞,从⽽不会影响⻚⾯的渲染速度
await
Promise.all([instance.get('/index/findBanner'),instance.get('/index/findCategory
1'),
19
20 instance.get('/index/findBanner'),instance.get('/index/findCategory1')])
21
22 })
class WxRequest{
code...
// 用来处理并发请求
all(...promise){
// 通过展开运算符接受传递的参数
// 展开运算符会将传入的参数转为数组
console.log(promise);
return Promise.all(promise)
}
}
1.9添加loading
在封装时添加 loading 效果,从⽽提⾼⽤户使⽤体验
在请求发送之前,需要通过 wx.showLoading 展示 loading 效果
当服务器响应数据以后,需要调⽤ wx.hideLoading 隐藏 loading 效果
1 // request.js
2
3 class WxRequest {
4 request(options) {
5
6 // 拼接完整的请求地址
7 options.url = this.defaults.baseURL + options.url
8
9 // 合并请求参数
10 options = { ...this.defaults, ...options }
11
12 + // 发送请求之前添加loading
13 + wx.showLoading()
14
15 // 在发送请求之前调⽤请求拦截器,新增或修改请求参数
16 options = this.interceptors.request(options)
17
18 console.log(options);
19
20 return new Promise((resolve, reject) => {
21
22 wx.request({
23
24 // 使⽤拓展运算符将request函数传来的对象参数展开
25 ...options,
26
27 // 当接⼝调⽤成功就会触发success回调函数
28 success: (res) => {
29
30 // 不管是请求失败还是请求成功,都已经将响应的数据传递给了响应拦截器
31 // 这时候再合并参数的时候,追加⼀个属性:isSuccess
32 // 如果属性值为true,说明执⾏了success回调函数
33 // 如果属性值为false,说明执⾏了fail回调函数
const mergeRes = Object.assign({}, res, { config:
options,isSuccess:true })
34
35 resolve(this.interceptors.response(mergeRes))
36
37 },
38
39 // 当接⼝调⽤失败时会触发fail回调函数
40 fail: (err) => {
const mergeErr = Object.assign({}, err, { iconfig:
options,isSuccess:false })
41
42 // 不管接⼝成功还是失败,都需要调⽤响应拦截器
43 err = this.interceptors.response(mergeErr)
44 reject(err)
45 },
46
47 + complete:() => {
48 + // 接⼝调⽤完成后隐藏loading
49 + wx.hideLoading()
50 }
51 })
52 })
53 }
54 }
1.test.js代码
1 // 导⼊模块、包提供的类
2 import WxRequest from 'mina-request'
3 // 导⼊封装的本地存储操作模块
4 import { getStorage, clearStorage } from './storage'
5 // 导⼊封装的增强 API
6 import { toast, modal } from './extendApi'
7
8 // 对类进⾏实例化
9 const instance = new WxRequest({
10 baseURL: 'https://gmall-prod.atguigu.cn/mall-api',
11 timeout: 15000
12 })
13
14 // 添加请求拦截器 (在请求发送之前对请求参数进⾏新增或者修改)
15 instance.interceptors.request = (config) => {
16 // 在实际开发中,有⼀些接⼝需要使⽤访问令牌 token
17 // 访问令牌 token 通常是存储到本地
18 // 需要先从本地获取到存储的 token
19 const token = getStorage('token')
20
21 // 如果本地存在 token,这时候就需要在请求头中添加 token 字段
22 if (token) {
23 config.header['token'] = token
24 }
25 26 // 在发送请求之前做些什么
27 return config
28 }
29
30 // 添加响应拦截器 (在服务器响应数据以后,对返回的数据进⾏逻辑处理)
31 instance.interceptors.response = async (response) => {
32 // 从 response 对象中解构两个数据
33 const { isSuccess, data } = response
34
35 // response 服务器响应的数据,只不过数据被 wx.request 进⾏了⼀层包装
36 // console.log(response)
37
38 // response.config 封装的包⾥⾯提供的 config 属性,是请求的参数信息
39 // 可以使⽤请求参数进⾏代码的调试
40
41 // response.data 服务器真正响应的数据
42
43 // response.isSuccess 判断代码执⾏了哪⼀个回调函数
44 // isSuccess = true,说明代码执⾏了 wx.request ⽅法的 success 回调函数
45 // isSuccess = false,说明代码执⾏了 wx.request ⽅法的 fail 回调函数
46
47 // 如果 isSuccess = false,说明⽹络出现了问题
48 if (!isSuccess) {
49 toast({
50 title: '⽹络异常请重试',
51 icon: 'error'
52 })
53
54 return Promise.reject(response)
55 }
56
57 // 如果 isSuccess = true,说明代码执⾏到了 success 回调函数
58 // 需要开发者对返回的参数进⾏逻辑判断
59 // 需要对后端返回的业务状态码进⾏判断
60 // 业务状态码 === 200,接⼝调⽤成功,服务器成功返回了数据
61 // 业务状态码 === 208,没有 token 或者 token 失效,需要让⽤户重新进⾏登录
62 // 业务状态码既不等于 200,也不等于 208,说明出现了其他异常,需要给⽤户统⼀进⾏提示
63 switch (data.code) {
64 case 200:65 // 接⼝调⽤成功,服务器成功返回了数据,只需要将数据简化以后返回即可
66 return data
67
68 case 208:
69 const res = await modal({
70 content: '鉴权失败,请重新登录',
71 showCancel: false
72 })
73
74 if (res) {
75 // 既然⽤户需要重新进⾏登录,就需要把之前⽤户存储的信息(过期的 token) 进⾏清除
76 clearStorage()
77
78 wx.navigateTo({
79 url: '/pages/login/login'
80 })
81 }
82
83 return Promise.reject(response)
84
85 default:
86 toast({
87 title: '程序出现异常,请联系客服或稍后重试!'
88 })
89 return Promise.reject(response)
90 }
91
92 // return response
93 }
94
95 // 导出实例
96 export default instance
1.10接⼝调⽤⽅式说明
开发中,我们会将所有的⽹络请求⽅法放置在 api ⽬录下统⼀管理,然后按照模块功能来划分成对应 的⽂件,在⽂件中将接⼝封装成⼀个个⽅法单独导出
1 // index.js
2
3 // 导⼊封装的⽹络请求模块实例
4
5 import http from '../utils/http'
6
7 // export const reqSwiperData = () => {
8 // return http.get('/index/findBanner')
9 // }
10
11 export const reqSwiperData = () => http.get('/index/findBanner')
1 // test/test.js
2
3 // 导⼊接⼝API函数
4 import {reqSwiperData} from '../../api/index'
5
6 Page({
7
8 // 点击按钮触发 handler ⽅法
9 async handler() {
10
11 const res = await reqSwiperData()
12
13 console.log(res);
14
15 })
8.首页页面
1.获取⾸⻚数据
1 // api.index.js
2
3 // 导⼊封装的 ⽹络请求模块实例
4 import http from '../utils/http'
5
6 export const reqIndexData = () => {
7 // 通过并发请求获取⾸⻚的数据,提升⻚⾯的渲染速度
8
9 // 通过 Promise.all 进⾏并发请求
10 // return Promise.all([
11 // http.get('/index/findBanner'),
12 // http.get('/index/findCategory1'),
13 // http.get('/index/advertisement'),
14 // http.get('/index/findListGoods'),
15 // http.get('/index/findRecommendGoods')
16 // ])
17
18 // 是使⽤封装的 all ⽅法发送请求
19 // 这两种⽅式都可以
20 return http.all(
21 http.get('/index/findBanner'),
22 http.get('/index/findCategory1'),
23 http.get('/index/advertisement'),
24 http.get('/index/findListGoods'),
25 http.get('/index/findRecommendGoods')
26 )
27 }
// 导入接口api
import { reqIndexData } from '../../api/index'
Page({
// 初始化数据
data:{
bannerList:[],//轮播图数据
categotyList:[],//分类数据
activeList:[],//活动广告
hotList:[],//人气推荐
guessList:[]//猜你喜欢
},
// 获取首页数据
async getIndexData(){
const res = await reqIndexData()
console.log(res);
// 在获取数据以后,对数据进行赋值
this.setData({
bannerList:res[0].data,//轮播图数据
categotyList:res[1].data,//分类数据
activeList:res[2].data,//活动广告
hotList:res[3].data,//人气推荐
guessList:res[4].data//猜你喜欢
})
},
// 监听页面加载
onLoad(){
this.getIndexData()
}
})
<view class="index-container">
<!-- 首页背景图 -->
<view class="window-bgc"></view>
<!-- 页面主体区域 -->
<view class="container">
<!-- bannerList、cateList是原本的data数据 -->
<!-- {{bannerList}}、{{categoryList}}是从服务器中获得的数据 -->
<!-- 轮播图区域 -->
<banner bannerList = "{{ bannerList }}"/>
<!-- 导航区域 -->
<entrance cateList = "{{ categotyList }}"/>
<!-- 广告区域 -->
<view class="adver">
<view class="adver-left">
<navigator url="/pages/goods/list/list?category2Id={{activeList[0].category2Id}}">
<image src="{{ activeList[0].imageUrl }}" mode="widthFix"/>
</navigator>
</view>
<view class="adver-right">
<view>
<navigator url="/pages/goods/list/list?category2Id={{activeList[1].category2Id}}">
<image src="{{ activeList[1].imageUrl }}" mode="widthFix"/>
</navigator>
</view>
<view>
<navigator url="/pages/goods/list/list?category2Id={{activeList[2].category2Id}}">
<image src="{{ activeList[2].imageUrl }}" mode="widthFix"/>
</navigator>
</view>
</view>
</view>
<!-- 商品列表 -->
<goods-list title="猜你喜欢" list="{{ guessList }}"></goods-list>
<goods-list title="人气推荐" list="{{ hotList }}"></goods-list>
</view>
</view>
2.分析轮播图区域并渲染
轮播图区域采⽤组件化⽅式开发,我们在 index ⽬录下新建 banner ⽂件夹,⾥⾯存放轮播图组件。 在 index/index.json ⽂件中导⼊组件,然后将组件当成标签进⾏使⽤。
{
"usingComponents": {
"banner": "./banner/banner",
"entrance": "./entrance/entrance",
"goods-list": "../../components/goods-list/goods-list"
}
}
Component({
// 组件的属性列表
properties:{
// 轮播图数据
bannerList: {
type: Array,
value: [
'../../../assets/banner/banner-1.jpg',
'../../../assets/banner/banner-2.jpg',
'../../../assets/banner/banner-3.jpg'
]
}
},
/**
* 组件的初始数据
*/
data:{
// 被激活的轮播图索引,默认是0
activeIndex:0
},
/**
*
*/
methods:{
getSwiperIndex(event){
// console.log(event);
const {current} = event.detail;
this.setData({
// 将当前的轮播图索引设置为activeInddex
activeIndex:current
})
}
}
})
<!--pages/index/banner/banner.wxml-->
<!-- 轮播图 -->
<view class="swiper-box">
<navigator class="navigator" url="/pages/goods/detail/detail?goodId={{ item.id }}">
<swiper autoplay class="swiper" indicator-active-color="#FF734C" interval="2000" duration="1000" indicator-color="rgba(0,0,0, .3)"
bindchange="getSwiperIndex"
>
<block wx:for="{{ bannerList }}" wx:key="index">
<swiper-item class="swiper-item">
<image class="img" src="{{ item.imageUrl }}"></image>
</swiper-item>
</block>
</swiper>
</navigator>
<!-- 轮播图的面板指示点,因为面板指示点不支持,所以我们只能通过自定义结构的方式 -->
<view class="indicator">
<!-- active 类名:当前被激活的面板指示点颜色 -->
<!-- rectangle 类名:默认的面板指示点颜色 -->
<text
wx:for="{{bannerList.length}}"
wx:key="id"
class="{{ 'active rectangle' }}"
></text>
</view>
</view>
3渲染分类导航
Component({
// 组件的属性列表
properties:{
// 分类列表
cateList: {
type: Array,
value: []
}
}
})
渲染导航分类结构
<!--pages/index/entrance/entrance.wxml-->
<view class="nav-list">
<!-- 一级分类导航容器 -->
<view wx:for="{{ cateList }}" wx:key="index" class="nav-item {{ index >= 5 ? 'small' : '' }}">
<!-- 导航链接 -->
<navigator url="/pages/goods/list/list?category1Id={{item.id}}" class="navigator-nav">
<image src="{{ item.imageUrl }}" class="nav-img"/>
<text class="nav-text">{{ item.name }}</text>
</navigator>
</view>
</view>
导航分类结构样式调整
/* pages/index/entrance/entrance.wxss */
.nav-list {
margin: 20rpx;
border-radius: 18rpx;
padding: 10px 0px 10px 10px;
background-color: white;
// 设置弹性布局
display: flex;
// 设置换行
flex-wrap: wrap;
.nav-item{
.navigator-nav{
display: flex;
// 更改主轴方向
// 由默认的横向排列更改为纵向排列
flex-direction: column;
align-items: center;
justify-content: center;
margin-right: 10px;
margin-top: 10px;
}
.nav-text{
margin-top: 4px;
font-size: 12px;
}
.nav-img{
width: 66rpx;
height: 66rpx;
}
&.small {
margin-top: 36rpx;
.nav-img{
width: 50rpx !important;
height: 50rpx !important;
}
}
}
}
4.渲染活动区域
1 // pages/index/index.html
2
3 <!-- ⼴告区域 -->
4 <view class="adver">
5 <view class="adver-left">
+ <navigator url="/pages/goods/list/list?category2Id={{
activeList[0].category2Id }}">
6
7 + <image src="{{ activeList[0].imageUrl }}" mode="widthFix" />
8 + </navigator>
9 </view>
10
11 <view class="adver-right">
12 <view>
+ <navigator url="/pages/goods/list/list?category2Id={{
activeList[1].category2Id }}">
13
14 + <image src="{{ activeList[1].imageUrl }}" mode="widthFix" />
15 + </navigator>
16 </view>
17 <view>
+ <navigator url="/pages/goods/list/list?category2Id={{
activeList[2].category2Id }}">
18
19 + <image src="{{ activeList[2].imageUrl }}" mode="widthFix" />
20 + </navigator>
21 </view>
22 </view>
23 </view>
5.猜你喜欢+⼈⽓推荐区域渲染
接收⾸⻚传递的 list 数据
Component({
/**
* 组件的属性列表
*/
properties: {
// 列表标题
title: {
type: String,
value: '',
},
// 传递的列表数据
list: {
type: Array,
value: []
}
},
/**
* 组件的初始数据
*/
data: {},
/**
* 组件的⽅法列表
*/
methods: {}
})
遍历 goods-item 组件,并将数据传递给 goods-item
<!--components/goods-list/goods-list.wxml-->
<view class="goods_container" wx:if="{{ list.length }}">
<!-- 标题 -->
<view class="goods_title">{{title}}</view>
<!-- 列表区域 -->
<view class="goods_card_list">
<goods-card wx:for="{{ list }}" wx:key="id" goodItem="{{ item }}"/>
</view>
<!-- 查看更多 -->
<view class="goods_more">
<navigator url="" class="goods_more_btn" url="/pages/goods/list/list" hover-class="navigator-hover" open-type="navigate">
查看更多
</navigator>
</view>
</view>
{
"component": true,
"usingComponents": {
"goods-card":"/components/goods-card/goods-card"
}
}
将数据传递给 goods-item 组件
Component({
/**
* 组建的属性列表
*/
properties:{
// 每一项商品的数据
goodItem: {
type:Object,
value: {}
}
},
/**
* 组件的初始数据
*/
data: {},
/**
* 组建的方法列表
*/
methods: {}
})
将数据传递给 goods-item 组件
<!--components/goods-card/goods-card.wxml-->
<view class="goods_card_container">
<!-- 图片 -->
<navigator class="navigator_nav" url="/pages/goods/detail/detail?goodsId={{goodItem.id}}">
<image src="{{ goodItem.imageUrl }}" mode="widthFix" class="goods_img"/>
<!-- 详细信息 -->
<view class="goods_item_info">
<!-- 商品名称 -->
<text class="goods_item_info_name">{{ goodItem.name }}</text>
<!-- 商品描述 -->
<text class="goods_item_info_promo">{{ goodItem.floralLanguage }}</text>
<!-- 商品价格 -->
<view class="goods_item_info_bottom">
<view class="goods_item_info_price">
<text class="text">¥</text>{{ goodItem.price }}
</view>
<view class="goods_item_info_origin_price">
<text class="text">¥</text>{{goodItem.marketPrice}}
</view>
<!-- 加入购物车图片 -->
<view class="goods_item_info_btn">
<image class="goods_image" src="/static/images/buybtn.png" mode=""/>
</view>
</view>
</view>
</navigator>
</view>
9.分类页面
1.渲染分类页面
// 导入已经封装好的请求api
import {reqCategoryData} from '../../api/category'
Page({
data:{
categoryList:[],
//设置被选择的高亮类
// 默认是第0个
activeIndex:0
},
// 实现一级分类的切换效果
updateAcitve(event){
// console.log(event);
// 打印元素的下标
// console.log(event.currentTarget.dataset);
// 讲点击元素的下表结构出来
const {index} = event.currentTarget.dataset;
console.log(index);
this.setData({
activeIndex:index
})
},
async getCategoryData(){
// 调用接口获取分类数据
const res = await reqCategoryData()
// 将分类数据打印出来
console.log(res);
this.setData({
categoryList:res.data
})
},
// 监听页面加载
onLoad(){
this.getCategoryData()
}
})
<!--pages/category/category.wxml-->
<view>
<view class="category-container">
<!-- 左侧的滚动视图区域 -->
<scroll-view class="category-left-view" scroll-y>
<!-- active:被选中的内容 -->
<view class="left-view-item {{ activeIndex === index ? 'active' : '' }}" wx:for="{{categoryList}}" wx:key="id" bindtap="updateAcitve" data-index="{{index}}">
{{ item.name}}
</view>
</scroll-view>
<!-- 右侧的滚动视图区域 -->
<scroll-view class="category-right-view" scroll-y>
<!-- 二级分类 -->
<view class="test">
<view wx:for="{{ 10 }}" wx:key="index" class="right-view-item">
<navigator class="navigator" url="">
<image class="" src="../../assets/images/cate-1.png" mode="" />
<text class="goods_item_name">真情告白</text>
</navigator>
</view>
</view>
</scroll-view>
</view>
</view>