6.商城(category)
#index.js
import { request} from "../../request/index.js";
import regeneratorRuntime from '../../lib/runtime/runtime';
Page({
data: {
//左侧的菜单数据
leftMenuList:[],
//右侧的商品数据
rightContent:[],
//被点击的左侧的菜单
currentIndex:0,
// 右侧内容的滚动条距离顶部的距离
scrollTop:0
},
//接口的返回数据
Cates:[],
onLoad: function (options) {
/*
0 web中的本地存储和 小程序中的本地存储的区别
1 写代码的方式不一样了
web: localStorage.setItem('key', data) localStorage.getItem("key")
小程序中: wx.setStorageSync('key', "data";(储存数据) wx.getStorageSync('key');(获取数据)
2 存的时候有没有做类型转换
web: 不管存入的是什么类型的数据,最终都会先调用一下 toString(), 把数据变成字符串再存入进去
小程序: 不需要进行类型转换 村什么类型就获取什么类型
1 先判断一下本地存储有没有旧的数据
{time:Data.now(),data:[...]}
2 没有旧数据 直接发送新请求
3 有旧数据 同时 旧数据也没有过期 就使用本地存储中的旧数据即可
*/
// 1 获取本地存储中的数据
const Cates=wx.getStorageSync('cates');
// 2 判断
if(!Cates){
// 不存在 发送请求获取数据
this.getCates();
}
else{
//有旧的数据 定义过期时间 10s改为5分钟
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
})
}
}
},
//获取分类数据
async getCates(){
// request({
// url:'/categories'
// })
// .then(res=>{
// //console.log(res);
// this.Cates=res.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,
// })
// })
// 1 使用es7的 async await发送请求
const res = await request({url:"/categories"});
this.Cates = res.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,
})
},
//左侧菜单的点击事件
handleItemTap(e){
/* 1 获取被点击的标题身上的索引
2 给data中的currentIndex赋值
3 根据不同的索引来渲染右侧的商品内容
*/
const{index}=e.currentTarget.dataset;
let rightContent=this.Cates[index].children;
this.setData({
currentIndex:index,
rightContent,
// 重新设置 右侧内容的scroll-view 标签与顶部的距离
scrollTop:0
})
}
})
#index.json
{
"usingComponents": {
"searchinput":"../../components/searchinput/search input"
},
"navigationBarTitleText": "商品分类 "
}
#index.wxml
<view class="cates">
<searchinput></searchinput>
<view class="cates_container">
<!--左侧菜单 -->
<scroll-view scroll-y class="left_menu">
<view
class="menu_item {{index===currentIndex?'active':''}}"
wx:for="{{leftMenuList}}"
wx:key="*this"
bindtap="handleItemTap"
data-index="{{index}}"
>
{{item}}
</view>
</scroll-view>
<!--右侧商品内容-->
<scroll-view scroll-top="{{scrollTop}}" scroll-y class="right_content">
<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"
url="/pages/goods_list/index?cid={{item2.cat_id}}"
>
<image mode="widthFix" src="{{item2.cat_icon}}"></image>
<view class="goods_name">{{item2.cat_name}}</view>
</navigator>
</view>
</view>
</scroll-view>
</view>
</view>
#index.less
pages{
height: 100%;
}
.cates{
height: 100%;
.cates_container{
/* less中使用calc的时候要注意 */
height: ~'calc( 100vh - 90rpx)';
display: flex;
.left_menu{
/* 子项 高度100% flex*/
flex: 2;
.menu_item{
height: 80rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 30rpx;
}
.active{
color:var(--themeColor);
border-left: 5rpx solid currentColor;
}
}
.right_content{
/* 子项 高度100% flex*/
flex: 5;
.goods_group{
.goods_title{
height: 80rpx;
display: flex;
justify-content: center;
align-items: center;
.delimiter{
color:#ccc;
padding: 0 10rpx;
}
.title{}
}
.goods_list{
display: flex;
flex-wrap: wrap;
navigator{
width: 33.33%;
text-align: center;
iamge{
width: 50%;
}
.goods_name{
}
}
}
}
}
}
}
#index.wxss
pages {
height: 100%;
}
.cates {
height: 100%;
}
.cates .cates_container {
/* less中使用calc的时候要注意 */
height: calc( 100vh - 90rpx);
display: flex;
}
.cates .cates_container .left_menu {
/* 子项 高度100% flex*/
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 {
/* 子项 高度100% flex*/
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 iamge {
width: 50%;
}
7.商城列表(good_list)
#index.js
/*
1 用户上滑页面 滚动条触底 开始加载下一页数据
1 找到滚动条触底事件 微信小程序官方文档寻找
2 判断还有没有下一页数据
1 获取到总页数
总页数 = Math.ceil(总条数 / 页容量 pagesize)
总页数 = Math.ceil( 23/ 10)=3
2 获取到当前页码 pagenum
3 判断一下当前的页码是否大于等于 总页数
表示 没有下一页数据
3 假如没有下一页数据 弹出提示
4 加入还有下一页数据 用来加载下一页数据
1 当前的页码++
2 重新发送请求
3 数据请求回来 要对data中的数组 进行 拼接 而不是全部替换!!!!
2 下拉刷新页面
1 出发下拉刷新事件 需要在页面的json文件中开启一个配置项
找到触发下拉刷新的事件
2 重置 数据 数组
3 重置页码 设置为1
4 发送请求
5 关闭下拉刷新窗口
*/
import { request} from "../../request/index.js";
import regeneratorRuntime from '../../lib/runtime/runtime';
Page({
/**
* 页面的初始数据
*/
data: {
tabs:[
{
id:0,
value:"商品",
isActive:true
},
],
goodsList:[]
},
//接口要的参数
QueryParams:{
query:"",
cid:"",
pagenum:1,
pagesize:10
},
// 总页数
totalPages: 1,
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.QueryParams.cid=options.cid||"";
this.QueryParams.query=options.query||"";
this.getGoodsList();
},
//获取商品列表数据
async getGoodsList(){
const data=await request({url:"/goods/search",data:this.QueryParams});
const message=data.data
const res=message.message
//获取总条数
const total=res.total;
// 计算总页数
this.totalPages=Math.ceil(total/this.QueryParams.pagesize);
// console.log(this.totalPages);
this.setData({
// 拼接数组
goodsList:[...this.data.goodsList,...res.goods]
})
// 关闭下拉刷新窗口 如果没有调用下拉刷新窗口 直接关闭也不会报错
wx.stopPullDownRefresh();
},
//标题点击事件 从子组件传递过来
handleTabsItemChange(e){
// 1 获取被点击的标题索引
const {index}=e.detail;
// 2 修改源数组
let{tabs}=this.data;
tabs.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
// 3 赋值到data中
this.setData({
tabs
})
},
// 页面上滑 滚动条触底事件
onReachBottom(){
if(this.QueryParams.pagenum>=this.totalPages){
//没有下一页数据
// console.log("没有下一页数据");
wx.showToast({
title: '没有下一页数据',
});
}
else{
//还有下一页
// console.log("还有下一页");
this.QueryParams.pagenum++;
this.getGoodsList();
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
// console.log("刷新");
// 1 重置数组
this.setData({
goodsList:[]
})
// 2 重置页码
this.QueryParams.pagenum=1;
// 3 发送请求
this.getGoodsList();
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
#index.json
{
"usingComponents": {
"searchinput":"../../components/searchinput/search input",
"Tabs":"../../components/Tabs/Tabs"
},
"navigationBarTitleText": "商品列表 " ,
"enablePullDownRefresh": true,
"backgroundTextStyle": "dark",
"backgroundColor": "#ffd68f"
}
#index.wxml
<searchinput></searchinput>
<!-- 监听事件 -->
<Tabs tabs="{{tabs}}" bindtabsItemChange="handleTabsItemChange" >
<block wx:if="{{tabs[0].isActive}}" >
<view class="first_tab" >
<navigator class="goods_item"
wx:for="{{goodsList}}"
wx:key="goods_id"
url="/pages/goos_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://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.51yuansu.com%2Fpic3%2Fcover%2F00%2F86%2F05%2F58dac1dd00b60_610.jpg&refer=http%3A%2F%2Fpic.51yuansu.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1646973396&t=5e4d0a94808567e7e968e4a2bb755c17'}}"></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>
</block>
</Tabs>
#index.less
.first_tab{
display: flex;
flex-direction: row;
flex-wrap: wrap;
background-color: #ffd68f;
.goods_item{
display: flex;
flex-wrap: wrap;
padding: 20rpx;
border-top: 10rpx solid #fff;
border-bottom: 10rpx solid #fff;
border-left: 10rpx solid #fff;;
width: 50%;
.goods_img_wrap{
padding-left: 0rpx;
align-content: center;
justify-content: center;
width: 100%;
image{
width:100%;
}
}
.goods_info_wrap{
align-content: center;
justify-content: flex-start;
.goods_name{
display: -webkit-box;
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp:1;/*设置 需要显示的行数*/
}
.goods_price{
padding-left: 0rpx;
}
}
}
}
#index.wxss
.first_tab {
display: flex;
flex-direction: row;
flex-wrap: wrap;
background-color: #ffd68f;
}
.first_tab .goods_item {
display: flex;
flex-wrap: wrap;
padding: 20rpx;
border: 30rpx solid #fff;
border-radius: 15%;
width: 50%;
}
.first_tab .goods_item .goods_img_wrap {
padding-left: 0rpx;
align-content: center;
justify-content: center;
width: 100%;
}
.first_tab .goods_item .goods_img_wrap image {
width: 100%;
}
.first_tab .goods_item .goods_info_wrap {
color: black;
align-content: center;
justify-content: flex-start;
}
.first_tab .goods_item .goods_info_wrap .goods_name {
display: -webkit-box;
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
/*设置 需要显示的行数*/
}
.first_tab .goods_item .goods_info_wrap .goods_price {
padding-left: 0rpx;
}
8.商城详情(good_detail)
#index.js
/*
1 发送请求获取数据
2 点击轮播图 预览大图
1 给轮播图绑定点击事件
2 调用小程序的api previewImage
3 点击 加入购物车
1 绑定点击事件
2 获取缓存中的购物车数据 数组格式
3 先判断 当前的商品是否已经存在于 购物车
4 已经存在修改商品数据 执行购物车数量++ 重新把购物车数组填充回缓存中
5 不存在于购物车的数组中 直接给购物车数组添加一个新的元素 县原武带上购买数量属性 num 重新把购物车数组 填充回缓存中
6 弹出提示
4 商品收藏
1 页面onshow的时候 加载缓存中的商品收藏数据
2 判断商品是不是被收藏
1 是 改变图标
1 不是 不改变图标
3 点击商品收藏按钮
1 判断商品是否存在于缓存数组中
2 已经存在 把该商品删除
3 没有存在 把商品添加到收藏数组中 存入缓存中即可
*/
import { request} from "../../request/index.js";
import regeneratorRuntime from '../../lib/runtime/runtime';
Page({
/**
* 页面的初始数据
*/
data: {
goodsObj:{},
// 商品是否被收藏
isCollect:false
},
// 商品对象
GoodsInfo:{},
/**
* 生命周期函数--监听页面加载
*/
onShow: function () {
let pages=getCurrentPages();
let currentPages=pages[pages.length-1];
let options=currentPages.options;
const{goods_id}=options;
this.getGoodsDetail(goods_id);
},
// 获取商品详情数据
async getGoodsDetail(goods_id){
const data=await request({url:"/goods/detail",data:{goods_id}});
const message=data.data;
const goodsObj=message.message;
this.GoodsInfo=goodsObj;
// 1 获取缓存中的商品收藏的数组
let collect=wx.getStorageSync('collect')||[];
// 2 判断当前商品是否被收藏
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
})
},
// 点击轮播图 放大预览
handlePrevewImage(e){
// 1 先构造要预览的图片数组
const urls=this.GoodsInfo.pics.map(v=>v.pics_mid);
// 2 接受传递过来的图片url
const current=e.currentTarget.dataset.url;
wx.previewImage({
current,
urls,
});
},
handleCartAdd(){
// 1 获取缓存中购物车数组
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{
// 4 已经存在购物车数据 执行 num++
cart[index].num++;
}
// 5 把购物车重新添加回缓存中
wx.setStorageSync("cart",cart);
// 6 弹窗提示
wx.showToast({
title: '加入成功',
icon:'success',
//true 防止用户疯狂点击按钮
mask:true
});
},
// 点击商品收藏图标
handleCollect(){
let isCollect=false;
// 1 获取缓存中的商品收藏数组
let collect=wx.getStorageSync('collect')||[];
// 2 判断该商品是否被收藏
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=true;
wx.showToast({
title: '收藏成功',
icon:'success',
mask:true
});
}
// 4 把数组存入到缓存中
wx.setStorageSync('collect', collect);
// 5 修改data 中的属性 isCollect
this.setData({
isCollect
})
}
})
#index.json
{
"usingComponents": {},
"navigationBarTitleText": "商品详情"
}
#index.wxml
<view class="detail_swiper">
<swiper autoplay circular indicator-dots>
<swiper-item
wx:for="{{goodsObj.pics}}"
wx:key="pics_id"
bindtap="handlePrevewImage"
data-url="{{item.pics_mid}}">
<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-icon_shoucang_mian':'icon-icon_shoucang_xian'}} "></text>
<view class="collect_text">收藏</view>
</view>
</view>
<view class="goods_info">
<view class="goods_info_title">图文详情</view>
<view class="goods_info_content">
<!-- 富文本 -->
<!-- {{goodsObj.goods_introduce}} -->
<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 url="/pages/cart/index" open-type="switchTab" class="tool_item">
<view class="iconfont icon-gouwuche"></view>
<view>购物车</view>
</navigator>
<view class="tool_item btn_cart " bindtap="handleCartAdd">
加入购物车
</view>
<view class="tool_item btn_buy">
立即购买
</view>
</view>
#index.less
page{
padding-bottom: 110rpx;
}
.detail_swiper{
swiper{
height: 100vw;
text-align: center;
image{
width: 60%;
}
}
}
.goods_price{
padding:15rpx;
font-size: 32rpx;
font-weight: 600;
color: var(--themeColor);
}
.goods_name_row{
border-top: 5rpx solid #dedede;
border-bottom: 5rpx solid #dedede;
padding: 10rpx 0 ;
display:flex;
.goods_name{
flex: 5;
color: black;
font-size: 30rpx;
padding: 0 10rpx;
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.goods_collect{
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-left: 1rpx solid #000;
.iconfont{}
.icon-icon_shoucang_mian{
color: orangered;
}
.collect_text{}
}
}
.goods_info{
.goods_info_title{
font-size: 32rpx;
color: var(--themeColor);
font-weight: 600;
padding: 20rpx;
}
.goods_info_content{}
}
.btm_tool{
border-top: 1px solid #ccc;
position: fixed;
left: 0;
bottom: 0;
width: 100%;
height: 110rpx;
background-color: #fff;
display: flex;
font-size: 24rpx;
.tool_item{
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 24rpx;
position: relative;
button{
position: absolute;
top:0;
left: 0;
width: 0;
height: 0;
opacity: 0;
}
}
.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;
}
.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;
}
}
9.购物车(cart)
#js
/*
1 获取用户的收货地址
1 绑定点击事件
2 调用小程序内置 api 获取用户的收货地址 wx.chooseAddress
2 获取 用户 对小程序 所授予 获取地址的权限状态 scope
1 假设 用户点击获取收货地址的提示框 确定
scope值 true 直接调用 获取收货地址
2 假设 用户 从来没有调用过收货地址的api
scope undefined 直接调用 获取收获地址
3 假设用户 点击 获取收货地址的提示框 取消
scope值 flase
1 诱导用户 自己打开 授权设置·页面(wx.openSetting) 当用户重新给予获取 地址权限的时候
2 获取收货地址
4 把获取到的收货地址存入到本子存储中
2 页面加载完毕
0 onLoad onShow
1 获取本地存储中的地址数据
2 把数据设置给data中的一个变量
3 onShow
0 回到了商品详情页面第一次添加商品的时候 手动添加商品
1 num=1
2 checked=true
1 获取缓存中的购物车数组
2 把购物车数据 填充到data中
4 全选的实现 数据的展示
1 onshow 获取缓存中的购物车数组
2 根据购物车中的商品数据 所有的商品都被选中 checked=true 全选则被选中
5 总价格和总数量
1 都需要商品被选中 我们才拿它来计算
2 获取购物车数组
3 遍历
4 判断商品是否被选中1
5 总价格+=商品的单价 * 商品的数量
6 总数量 +=商品的数量
7 把计算后的价格和数量设置回data中即可
6 商品的选中
1 绑定change事件
2 获取到被修改的商品对象
3 商品对象的选中状态
4 重现填充回data中和缓存中
5 重现计算全选 总价格 总数量
7 全选和反选
1 全选复选框绑定事件 change
2 获取data中的全选变量 allChecked
3 直接取反 allChecked=!allChecked
4 遍历购物车数组 让里面商品 选中状态跟随 allChecked 改变而改变
5 把购物车数组 和allChecked 重新设置回data 把购物车重新设置回缓存中
8 商品的数量的编辑
1 “+” “-” 按钮 绑定同一个点击事件 区分的关键 自定义属性
1 “+” “-”
2 “-” “-1”
2 传递被点击的商品id goods_id
3 获取data中的购物车数组 来获取需要被修改的商品对象
4 直接修改商品对象的数量 num
当购物车数量=1 同时数量点击 “-” 弹窗提示 询问用户 是否删除
1 确定 直接执行删除
2 取消 什么都不做
5 把cart数组 重新设置回缓存中 和data中 this.setCart ;
9 点击结算
1 判断有没有收到收货地址信息
2 判单用户有没有选购商品
3 经过以上的验证 跳转到支付页面
*/
import { getSetting,chooseAddress,openSetting,showModal,showToast } from"../../utils/asyncWx.js";
import regeneratorRuntime from '../../lib/runtime/runtime';
Page({
data:{
address:{},
cart:[],
allChecked:false,
totalPrice:0,
totalNum:0
},
onShow(){
// 1 获取缓存中的收货地址信息
const address=wx.getStorageSync('address');
// 1 获取缓存中的购物车数据
const cart=wx.getStorageSync('cart')|| [];
this.setData({address});
this.setCart(cart);
},
// 点击收货地址
async handleChooseAddress(){
try{
// 1 获取 权限状态
const res1 = await getSetting();
const scopeAddress = res1.authSetting["scope.address"];
// 2 判断权限状态
if(scopeAddress ===false){
await openSetting();
}
// 4 调用获取收货地址的api
const address =await chooseAddress();
// 5 存入到缓存中
wx.setStorageSync('address', address);
}
catch(error){
console.log(error);
}
},
// 商品的选中
handleItemChange(e){
// 1 获取被修改的商品的id
const goods_id=e.currentTarget.dataset.id;
// 2 获取购物车数组
let {cart}=this.data;
// 3 找到被修改的商品对象
let index=cart.findIndex(v=>v.goods_id);
// 4 选中状态取反
cart[index].checked=!cart[index].checked;
this.setCart(cart);
},
// 设置购物车状态的同时 重新计算 底部工具栏的数据 全选 总价格 购买的数量
setCart(cart){
let allChecked=true;
// 1 总价格 总数量
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,
allChecked
});
wx.setStorageSync('cart', cart);
},
//商品全选功能
handleItemAllCheck(){
// 1 获取data中的数据
let {cart,allChecked}=this.data;
// 2 修改值
allChecked=!allChecked;
// 3 循环修改cart数组 中的商品选中状态
cart.forEach(v=>v.checked=allChecked);
// 4 把修改后的值 填充回data或者缓存中
this.setCart(cart);
},
// 商品数量编辑
async handleItemNumEdit(e){
// 1 获取传递过来的参数
const{operation,id}=e.currentTarget.dataset;
// 2 获取购物车数组
let{cart}=this.data;
// 3 找到需要修改的商品的索引
const index=cart.findIndex(v=>v.goods_id===id);
// 判断是否要执行删除
if(cart[index].num===1&&operation===-1){
// 4.1 弹窗提示
const res=await showModal({content:"是否删除"});
if (res.confirm) {
cart.splice(index,1);
this.setCart(cart);
}
}else{
// 4 进行修改数量
cart[index].num+=operation;
// 5 设置回缓存和data中
this.setCart(cart);
}
},
async handlePay(){
// 1 判断收货地址
const{address,totalNum}=this.data;
if(!address.userName){
await showToast({title:"您还没有选择收货地址"});
return;
}
// 2 判断用户有没有选中商品
if(totalNum===0){
await showToast({title:"您未选中商品"});
return;
}
// 3 跳转到支付页面
wx.navigateTo({
url: '/pages/pay/index',
})
}
})
#json
{
"usingComponents": {},
"navigationBarTitleText": "购物车"
}
#.wxml
<!-- 收货地址 -->
<view class="revice_address_row">
<!-- 当收货地址 不存在 按钮显示 对象 空对象 bool类型也是true -->
<view class="address_btn" wx:if="{{!address.userName}}" >
<button bindtap="handleChooseAddress" 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>
<view class="user_phone">{{address.telNumber}}</view>
</view>
</view>
<!-- 购物车内容 -->
<view class="cart_content">
<view class="cart_title">购物车</view>
<view class="cart_main">
<!-- 当cart数组 长度不为0 显示商品信息 -->
<block wx:if="{{cart.length!==0}}" >
<view class="cart_item"
wx:for="{{cart}}"
wx:key="goods_id"
>
<!-- 复选框 -->
<view class="cart_chk_wrap">
<checkbox-group data-id="{{item.goods_id}}" bindchange="handleItemChange">
<checkbox checked="{{item.checked}}" ></checkbox>
</checkbox-group>
</view>
<!-- 商品图片 -->
<navigator class="cart_image_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-id="{{item.goods_id}}" data-operation="{{-1}}" class="num_edit">-</view>
<view class="goods_num">{{item.num}}</view>
<view bindtap="handleItemNumEdit" data-id="{{item.goods_id}}" data-operation="{{1}}" class="num_edit">+</view>
</view>
</view>
</view>
</view>
</block>
<block wx:else>
<image mode="widthFix" src="https://img0.baidu.com/it/u=2567860002,4149073386&fm=253&fmt=auto&app=138&f=PNG?w=500&h=500"></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>
#less
page{
padding-bottom: 90rpx;
}
.revice_address_row{
.address_btn{
padding: 20rpx;
button{
width: 60%;
}
}
.user_info_row{
display: flex;
.user_info{
flex: 5;
}
.user_phone{
flex: 3;
}
}
}
.cart_content {
.cart_title {
padding: 20rpx;
font-size: 36rpx;
color: var(--themeColor);
border-top: 1rpx solid currentColor;
border-bottom: 1rpx solid currentColor;
}
.cart_main {
.cart_item {
display: flex;
padding: 10rpx;
border-bottom: 1px solid #ccc
.cart_chk_wrap {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
checkbox-group {
checkbox {
}
}
}
.cart_image_wrap {
flex: 2;
display: flex;
justify-content: center;
align-items: center;
image {
width: 90%;
}
}
.cart_info_wrap {
flex: 4;
display: flex;
flex-direction: column;
justify-content: space-around;
.goods_name {
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
color: #666;
}
.goods_price_wrap {
display: flex;
justify-content: space-between;
.goods_price {
font-size: 36rpx;
color: rgba(248, 11, 11, 0.945);
}
.cart_num_tool {
display: flex;
.num_edit {
width: 55rpx;
height: 55rpx;
display: flex;
justify-content: center;
align-items: center;
border: 1rpx solid #666;
}
.goods_num {
width: 55rpx;
height: 55rpx;
display: flex;
justify-content: center;
align-items: center;
}
}
}
}
}
}
}
.footer_tool {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 90rpx;
background-color: #fff;
display: flex;
.all_chk_wrap {
flex: 2;
display: flex;
justify-content: center;
align-items: center;
}
.total_price_wrap {
flex: 5;
padding-right: 15rpx;
text-align: right;
.total_price {
.total_price_text {
color: red;
font-size: 32rpx;
font-weight: 600;
}
}
.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;
}
}
#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;
}
.revice_address_row .user_info_row .user_info {
flex: 5;
}
.revice_address_row .user_info_row .user_phone {
flex: 3;
}
.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: 1px solid #ccc;
}
.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_image_wrap {
flex: 2;
display: flex;
justify-content: center;
align-items: center;
}
.cart_content .cart_main .cart_item .cart_image_wrap image {
width: 90%;
}
.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 {
font-size: 36rpx;
color: rgba(248, 11, 11, 0.945);
}
.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;
align-items: center;
border: 1rpx solid #666;
}
.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;
align-items: center;
}
.footer_tool{
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 90rpx;
background-color: #fff;
display: flex;
border-top: 1px 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 .total_price_text {
color: red;
font-size: 35rpx;
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;
}
10.商品收藏collect
#.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
});
},
handleTabsItemChange(e){
// 1 获取被点击的标题索引
const {index}=e.detail;
// 2 修改源数组
let{tabs}=this.data;
tabs.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
// 3 赋值到data中
this.setData({
tabs
})
}
})
#.json
{
"usingComponents": {
"Tabs":"../../components/Tabs/Tabs"
},
"navigationBarTitleText": "商品收藏"
}
#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/goos_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://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.51yuansu.com%2Fpic3%2Fcover%2F00%2F86%2F05%2F58dac1dd00b60_610.jpg&refer=http%3A%2F%2Fpic.51yuansu.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1646973396&t=5e4d0a94808567e7e968e4a2bb755c17'}}"></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>
#less
page{
color: #000000;
}
.collect_main{
background-color: #f3f4f6;
.collect_title{
padding: 30rpx;
color: #000000;
.collect_tips{
padding: 15rpx;
border: 1rpx solid #ccc;
margin-left: 25rpx;
background-color: #fff;
}
.active{
color: var(--themeColor);
border-color: currentColor;
}
}
.collect_content{
.goods_item{
display: flex;
border-bottom: 5rpx solid #ccc ;
background-color: #fff;
.goods_img_wrap{
flex:2;
display: flex;
justify-content: center;
align-items: center;
image{
width: 70%;
}
}
.goods_info_wrap{
flex: 3;
display: flex;
flex-direction: column;
justify-content: space-around;
.goods_name{
display: -webkit-box;
overflow: hidden;
-webkit-backdrop-filter: vertical;
-webkit-line-clamp: 2;
}
.goods_price{
color:var(--themColor);
font-size: 32rpx;
}
}
}
}
}
#wxss
page {
color: #000000;
}
.collect_main {
background-color: #f3f4f6;
}
.collect_main .collect_title {
padding: 30rpx;
color: #000000;
}
.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: 5rpx 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-backdrop-filter: vertical;
-webkit-line-clamp: 2;
}
.collect_main .collect_content .goods_item .goods_info_wrap .goods_price {
color: var(--themColor);
font-size: 32rpx;
}
11.订单查询order
#.js
/*
1 页面被打开的时候 onshow
0 onshow 不同于onload 无法在形参上接受 options参数
onshow 判断缓存中有没有token
1 没有 直接跳转授权页面
2 有 直接往下进行
1 获取url上的参数type
2 根据type 去发送请求获取订单数据
3 渲染页面
2 点击不同的标题 重新发送请求来获取和渲染数据
*/
import { request} from "../../request/index.js";
import regeneratorRuntime from '../../lib/runtime/runtime';
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;
}
// 1 获取当前的小程序的页面栈-数组 长度最大是10页面
let pages=getCurrentPages();
// 2 数组中 索引最大的页面就是当前页面
let currentPages=pages[pages.length-1];
// 3 获取url上的type参数
const {type}=currentPages.options;
// 4 激活选中页面标题 当type=1 index=0
this.changeTitleByIndex(type-1);
this.getOrders(type);
},
// 获取订单列表的方法
async getOrders(type){
const data=await request({url:"/my/orders/all",data:{type}});
const message=data.data
const res=message.message
console.log(res);
this.setData({
orders:res.orders.map(v=>({...v,create_time_cn:(new Date(v.create_time*1000).toLocaleString())}))
})
},
// 根据标题索引来激活选中 标题数组
changeTitleByIndex(index){
// 2 修改源数组
let{tabs}=this.data;
tabs.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
// 3 赋值到data中
this.setData({
tabs
})
},
handleTabsItemChange(e){
// 1 获取被点击的标题索引
const {index}=e.detail;
this.changeTitleByIndex(index);
// 2 重新发送请求 type=1 index=0
this.getOrders(index+1);
},
})
#.json
{
"usingComponents": {
"Tabs":"../../components/Tabs/Tabs"
},
"navigationBarTitleText": "订单查询"
}
#.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>
#.less
.order_main {
.order_item {
padding: 20rpx;
border-bottom: 1px solid #ccc;
.order_no_row {
display: flex;
padding: 10rpx;
justify-content: space-between;
.order_no_text {
}
.order_no_value {
}
}
.order_price_row {
display: flex;
padding: 10rpx;
justify-content: space-between;
.order_price_text {
}
.order_price_value {
}
}
.order_time_row {
display: flex;
padding: 10rpx;
justify-content: space-between;
.order_time_text {
}
.order_time_value {
}
}
}
}
#wxss
.order_main .order_item {
padding: 20rpx;
border-bottom: 1px solid #ccc;
}
.order_main .order_item .order_no_row {
display: flex;
padding: 10rpx;
justify-content: space-between;
}
.order_main .order_item .order_price_row {
display: flex;
padding: 10rpx;
justify-content: space-between;
}
.order_main .order_item .order_time_row {
display: flex;
padding: 10rpx;
justify-content: space-between;
}
12.商品支付(pay)
#js
/*
1 页面加载的时候
1 从缓存中获取购物车数据 渲染到页面中
这些数据 checked=true
2 微信支付
1 哪些人 哪些账号 可以实现微信支付
1 企业账号
2 企业账号的小程序后台中 必须给开发者 添加上白名单
1 一个 appid 可以同时绑定多个开发者
2 这些开发者可以公用这个appid 和他的 开发权限
3 支付按钮
1 先判断缓存中有没有 token
2 没有就跳转到授权页面 进行获取token
3 有token 正常运行
4 创建订单 获取订单编号
5 已经完成了微信支付
6 手动删除缓存中 已基本被选中了的商品
7 删除后的购物车数据
8 再跳转页面
*/
import { getSetting,chooseAddress,openSetting,showModal,showToast,requestPayment } from"../../utils/asyncWx.js";
import regeneratorRuntime from '../../lib/runtime/runtime';
import { } from "../../request/index.js";
Page({
data:{
address:{},
cart:[],
totalPrice:0,
totalNum:0
},
onShow(){
// 1 获取缓存中的收货地址信息
const address=wx.getStorageSync('address');
// 1 获取缓存中的购物车数据
let cart=wx.getStorageSync('cart')|| [];
// 过滤后的购物车数组
cart=cart.filter(v=>v.checked);
this.setData({address});
// 1 总价格 总数量
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(){
try {
// 1 判断缓存中有没有token
const token=wx.getStorageInfoSync("token");
// 2 判断
if(token){
wx.navigateTo({
url: '/pages/auth/index',
});
return;
}
console.log("不存在token");
// 3 创建订单
// 3.1 准备 请求头参数
// const = { Authorizaation: token };
// 3.2 准备 请求体参数
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};
// 4 准备发送请求 创建订单 获取订单编号
const {order_number}=await request({url:"/my/orders/create",method:"POST",data:orderParams,});
// 5 发起 预支付接口
const {pay} = await request({url:"/my/orders/create",method:"POST",data:orderParams,data:{order_number}});
// 6 发起微信支付
await requestPayment(pay);
// 7 查询后台 订单状态
const res=await request({url:"/my/orders/chkOrder",method:"POST",data:orderParams,data:{order_number}});
await showToast({title:"支付成功"})
// 手动删除缓存中 已经支付了的商品
let newCart=wx.getStorageSync("cart");
newCart=newCart.filter(v=>!v.checked);
wx.getStorageSync('cart',newCart);
// 8 支付成功了 跳转到订单页面
wx.navigateTo({
url: '/pages/order/index',
});
} catch (error) {
await showToast({title:"支付成功"})
console.log(error);
}
}
})
#.json
{
"usingComponents": {},
"navigationBarTitleText": "商品支付"
}
#.wxml
<!-- 收货地址 -->
<view class="revice_address_row">
<!-- 当收货地址 存在详细信息就显示 -->
<view class="user_info_row">
<view class="user_info">
<view>{{address.userName}}</view>
<view>{{address.provinceName+address.cityName+address.countyName+address.detailInfo}}</view>
</view>
<view class="user_phone">{{address.telNumber}}</view>
</view>
</view>
<!-- 购物车内容 -->
<view class="cart_content">
<view class="cart_title">购物车</view>
<view class="cart_main">
<!-- 当cart数组 长度不为0 显示商品信息 -->
<view class="cart_item"
wx:for="{{cart}}"
wx:key="goods_id"
>
<!-- 商品图片 -->
<navigator class="cart_image_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 class="goods_num">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_text">{{totalPrice}}</text>
</view>
<view>包含运费</view>
</view>
<!-- 支付 -->
<view class="order_pay_wrap" bindtap="handleOrderPay" >
支付({{totalNum}})
</view>
</view>
#.wxss
page{
padding-bottom: 90rpx;
}
.revice_address_row .user_info_row {
display: flex;
}
.revice_address_row .user_info_row .user_info {
flex: 5;
}
.revice_address_row .user_info_row .user_phone {
flex: 3;
}
.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: 1px solid #ccc;
}
.cart_content .cart_main .cart_item .cart_image_wrap {
flex: 2;
display: flex;
justify-content: center;
align-items: center;
}
.cart_content .cart_main .cart_item .cart_image_wrap image {
width: 90%;
}
.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 {
font-size: 36rpx;
color: rgba(248, 11, 11, 0.945);
}
.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 .goods_num {
width: 55rpx;
height: 55rpx;
display: flex;
justify-content: center;
align-items: center;
}
.footer_tool{
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 90rpx;
background-color: #fff;
display: flex;
border-top: 1px solid #ccc;
}
.footer_tool .total_price_wrap{
flex: 5;
padding-right: 15rpx;
text-align: right;
}
.footer_tool .total_price_wrap .total_price .total_price_text {
color: red;
font-size: 35rpx;
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;
}
13.搜索中心
#.js
/*
1 输入框绑定 值改变事件 inp事件
1 获取到输入框的值
2 合法性判断
3 检验通过 把输入框的值 发送到后台
4 返回的数据打印到页面
*/
import { request} from "../../request/index.js";
import regeneratorRuntime from '../../lib/runtime/runtime';
Page({
data: {
goods:[],
//取消按钮 是否显示
isFocus:false,
// 输入框的值
inpValue:""
},
TimeId:-1,
// 输入框的值改变 就会触发事件
handleInput(e){
// 1 获取输入框的值
const {value}=e.detail;
// 2 检测合法性
if(!value.trim()){
this.setData({
goods:[],
isFocus:false
})
// 值不合法
return;
}
//准备发送请求获取数据
this.setData({
isFocus:true
})
// 3 准备发送请求获取数据
clearTimeout(this.TimeId);
this.TimeId = setTimeout(()=>{
this.qsearch(value);
},1000);
},
// 发送请求获取搜索建议
async qsearch(query){
const data=await request({url:"/goods/search",data:{query}});
const message=data.data
const goods=message.message
const res=goods.goods
console.log(res);
this.setData({
goods:res,
})
},
//点击取消按钮
handleCancel(){
this.setData({
inpValue:"",
isFocus:false,
goods:[]
})
}
})
#.json
{
"usingComponents": {},
"navigationBarTitleText": "搜索中心"
}
#.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/goos_detail/index?goods_id={{item.goods_id}}" class="search_item"
wx:for="{{goods}}"
wx:key="goods_id">
{{item.goods_name}}
</navigator>
</view>
#.less
page{
background-color: #dedede;
padding: 20rpx;
}
.search_row{
height: 75rpx;
display: flex;
input{
background-color: #fff;
flex: 1;
height: 100%;
padding-left: 100rpx;
}
button{
width: 100rpx;
height: 100%;
font-size: 30rpx;
padding: 0;
margin: 0 10rpx;
display: flex;
justify-content: center;
align-items: center;
}
}
.search_content{
margin-top: 30rpx;
.search_item{
background-color: #fff;
font-size: 30rpx;
padding: 15rpx 10rpx;
border-bottom: 1rpx solid #ccc;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
#.wxss
page {
background-color: #dedede;
padding: 20rpx;
}
.search_row {
height: 75rpx;
display: flex;
}
.search_row input {
background-color: #fff;
flex: 1;
height: 100%;
padding-left: 100rpx;
}
.search_row button {
width: 100rpx;
height: 100%;
font-size: 30rpx;
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: 30rpx;
padding: 15rpx 10rpx;
border-bottom: 1rpx solid #ccc;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
14.支付授权(auth)
#.js
import { request} from "../../request/index.js";
import regeneratorRuntime from '../../lib/runtime/runtime';
import { login} from "../../utils/asyncWx.js";
Page({
// 获取用户信息
async handleGetUserInfo(e){
try {
//console.log(e);
// 1 获取用户信息
const {encryptedData,rawData,iv,signature}=e.detail;
// 2 获取小程序 登录成功 后的code值
const {code}=await login();
const loginParams={encryptedData,rawData,iv,signature,code};
// console.log(code);
// 3 发送请求 获取token值
const token = await request ({url:"/users/wxlogin",data:loginParams,method:"post"});
// console.log(res);
// 4 把token 存入缓存中,同时跳转回上一个页面
wx.setStorageSync("token", 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjIzLCJpYXQiOjE1NjQ3MzAwNzksImV4cCI6MTAwMTU2NDczMDA3OH0.YPt-XeLnjV-_1ITaXGY2FhxmCe4NvXuRnRB8OMCfnPo');
wx-wx.navigateBack({
delta: 1
});
} catch (error) {
console.log(error);
}
}
})
#.json
{
"usingComponents": {},
"navigationBarTitleText": "支付授权"
}
#.wxml
<button type="primary" plain open-type="getUserInfo" bindgetuserinfo="handleGetUserInfo">
获取授权
</button>
#.wxss
button{
margin-top: 40rpx;
width: 70%;
}
15.个人中心(user)
#.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})
}
})
#.json
{
"usingComponents": {},
"navigationBarTitleText": "个人中心"
}
#.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">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">
<view class="iconfont icon-dingdan"></view>
<view class="order_name">全部订单</view>
</navigator>
<navigator url="/pages/order/index?type=2">
<view class="iconfont icon-daifukuan"></view>
<view class="order_name">待付款</view>
</navigator>
<navigator url="/pages/order/index?type=3">
<view class="iconfont icon-daishouhuo"></view>
<view class="order_name">待收货</view>
</navigator>
<navigator>
<view class="iconfont icon-tuikuan"></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>1001011</text>
</view>
<navigator url="/pages/feedback/index" class="app_info_item">意见反馈</navigator>
<view class="app_info_item">关于我们</view>
</view>
<!-- 推荐 -->
<view class="recommond_wrap">把应用推荐给其他人 </view>
</view>
</view>
#.less
page{
background-color: rgb(241, 241, 241);
}
.user_info_wrap{
height: 45vh;
overflow: hidden;
background-color: var(--themeColor);
.user_img_wrap{
position: relative;
.user_bg{
height:60vh;
// 高斯模糊
filter: blur(10rpx);
}
.user_info{
position: absolute;
left: 50%;
transform: translate(-50%);
top: 30%;
text-align: center;
}
.user_icon{
width: 200rpx;
height: 200rpx;
border-radius: 50%;
}
.user_name{
color: #fff;
margin-top: 40rpx;
}
}
.user_btn{
position: absolute;
left: 50%;
transform: translateX(-50%);
top:20%;
border: 1rpx solid greenyellow;
color: greenyellow;
font-size: 38rpx;
padding: 30rpx;
border-radius: 10rpx;
}
}
.user_content{
position: relative;
.user_main{
color: #666;
padding: 15rpx;
position: absolute;
width: 90%;
left: 50%;
transform: translateX(-50%);
top:-40rpx;
.history_wrap{
background-color: #fff;
display: flex;
navigator{
flex: 1;
text-align: center;
padding: 10rpx 0;
.his_num{
color: var(--themeColor);
}
.his_name{}
}
}
.orders_wrap{
background-color: #fff;
margin-top: 30rpx;
.orders_title{
padding: 20rpx;
border-bottom: 1rpx solid #ccc;
}
.order_content{
display: flex;
navigator{
padding: 15rpx 0;
flex: 1;
text-align: center;
.iconfont{
color: var(--themeColor);
}
.order_name{}
}
}
}
.address_wrap{
margin-top: 30rpx;
background-color: #fff;
padding: 20rpx;
}
.app_info_wrap{
margin-top: 30rpx;
background-color: #fff;
.app_info_item{
border-bottom: 1rpx solid #000;
padding: 20rpx;
}
.app.info_contact{
display: flex;
justify-content: space-between;
}
}
.recommond_wrap{
margin-top: 30rpx;
background-color: #fff;
padding: 20rpx;
}
}
}
#.wxss
page {
background-color: #f1f1f1;
}
.user_info_wrap {
height: 45vh;
overflow: hidden;
background-color: var(--themeColor);
}
.user_info_wrap .user_img_wrap {
position: relative;
}
.user_info_wrap .user_img_wrap .user_bg {
height: 60vh;
filter: blur(10rpx);
}
.user_info_wrap .user_img_wrap .user_info {
position: absolute;
left: 50%;
transform: translate(-50%);
top: 30%;
text-align: center;
}
.user_info_wrap .user_img_wrap .user_icon {
width: 200rpx;
height: 200rpx;
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: 20%;
border: 1rpx solid greenyellow;
color: greenyellow;
font-size: 38rpx;
padding: 30rpx;
border-radius: 10rpx;
}
.user_content {
position: relative;
}
.user_content .user_main {
color: #666;
padding: 15rpx;
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;
font-size: 30rpx;
}
.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 .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(--themeColor);
}
.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 {
border-bottom: 1rpx solid #000;
padding: 20rpx;
}
.user_content .user_main .app_info_wrap .app.info_contact {
display: flex;
justify-content: space-between;
}
.user_content .user_main .recommond_wrap {
margin-top: 30rpx;
background-color: #fff;
padding: 20rpx;
}
16.登录(login)
#.js
// pages/login/index.js
Page({
handleGetUserInfo(e){
// console.log(e);
const{userInfo}=e.detail;
wx.setStorageSync('userinfo', userInfo)
wx.navigateBack({
delta: 1,
})
}
})
#.json
{
"usingComponents": {},
"navigationBarTitleText": "登录"
}
#.wxml
<button type="primary" plain open-type="getUserInfo" bindgetuserinfo="handleGetUserInfo" > 登录 </button>
#.wxss
button{
margin-top: 40rpx;
width: 70%;
}
17.意见反馈(feedback)
#.js
/*
1 点击“+”触发tap点击事件
1 调小程序内置的 选择图片的api
2 获取到图片的路径数组
3 把图片路径存到data变量中
4 页面就可以根据图片数组进行循环显示 自定义组件
1 点击 自定义图片 组件
1 获取被点击的元素的索引
2 获取data中的图片数组
3 根据索引 数组中删除对应的元素
4 把数组重新设置回data中
3 点击“提交”
1 获取文本域的内容
1 data中定义变量 类似 输入框的获取
2 文本域 绑定输入事件 事件触发的时候 把输入框的值 存入到变量中
2 对这些内容 合法性验证
3 验证通过 用户选择的图片 上传到专门的图片服务器 返回图片外网的链接
1 遍历图片数组
2 挨个上传
3 自己再维护图片数组 存放图片上传后的外网的链接
4 文本域 和外网的图片的路径 一起提交到服务器(前端的模拟 不会发送到后台)
5 清空当前页面
6 返回上一页
*/
Page({
data: {
tabs:[
],
// 被选中的图片路径 数组
chooseImgs:[],
// 文本域的内容
textVal:""
},
// 外网的图片的路径数组
UpLoadImgs:[],
handleTabsItemChange(e){
// 1 获取被点击的标题索引
const {index}=e.detail;
// 2 修改源数组
let{tabs}=this.data;
tabs.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
// 3 赋值到data中
this.setData({
tabs
})
},
// 点击“+”选择图片
handleChooseImg(){
// 2 调用小程序内置的选择图片api
wx.chooseImage({
// 同时选中图片的数量
count: 9,
// 图片的格式 原图 压缩
sizeType:['original', 'compressed'] ,
// 图片的来源 相册 照相机
sourceType:['album', 'camera'],
success:(result)=>{
this.setData({
// 图片数组 进行拼接
chooseImgs:[...this.data.chooseImgs,...result.tempFilePaths]
})
}
})
},
// 点击自定义图片组件
handleRemoveImg(e){
// 2 获取被点击的事件索引
const{index}=e.currentTarget.dataset;
// 3 获取data中的图片数组
let{chooseImgs}=this.data;
// 4 删除元素
chooseImgs.splice(index,1);
this.setData({
chooseImgs
})
},
// 文本域的输入事件
handleTextInput(e) {
this.setData({
textVal: e.detail.value
})
},
// 提交按钮的点击
handleFormSumbit(){
// 1 获取文本域的内容
const{textVal,chooseImgs}=this.data;
// 2 合法性的验证
if(!textVal.trim()){
// 不合法
wx.showToast({
title: '输入不合法',
icon:"none",
mask:true
});
return;
}
// 3 准备上传图片到专门的服务器
// 上传文件的api 不支持 多个文件同时上传 (可以遍历数组 一个一个上传)
// 显示正在等待的图片
wx.showLoading({
title: '正在上传中',
mask:true
})
// 判断有没有需要上传的数组
if(chooseImgs.length!=0){
chooseImgs.forEach((v,i)=>{
wx.uploadFile({
// 图片要上传到哪里
url: 'https://media.mogu.com/image/scale?appKey=15m&w=500&h=500&quality=100',
// 被上传的文件的路径
filePath: v,
// 上传的文件的名称 后台来获取文件 file
name: "image",
// 顺带的文本信息
formData: {},
success: (result) => {
console.log(result);
let url=JSON.parse (result.data);
this.UpLoadImgs.push(url);
// 所有的图片都上传完毕了才触发
if(i==chooseImgs.length-1){
//
console.log("把文本内容和外网图片数组提交到后台");
// 页面提交成功
// 重置页面
this.setData({
textVal:"",
chooseImage:[]
})
//返回上一个页面
wx.navigateBack({
delta: 1,
})
}
}
});
})
}else{
console.log("只是提交了文本");
wx.navigateBack({
delta: 1,
})
wx.showToast({
title: '提交成功!', // 标题
icon: 'success', // 图标类型,默认success
duration: 1000 // 提示窗停留时间,默认1500ms
})
}
}
})
#.json
{
"usingComponents": {
"Tabs":"../../components/Tabs/Tabs",
"UpImg":"../../components/UpImg/UpImg"
},
"navigationBarTitleText": "意见反馈"
}
#.wxml
<Tabs tabs="{{tabs}}" bindtabsItemChange="handleTabsItemChange" style="background-color: white;" >
<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="handleChooseImg">+</button>
<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="handleFormSumbit" type="warn">
<icon type="success_no_circle" color="white" size="15" ></icon>
提交
</button>
</view>
</view>
</Tabs>
#.less
page{
background-color: #dedede;
}
.fb_main {
padding: 20rpx;
color: #666;
.fb_title {
}
.fb_tips {
display: flex;
flex-wrap: wrap;
text {
width: 30%;
padding: 10rpx;
text-align: center;
font-size: 30rpx;
background-color: #fff;
margin: 10rpx;
}
}
.fb_content {
background-color: #fff;
padding: 10rpx;
margin-top: 40rpx;
textarea{
padding: 10rpx;
}
.fb_tool{
display: flex;
flex-wrap: wrap;
padding-bottom: 10rpx;
button{
margin: 0;
width: 120rpx;
height: 120rpx;
font-size: 80rpx;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
margin-left: 20rpx;
color: #ccc;
}
.up_img_item{
margin-top: 20rpx;
margin-left: 20rpx;
}
}
}
.form_btn_wrap {
margin-top: 20rpx;
display: flex;
justify-content: flex-end;
button{
margin: 0;
width: 30%;
}
}
}
#wxss
page {
background-color: #dedede;
}
.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;
font-size: 30rpx;
background-color: #fff;
margin: 10rpx;
}
.fb_main .fb_content {
background-color: #fff;
padding: 10rpx;
margin-top: 40rpx;
}
.fb_main .fb_content textarea {
padding: 10rpx;
}
.fb_main .fb_content .fb_tool {
display: flex;
flex-wrap: wrap;
padding-bottom: 10rpx;
}
.fb_main .fb_content .fb_tool button {
margin: 0;
width: 120rpx;
height: 120rpx;
font-size: 80rpx;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
margin-left: 20rpx;
color: #ccc;
}
.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 {
margin: 0;
width: 30%;
}
18.request
#.js
// 同步发送异步代码的次数
let ajaxTimes=0;
export const request=(params)=>{
// 判断 url中是否带有/my/ 请求的是私有路径 带上header token
let header={ ...params,header};
if(params.url.includes("/my/")){
// 拼接header 带上 token
header["Authorization"]=wx.getStorageSync('token');
}
ajaxTimes++;
//显示加载中 效果
wx.showLoading({
title: '加载中',
mask:true
});
//定义公共的url
const baseUrl="https://api-hmugo-web.itheima.net/api/public/v1"
return new Promise((resolve,reject)=>{
wx.request({
...params,
header:header,
url:baseUrl+params.url,
success:(result)=>{
resolve(result);
},
fail:(err)=>{
reject(err);
},
complete:()=>{
ajaxTimes--;
if(ajaxTimes===0){
//关闭正在等待的图标
wx.hideLoading();
}
}
});
})
}