重要提示(附完整代码链接)
(下篇)承接(中篇内容),接口文档和阿里图标库已在(上篇)给出,本文所粘贴代码或许存在不完整之处,完整代码请点此查看。
支付页面创建订单并发起预支付
(中篇)里边,我们有了token值,紧接着这里就需要创建订单了,获取订单编号,然后在发起预支付。现在回到pay文件夹下的index.js中进行修改代码,编写如果有token值时的下一步逻辑。这里用到了接口文档如下图所示。接口文档(地址)。
import {
request
} from "../../request/index.js"
Page({
setCart(cart) {
let allChecked = true;
let totalPrice = 0;
let totalNum = 0;
cart.forEach(v => {
if (v.checked) {
totalPrice += v.num * v.goods_price;
totalNum += v.num;
} else {
allChecked = false;
}
})
allChecked = cart.length != 0 ? allChecked : false;
this.setData({
cart,
totalPrice,
totalNum,
});
wx.setStorageSync('cart', cart);
},
data: {
address: {},
cart: [],
allChecked: false,
totalPrice: 0,
totalNum: 0
},
onLoad: function (options) {
},
onShow: function () {
let cart = wx.getStorageSync('cart')
const address = wx.getStorageSync('address');
cart = cart.filter(v => v.checked);
let totalPrice = 0;
let totalNum = 0;
cart.forEach(v => {
totalPrice += v.num * v.goods_price;
totalNum += v.num;
})
this.setData({
cart,
totalPrice,
totalNum,
address,
});
},
async handleOrderPay(e) {
const token = wx.getStorageSync('token');
if (!token) {
wx.navigateTo({
url: '/pages/auth/index',
})
return;
};
const header = {
Authorization: wx.getStorageSync('token')
};
const order_price = this.data.totalPrice;
const consignee_addr = this.data.address.all;
const cart = this.data.cart;
let goods = [];
cart.forEach(v => goods.push({
goods_id: v.goods_id,
goods_number: v.num,
goods_price: v.goods_price
}))
const orderParams = {
order_price,
consignee_addr,
goods
}
const
number
= await request({
url: "https://api-hmugo-web.itheima.net/api/public/v1/my/orders/create",
method: "POST",
data: orderParams,
header
});
wx.setStorageSync('order_number', number.data.message.order_number);
const order_number=wx.getStorageSync('order_number');
const res = await request({
url: "https://api-hmugo-web.itheima.net/api/public/v1/my/orders/req_unifiedorder",
method: "POST",
data: {order_number},
header
})
console.log(res);
},
onHide: function () {
},
onUnload: function () {
},
onPullDownRefresh: function () {
},
onReachBottom: function () {
},
onShareAppMessage: function () {
}
})
<!--pages/pay/index.wxml-->
<!-- 收货地址容器 -->
<view class="receive_address">
<view class="user_info_row">
<view class="user_info">
<view> {{address.userName}}</view>
<view>{{address.all}}</view>
</view>
<view class="user_phone">
{{address.telNumber}}
</view>
</view>
</view>
<!-- 购物车具体内容 -->
<view class="cart_content">
<view class="cart_title">购物车</view>
<view class="cart_main">
<view class="cart_item" wx:for="{{cart}}" wx:key="goods_id">
<!-- 商品图片 -->
<navigator class="cart_image">
<image mode="widthFix" src="{{item.goods_small_logo}}">
</image>
</navigator>
<!-- 商品信息 -->
<view class="cart_info">
<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 class="goods_nums">x{{item.num}}</view>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 底部导航栏 -->
<view class="footer_tool">
<!-- 总价格 -->
<view class="total_price_wrap">
<view class="total_price">
合计:<text class="total_price_number">¥{{totalPrice}}</text>
</view>
<view>包含运费</view>
</view>
<!-- 支付 -->
<view class="order_pay_wrap" bindtap="handleOrderPay">支付({{totalNum}})</view>
</view>
效果如下,我们查看返回的一些参数。pay:里边包含了该部分参数。
接下来是根据获取的支付参数,调用小程序内部的API,wx.requestPayment 进行调起微信支付。这里先在utils文件夹下的util.js内封装了一个函数如下
export const requestPayment=(pay)=>{
return new Promise((resolve,reject)=>{
wx.requestPayment({
...pay,
success: (result) => {
resolve(result)
},
fail: (err) => {
reject(err);
}
});
})
}
然后在pay文件夹下的index.js内引入并使用,同时查询下后台 订单状态,查询成功后,则跳转到订单页面。
import {
requestPayment
} from "../../utils/util.js";
import {request} from "../../request/index.js";
Page({
setCart(cart) {
let allChecked = true;
let totalPrice = 0;
let totalNum = 0;
cart.forEach(v => {
if (v.checked) {
totalPrice += v.num * v.goods_price;
totalNum += v.num;
} else {
allChecked = false;
}
})
allChecked = cart.length != 0 ? allChecked : false;
this.setData({
cart,
totalPrice,
totalNum,
});
wx.setStorageSync('cart', cart);
},
data: {
address: {},
cart: [],
allChecked: false,
totalPrice: 0,
totalNum: 0
},
onLoad: function (options) {
},
onShow: function () {
let cart = wx.getStorageSync('cart')
const address = wx.getStorageSync('address');
cart = cart.filter(v => v.checked);
let totalPrice = 0;
let totalNum = 0;
cart.forEach(v => {
totalPrice += v.num * v.goods_price;
totalNum += v.num;
})
this.setData({
cart,
totalPrice,
totalNum,
address,
});
},
async handleOrderPay(e) {
const token = wx.getStorageSync('token');
if (!token) {
wx.navigateTo({
url: '/pages/auth/index',
})
return;
};
const header = {
Authorization: wx.getStorageSync('token')
};
const order_price = this.data.totalPrice;
const consignee_addr = this.data.address.all;
const cart = this.data.cart;
let goods = [];
cart.forEach(v => goods.push({
goods_id: v.goods_id,
goods_number: v.num,
goods_price: v.goods_price
}))
const orderParams = {
order_price,
consignee_addr,
goods
}
const
number = await request({
url: "https://api-hmugo-web.itheima.net/api/public/v1/my/orders/create",
method: "POST",
data: orderParams,
header
});
wx.setStorageSync('order_number', number.data.message.order_number);
const order_number = wx.getStorageSync('order_number');
const zhifu = await request({
url: "https://api-hmugo-web.itheima.net/api/public/v1/my/orders/req_unifiedorder",
method: "POST",
data: {
order_number
},
});
console.log(zhifu);
wx.setStorageSync('pay', zhifu.data.message.pay);
const {pay} = wx.getStorageSync('pay')
await requestPayment(pay);
await request({ url: "https://api-hmugo-web.itheima.net/api/public/v1/my/orders/chkOrder", method: "POST", data: { order_number } });
wx.showToast({
title: '订单支付成功',
icon: 'success',
duration: 2000,
});
wx.navigateTo({
url: '/pages/order/index',
})
},
onHide: function () {
},
onUnload: function () {
},
onPullDownRefresh: function () {
},
onReachBottom: function () {
},
onShareAppMessage: function () {
}
})
注意:这里因为token值的缘故,再次出现了无效的token值报错。
移除购物车已支付的数据
当订单支付成功后,需要移除购物车中,已经支付了的商品的数据。只需要在跳转到订单页面代码之后,追加下列代码
let newCart = wx.getStorageSync("cart");
newCart = newCart.filter(v => !v.checked);
wx.setStorageSync("cart", newCart);
封装统一的请求头到request中
在request文件夹中的index.js进行封装。
let ajaxTimes = 0;
export const request = (params) => {
let header={...params.header};
if(params.url.includes("/my/")){
header["Authorization"]=wx.getStorageSync('token')
}
ajaxTimes++;
wx.showLoading({
title: '页面加载中',
mask: true
})
return new Promise((
resolve,
reject
) => {
wx.request({
...params,
header:header,
success: (result) => {
resolve(result)
},
fail: (err) => {
reject(err);
},
complete: () => {
ajaxTimes--;
if (ajaxTimes === 0) {
wx.hideLoading()
}
}
});
})
}
随后在pay文件夹下的index.js内删掉使用header的地方即可。
登录页面
在点击我的这个图标后,会跳转的个人中心页面,如果没登录的话会出现一个要登录的界面。 login文件夹下的四个文件代码分别为
Page({
data: {
userInfo: {},
hasUserInfo: false,
canIUseGetUserProfile: false,
},
onLoad() {
if (wx.getUserProfile) {
this.setData({
canIUseGetUserProfile: true
})
}
},
getUserProfile(e) {
wx.getUserProfile({
desc: '用于完善会员资料',
success: (res) => {
this.setData({
userInfo: res.userInfo,
hasUserInfo: true,
})
const users=res.userInfo;
wx.setStorageSync("userinfo", users)
wx.navigateBack({
delta: 1
});
}
})
},
})
{
"usingComponents": {},
"navigationBarTitleText": "登录"
}
<!--pages/login/index.wxml-->
<button type="primary" plain bindtap="getUserProfile" > 登录 </button>
button{
margin-top: 40rpx;
width: 70%;
}
最后效果如下
为了测验点击登录按钮后自动返回上一页(我的)页面,需要在user文件夹下的index.wxml增加一个navigator 标签。
<!--pages/user/index.wxml-->
<navigator url="/pages/login/index">前往登录</navigator>
随后在该页面点击前往登录,就会跳转到登录页面,登录完成后,返回到我的页面。
个人中心(我的)页面
这里代码如下,比较简单,注意阿里矢量图标库的使用方法,如class="iconfont icon-ding_dan" ,要先引入名为ding_dan 的图标。
Page({
data: {
userinfo: {},
collectNums: 0
},
onShow() {
const userinfo = wx.getStorageSync("userinfo");
const collect = wx.getStorageSync("collect") || [];
this.setData({
userinfo,
collectNums: collect.length
});
}
})
{
"usingComponents": {},
"navigationBarTitleText": "个人中心"
}
<!--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> -->
<image class="user_bg" src="https://tse4-mm.cn.bing.net/th/id/OIP-C.14Ruvh-IH1yXvYrC1rRXiAAAAA?w=140&h=150&c=7&r=0&o=5&dpr=1.1&pid=1.7" ></image>
<view class="user_info">
<!-- <image class="user_icon" src="{{userinfo.avatarUrl}}"></image> -->
<image class="user_icon" src="https://tse4-mm.cn.bing.net/th/id/OIP-C.14Ruvh-IH1yXvYrC1rRXiAAAAA?w=140&h=150&c=7&r=0&o=5&dpr=1.1&pid=1.7" ></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">0</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">0</view>
<view class="his_name">关注的商品</view>
</navigator>
<navigator>
<view class="his_num">0</view>
<view class="his_name">我的足迹</view>
</navigator>
</view>
<!-- 我的订单 -->
<view class="orders_wrap">
<view class="orders_title">我的订单</view>
<view class="order_content">
<navigator url="/pages/order/index?type=1">
<!-- icon-ding_dan是阿里矢量图标库的使用方法 -->
<view class="iconfont icon-ding_dan"></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-receipt_address"></view>
<view class="order_name">待收货</view>
</navigator>
<navigator>
<view class="iconfont icon-tuikuantuihuo;"></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 url="/pages/feedback/index" class="app_info_item">意见反馈</navigator>
<view class="app_info_item">关于我们</view>
</view>
<!-- 推荐 -->
<view class="recommend_wrap">
把应用推荐给其他人
</view>
</view>
</view>
page {
background-color: #edece8;
}
.user_info_wrap {
height: 45vh;
overflow: hidden;
background-color: var(--themColor);
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%;
transform: translateX(-50%);
top: 20%;
text-align: center;
}
.user_info_wrap .user_img_wrap .user_info .user_icon {
width: 150rpx;
height: 150rpx;
border-radius: 50%;
}
.user_info_wrap .user_img_wrap .user_info .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;
text-align: center;
padding: 10rpx 0;
}
.user_content .user_main .history_wrap navigator .his_num {
color: var(--themColor);
}
.user_content .user_main .orders_wrap {
background-color: #fff;
margin-top: 30rpx;
}
.user_content .user_main .orders_wrap .orders_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(--themColor);
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;
}
最后效果如下
订单查询实现
在user文件夹中的index.wxml,我们把“全部订单”,“待付款”等超链接,进行页面跳转的时候,同时传递了参数type,这决定了跳转到order文件夹(即订单页面)显示不同的内容。这里我们使用了之前在goods_list中使用过的tabs自定义组件,同理,先在order文件夹下的index.json引入组件,index.js中先写明导航栏的初始数据,然后使用。
Page({
data: {
tabs: [{
id: 0,
value: '综合',
isActive: true
},
{
id: 1,
value: '待付款',
isActive: false
},
{
id: 2,
value: '待发货',
isActive: false
},
{
id: 3,
value: '退款/退货',
isActive: false
}
],
},
handleTabsItemChange(e) {
const { index } = e.detail;
this.changeTitleByIndex(index);
this.getOrders(index+1);
},
})
{
"usingComponents": {
"tabs": "../../components/tabs/tabs"
},
"navigationBarTitleText": "订单查询"
}
<!--pages/order/index.wxml-->
<tabs tabs="{{tabs}}" bindtabsItemChange="handleTabsItemChange">
<view class="order_main">
<view wx:for="{{orders}}" wx:key="order_id" class="order_item">
<view class="order_no_row">
<view class="order_no_text">订单编号</view>
<view class="order_no_value">{{item.order_number}}</view>
</view>
<view class="order_price_row">
<view class="order_price_text">订单价格</view>
<view class="order_price_value">¥{{item.order_price}}</view>
</view>
<view class="order_time_row">
<view class="order_time_text">订单日期</view>
<view class="order_time_value">{{item.create_time_cn}}</view>
</view>
</view>
</view>
</tabs>
.order_main .order_item {
padding: 20rpx;
border-bottom: 1rpx solid #ccc;
color: #666;
}
.order_main .order_item .order_no_row {
display: flex;
padding: 10rpx 0;
justify-content: space-between;
}
.order_main .order_item .order_price_row {
display: flex;
padding: 10rpx 0;
justify-content: space-between;
}
.order_main .order_item .order_price_row .order_price_value {
color: var(--themeColor);
font-size: 32rpx;
}
.order_main .order_item .order_time_row {
display: flex;
padding: 10rpx 0;
justify-content: space-between;
}
最后效果如下 现在我们要实现的功能是,在个人中心页面点击不同的按钮后,如全部订单,待收货等,跳转到order页面(订单)中显示不同的内容,通过传递过来的参数type。
- 页面被打开的时候 onShow函数启动
onShow 不同于onLoad 无法在形参上接收 options参数 判断缓存中有没有token 没有 直接跳转到授权页面 有则 直接往下进行 1.1. 获取url上的参数type 1.2. 根据type来决定页面标题的数组元素 哪个被激活选中 1.3. 根据type 去发送请求获取订单数据 1.4. 渲染页面 - 点击不同的标题 重新发送请求来获取和渲染数据
import {
request
} from "../../request/index.js";
Page({
data: {
orders: [],
tabs: [{
id: 0,
value: '综合',
isActive: true
},
{
id: 1,
value: '待付款',
isActive: false
},
{
id: 2,
value: '待发货',
isActive: false
},
{
id: 3,
value: '退款/退货',
isActive: false
}
],
},
onShow(options) {
const token = wx.getStorageSync("token");
if (!token) {
wx.navigateTo({
url: '/pages/auth/index'
});
return;
}
let pages = getCurrentPages();
let currentPage = pages[pages.length - 1];
const {
type
} = currentPage.options;
this.changeTitleByIndex(type - 1);
this.getOrders(type);
},
async getOrders(type) {
const res = await request({
url: "https://api-hmugo-web.itheima.net/api/public/v1/my/orders/all",
data: {
type
}
});
this.setData({
orders: res.data.message.orders.map(v => ({
...v,
create_time_cn: (new Date(v.create_time * 1000).toLocaleString())
}))
})
},
changeTitleByIndex(index) {
let {
tabs
} = this.data;
tabs.forEach((v, i) => i === index ? v.isActive = true : v.isActive = false);
this.setData({
tabs
})
},
handleTabsItemChange(e) {
const {
index
} = e.detail;
this.changeTitleByIndex(index);
this.getOrders(index + 1);
},
})
因为,支付功能的完整实现需要企业微信进行开发,所以我们前边并没有完全实现支付功能,在这里的订单页面,只有显示一堆待付款。效果如下 其他页面为空。
实现商品的收藏功能
在商品的详情页面,有一个收藏按钮,点击后会提示收藏成功,此时在点击个人中心页面,选择收藏的商品点击,则跳转到收藏页面,展示刚才被收藏的商品。实现的原理很简单,点击收藏后,将该商品存入缓存数组中,点击取消收藏后,该商品的信息从缓存数组中删除掉。 现在回到goods_detail页面文件夹,修改里边index.js代码。实现的步骤如下
- 页面加载onShow函数的时候 加载缓存中的商品收藏的数据
- 判断当前商品是不是被收藏
2.1 是 改变页面的图标 2.2 不是的话,无操作 - 点击商品收藏按钮
3.1 判断该商品是否存在于缓存数组中 3.2 已经存在 把该商品删除 3.3 没有存在 把商品添加到收藏数组中 存入到缓存中即可
我们设置了一个检验在商品详情的index.js中,如果用户未登录,那么需要跳转到登录页面进行登录后才可以进行收藏功能。
import {
request
} from "../../request/index.js"
Page({
data: {
goodsObj: {},
isCollect: false
},
goodsInfo: {},
onLoad: function (options) {
const {
goods_id
} = options;
this.getGoodsDetail(goods_id);
},
async getGoodsDetail(goods_id) {
const goodsObj = await request({
url: "https://api-hmugo-web.itheima.net/api/public/v1/goods/detail",
data: {
goods_id
}
});
this.goodsInfo = goodsObj.data.message;
let collect = wx.getStorageSync("collect") || [];
let isCollect = collect.some(v => v.goods_id === this.goodsInfo.goods_id);
this.setData({
goodsObj: {
goods_name: goodsObj.data.message.goods_name,
goods_price: goodsObj.data.message.goods_price,
goods_introduce: goodsObj.data.message.goods_introduce.replace(/\.webp/g, '.jpg'),
pics: goodsObj.data.message.pics
},
isCollect
})
},
handlePrevewImage(e) {
console.log('预览');
const urls = this.goodsInfo.pics.map(v => v.pics_mid)
const current = e.currentTarget.dataset.url
wx.previewImage({
current: current,
urls: urls
})
},
handleCartAdd(e) {
let cart = wx.getStorageSync('cart') || [];
let index = cart.findIndex(v => v.goods_id === this.goodsInfo.goods_id);
if (index === -1) {
this.goodsInfo.num = 1;
this.goodsInfo.checked = true;
cart.push(this.goodsInfo);
} else {
cart[index].num++;
}
wx.setStorageSync("cart", cart);
wx.showToast({
title: '添加成功',
icon: 'success',
mask: 'true'
})
},
handleCollect() {
let userinfo = wx.getStorageSync("userinfo");
if (userinfo) {
let isCollect = false;
let collect = wx.getStorageSync("collect") || [];
let index = collect.findIndex(v => v.goods_id === this.goodsInfo.goods_id);
if (index !== -1) {
collect.splice(index, 1);
isCollect = false;
wx.showToast({
title: '取消成功',
icon: 'success',
mask: true
});
} else {
collect.push(this.goodsInfo);
isCollect = true;
wx.showToast({
title: '收藏成功',
icon: 'success',
mask: true
});
}
wx.setStorageSync("collect", collect);
this.setData({
isCollect
})
}else{
wx.navigateTo({
url: '/pages/login/index',
})
}
},
onShow: function () {
let pages = getCurrentPages();
let currentPage = pages[pages.length - 1];
let options = currentPage.options;
const {
goods_id
} = options;
this.getGoodsDetail(goods_id);
},
onHide: function () {
},
})
给收藏按钮绑定点击事件
<!--pages/goods_detail/index.wxml-->
<view class="detail_swiper">
<!-- 轮播图内容,bindtap绑定一个预览大图事件 -->
<swiper autoplay="true" circular="true" indicator-dots="true" bindtap="handlePrevewImage"
data-url="{{item.pics_mid}}">
<swiper-item wx:for="{{goodsObj.pics}}" wx:key="pics_id">
<image mode="widthFix" src="{{item.pics_mid}}"></image>
</swiper-item>
</swiper>
</view>
<!-- 商品内容文字 -->
<!-- 商品价格 -->
<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-shoucang1':'icon-shoucang'}}"></text>
<view class="collect_text">收藏</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>
<!-- 增加联系客服功能 ,隐藏在客服的下一层,透明度为0,设置其高和宽与客服部分一致-->
<button open-type="contact"></button>
</view>
<!-- 分享 -->
<view class="tool_item">
<view class="iconfont icon-fenxiang"></view>
<view>分享</view>
<button open-type="share"></button>
</view>
<!-- 购物车 -->
<!-- switchTab允许跳转tabBar(导航栏)页面 -->
<navigator open-type="switchTab" url="/pages/cart/index" class="tool_item">
<view class="tool_item">
<view class="iconfont icon-gouwuche"></view>
<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>
然后在collect文件夹内进行修改代码,
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
});
},
handleTabsItemChange(e) {
const { index } = e.detail;
let { tabs } = this.data;
tabs.forEach((v, i) => i === index ? v.isActive = true : v.isActive = false);
this.setData({
tabs
})
}
})
{
"usingComponents": {
"Tabs":"../../components/tabs/tabs"
},
"navigationBarTitleText":"商品收藏"
}
<!--pages/collect/index.wxml-->
<tabs tabs="{{tabs}}" bindtabsItemChange="handleTabsItemChange">
<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:'https://ww1.sinaimg.cn/large/007rAy9hgy1g24by9t530j30i20i2glm.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>
</tabs>
.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;
border-bottom: 1px solid #ccc;
background-color: #fff;
}
.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;
}
最后效果如下
搜索页面功能的实现
搜索页面首先有搜索框,我们之前自定义的一个组件,本质是一个超链接,可以在搜索框里边进行输入关键字搜索。在此页面,search文件夹的内容,我们不使用自定义的组件实现。
注意:这里边有个地方,即 防抖 (防止抖动) 使用定时器经过一段时间没有输入了,在发送请求 ,否则每输入一个字符都会发送一次请求,页面抖动。
- 防抖 一般 输入框中 防止重复输入 重复发送请求
- 节流 一般是用在页面下拉和上拉
- 两种都可以通过定义全局的定时器id实现
import { request } from "../../request/index.js";
Page({
data: {
goods:[],
isFocus:false,
inpValue:""
},
TimeId:-1,
handleInput(e){
const {value}=e.detail;
if(!value.trim()){
this.setData({
goods:[],
isFocus:false
})
return;
}
this.setData({
isFocus:true
})
clearTimeout(this.TimeId);
this.TimeId=setTimeout(() => {
this.qsearch(value);
}, 1000);
},
async qsearch(query){
const res=await request({url:"https://api-hmugo-web.itheima.net/api/public/v1/goods/qsearch",data:{query}});
console.log(res);
this.setData({
goods:res
})
},
handleCancel(){
this.setData({
inpValue:"",
isFocus:false,
goods:[]
})
}
})
{
"usingComponents": {},
"navigationBarTitleText":"搜索中心"
}
<!--pages/search/index.wxml-->
<view class="search_row">
<input value="{{inpValue}}" placeholder="请输入您要搜索的商品" bindinput="handleInput"> </input>
<button bindtap="handleCancel" hidden="{{!isFocus}}">取消</button>
</view>
<view class="search_content">
<navigator url="/pages/goods_detail/index?goods_id={{item.data.message.goods_id}}" class="search_item" wx:for="{{goods.data.message}}" wx:key="goods_id">
{{item.goods_name}}
</navigator>
</view>
page {
background-color: #dedede;
padding: 20rpx;
}
.search_row {
height: 60rpx;
display: flex;
}
.search_row input {
background-color: #fff;
flex: 1;
height: 100%;
padding-left: 30rpx;
}
.search_row button {
width: 110rpx;
height: 100%;
padding: 0;
margin: 0 10rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 26rpx;
}
.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;
}
最后效果如下
意见反馈页面
在小程序的官方开发文档,button按钮有一个open-type开放能力,可以直接掉起意见反馈。这里我们不借用开放能力,自定义一个页面。首先是修改个人中心的代码,给意见反馈部分,增加一个跳转链接功能。
<!--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> -->
<image class="user_bg" src="https://tse4-mm.cn.bing.net/th/id/OIP-C.14Ruvh-IH1yXvYrC1rRXiAAAAA?w=140&h=150&c=7&r=0&o=5&dpr=1.1&pid=1.7" ></image>
<view class="user_info">
<!-- <image class="user_icon" src="{{userinfo.avatarUrl}}"></image> -->
<image class="user_icon" src="https://tse4-mm.cn.bing.net/th/id/OIP-C.14Ruvh-IH1yXvYrC1rRXiAAAAA?w=140&h=150&c=7&r=0&o=5&dpr=1.1&pid=1.7" ></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">0</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">0</view>
<view class="his_name">关注的商品</view>
</navigator>
<navigator>
<view class="his_num">0</view>
<view class="his_name">我的足迹</view>
</navigator>
</view>
<!-- 我的订单 -->
<view class="orders_wrap">
<view class="orders_title">我的订单</view>
<view class="order_content">
<!-- 这里使用url跳转时,传递了type参数,点击下边不同链接时,跳转至order页面,但是显示不同内容。 -->
<navigator url="/pages/order/index?type=1">
<!-- icon-ding_dan是阿里矢量图标库的使用方法 -->
<view class="iconfont icon-ding_dan"></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-receipt_address"></view>
<view class="order_name">待收货</view>
</navigator>
<navigator>
<view class="iconfont icon-tuikuantuihuo;"></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 url="/pages/feedback/index" class="app_info_item">意见反馈</navigator>
<view class="app_info_item">关于我们</view>
</view>
<!-- 推荐 -->
<view class="recommend_wrap">
把应用推荐给其他人
</view>
</view>
</view>
接下来是在feedback文件夹下,index.json进行修改代码,包括,页面命名,顶部导航栏组件的引入等准备工作。
{
"usingComponents": {
"tabs": "../../components/tabs/tabs"
},
"navigationBarTitleText": "意见反馈"
}
然后为了实现意见反馈页面中能实现添加图片,也能删除图片功能,我们自定义一个组件,在components文件夹下新建UpImg文件夹,然后鼠标右键选择该文件夹,选择新建compinents,然后输入UpImg,微信开发者工具自动帮我们生成以下文件。 然后在该文件夹UpImg下修改里边文件代码
Component({
properties: {
src:{
type:String,
value:""
}
},
data: {
},
methods: {
}
})
{
"component": true,
"usingComponents": {}
}
<!--components/UpImg/UpImg.wxml-->
<view class="up_img_wrap">
<!-- 图片 -->
<image src="{{src}}"></image>
<!-- 表示删除的图标 -->
<icon type="clear" size="23" color="red">
</icon>
</view>
.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;
}
随后回到feedback文件夹中的index.json引入组件UpImg。
{
"usingComponents": {
"tabs": "../../components/tabs/tabs",
"UpImg":"../../components/UpImg/UpImg"
},
"navigationBarTitleText": "意见反馈"
}
在index.js内修改代码逻辑
Page({
data: {
tabs: [{
id: 0,
value: "体验问题",
isActive: true
},
{
id: 1,
value: "商品、商家投诉",
isActive: false
}
],
chooseImgs: [],
textVal: [],
},
UpLoadImgs: [],
handleTabsItemChange(e) {
const {
index
} = e.detail;
let {
tabs
} = this.data;
tabs.forEach((v, i) => i === index ? v.isActive = true : v.isActive = false);
this.setData({
tabs
})
},
handleChooseImg(e) {
wx.chooseImage({
count: 9,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (result) => {
this.setData({
chooseImgs: [...this.data.chooseImgs, ...result.tempFilePaths]
})
}
})
},
handleRemoveImg(e) {
const {
index
} = e.currentTarget.dataset.index;
let {
chooseImgs
} = this.data;
chooseImgs.splice(index, 1)
this.setData({
chooseImgs
})
},
handleTextInput(e) {
this.setData({
textVal: e.detail.value
})
},
handleFormSubmit(e) {
const {
textVal,
chooseImgs
} = this.data;
var str=textVal.trim;
if (!str) {
wx.showToast({
title: '您的输入为空!',
mask: 'true',
icon: 'error'
})
return;
};
wx.showLoading({
title: "正在上传中",
mask: true
});
if(chooseImgs.length != 0){
chooseImgs.forEach((v, i) => {
wx.uploadFile({
filePath: v,
name: 'image',
url: 'https://img.coolcr.cn/api/upload',
formData: {},
success: (result) => {
let url = JSON.parse(result.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(
wx.hideLoading(),
console.log('只提交了文本'),
wx.navigateBack({
delta: 1
})
)
}
})
在index.wxml中书写结构
<!--pages/feedback/index.wxml-->
<tabs tabs="{{tabs}}" bindtabsItemChange="handleTabsItemChange">
<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 -->
<textarea value="{{textVal}}" bindinput="handleTextInput" placeholder="请描述一下您的问题"> </textarea>
<view class="fb_tool">
<!-- +号按钮增加一个选择图片事件 -->
<button bindtap="handleChooseImg">+</button>
<!-- 渲染选择的图片 -->
<!-- 当数据是一些简单数据的时候,我们这里的Wx:key要写为*this -->
<!-- data-index是传递当前点击的图片的索引值 -->
<view class="up_img_item" wx:for="{{chooseImgs}}" wx:key="*this" bindtap="handleRemoveImg"
data-index="{{index}}">
<UpImg src="{{item}}"></UpImg>
</view>
</view>
</view>
<view class="form_btn_wrap">
<button bindtap="handleFormSubmit" type="warn">
<icon type="success_no_circle" size="23" color="white">
</icon>
提交
</button>
</view>
</view>
</tabs>
在index.wxss书写样式
page {
background-color: #eeeeee;
}
.fb_main {
padding: 20rpx;
color: #666;
}
.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_tool {
display: flex;
flex-wrap: wrap;
padding-bottom: 30rpx;
}
.fb_main .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;
margin-top: 20rpx;
color: #ccc;
}
.fb_main .fb_tool .up_img_item {
margin-left: 20rpx;
margin-top: 20rpx;
}
.fb_main .form_btn_wrap {
margin-top: 20rpx;
display: flex;
justify-content: flex-end;
}
.fb_main .form_btn_wrap button {
margin: 0;
width: 30%;
}
最后效果如下
首页中添加商品的跳转链接
现在给首页中,点击相关图片后,实现跳转,首先是轮播图。因为这里后台接口写的商品的地址有修改,比如goods_id为129的商品,他的地址url为 /pages/goods_detail/main?goods_id=129",而我们的文件夹内的文件都是index命名的,所以这里借用了goods_id指定路径。给navigator增加跳转url。同时也给分类一栏增添了url,写死了一个路径url="/pages/category/index" 。
<view class="gouwu">
<!-- 搜索框 -->
<SearchInput></SearchInput>
<!-- 轮播图 -->
<view class="lunbotu">
<!-- swiper、image等标签一开始设置过了默认的宽度和高度 -->
<!-- 在swiper标签上开始autoplay自动轮播属性,并显示轮播指示点 和衔接轮播-->
<swiper autoplay="true" indicator-dots="true" circular="true">
<!-- 需要给swiper-item进行循环 -->
<!-- 循环的内容都是从服务器端返回并存入swiperList中获得的 -->
<swiper-item wx:for="{{swiperList}}" wx:key="goods_id">
<!-- 给首页的轮播图增添跳转页面功能 -->
<!--因为这里后台接口写的商品的地址有修改,为 /pages/goods_detail/main?goods_id=129",而我们
的文件都是index命名的,所以这里借用了goods_id指定路径-->
<navigator url="/pages/goods_detail/index?goods_id={{item.goods_id}}" >
<!-- 图片标签增加mode属性widthFix,图片标签的内容的高宽等比例变化 -->
<image mode="widthFix" src="{{item.image_src}}"></image>
</navigator>
</swiper-item>
</swiper>
</view>
<!-- 分类导航 -->
<view class="fenlei">
<navigator wx:for="{{cateList}}" wx:key="name" url="/pages/category/index" open-type="switchTab">
<image mode="widthFix" src="{{item.image_src}}"></image>
</navigator>
</view>
<!-- 楼层 -->
<view class="louceng">
<view class="floor_group" wx:for="{{floorList}}" wx:for-item="item1" wx:for-index="index1" wx:key="floor_title">
<!-- 楼层的标题 -->
<view class="title">
<image mode="widthFix" src="{{item1.floor_title.image_src}}"></image>
</view>
<!-- 楼层的具体内容 -->
<view class="list">
<navigator wx:for="{{item1.product_list}}" wx:for-item="item2" wx:for-index="index2" wx:key="name">
<image mode="{{index2===0? 'widthFix':'scaleToFill'}}" src="{{item2.image_src}}"></image>
</navigator>
</view>
</view>
</view>
</view>
最后给首页的具体内容中的图片增添跳转链接,如时尚女装中的图片,点击会跳转到商品列表页。
<view class="gouwu">
<!-- 搜索框 -->
<SearchInput></SearchInput>
<!-- 轮播图 -->
<view class="lunbotu">
<!-- swiper、image等标签一开始设置过了默认的宽度和高度 -->
<!-- 在swiper标签上开始autoplay自动轮播属性,并显示轮播指示点 和衔接轮播-->
<swiper autoplay="true" indicator-dots="true" circular="true">
<!-- 需要给swiper-item进行循环 -->
<!-- 循环的内容都是从服务器端返回并存入swiperList中获得的 -->
<swiper-item wx:for="{{swiperList}}" wx:key="goods_id">
<!-- 给首页的轮播图增添跳转页面功能 -->
<!--因为这里后台接口写的商品的地址有修改,为 /pages/goods_detail/main?goods_id=129",而我们
的文件都是index命名的,所以这里借用了goods_id指定路径-->
<navigator url="/pages/goods_detail/index?goods_id={{item.goods_id}}">
<!-- 图片标签增加mode属性widthFix,图片标签的内容的高宽等比例变化 -->
<image mode="widthFix" src="{{item.image_src}}"></image>
</navigator>
</swiper-item>
</swiper>
</view>
<!-- 分类导航 -->
<view class="fenlei">
<!-- 给分类导航添加跳转url -->
<navigator wx:for="{{cateList}}" wx:key="name" url="/pages/category/index" open-type="switchTab">
<image mode="widthFix" src="{{item.image_src}}"></image>
</navigator>
</view>
<!-- 楼层 -->
<view class="louceng">
<view class="floor_group" wx:for="{{floorList}}" wx:for-item="item1" wx:for-index="index1" wx:key="floor_title">
<!-- 楼层的标题 -->
<view class="title">
<image mode="widthFix" src="{{item1.floor_title.image_src}}"></image>
</view>
<!-- 楼层的具体内容 -->
<view class="list">
<!-- 点击具体内容里边的图片时 ,跳转到相关页面 -->
<navigator wx:for="{{item1.product_list}}" wx:for-item="item2" wx:for-index="index2" wx:key="name"
url="{{item2.navigator_url}}">
<image mode="{{index2===0? 'widthFix':'scaleToFill'}}" src="{{item2.image_src}}"></image>
</navigator>
</view>
</view>
</view>
</view>
项目发布
实际应用中,代码写完后,会由企业的相关人员进行测试,这里我们跳过,进入到项目发布步骤。首先,把小程序开发者工具,本地设置中不校验合法域名,进行取消勾选。否则在用户的实际应用时,会造成不安全因素。然后我们把小程序开发中使用的所有外网链接,在小程序云平台,加入到白名单就可以了。 然后这里的APPID要修改为自己的,或者企业的,不能是测试号了 然后确保代码总共不超过2M。 然后点击上传 然后打开微信公众平台,点击版本管理,然后下拉页面,找到开发版本 这里如果我们选择提交审核,那么就会提交给微信官方审核,然后正式上线。这里我选择的是体验版,同时,将一些链接url放入白名单,找到开发,开发管理,服务器域名中进行配置。
|