优选商城
1.当我们想把微信小程序的优选商城这个项目做出来的时候,我们先配置好所有的路由,(个人习惯),我们也可以一边写一边配置需要的路由,我们在app.json里面配置
2.谁的路由在最上面,系统会默认先加载谁 ,我们也需要先配置好tabBar底部,路由跳转, 下面是代码和效果
"tabBar": {
"list": [{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "/assets/icon/首页.png",
"selectedIconPath": "assets/icon/首页1.png"
},
{
"pagePath": "pages/category/category",
"text": "商品",
"iconPath": "/assets/icon/商品.png",
"selectedIconPath": "assets/icon/商品1.png"
},
{
"pagePath": "pages/home/home",
"text": "购物车",
"iconPath": "/assets/icon/购物车.png",
"selectedIconPath": "assets/icon/购物车1.png"
},
{
"pagePath": "pages/my/my",
"text": "我的",
"iconPath": "/assets/icon/我的.png",
"selectedIconPath": "./assets/icon/我的1.png"
}
]
},
下面我们来介绍这个微信小程序的项目
###首页
1:我们先看一下效果图
我们的头部搜索框,是组件拆分,有利于我们后期的开发,也方便后期的维护,做的是一个假搜索框 <navigator url="/pages/search/search">搜索</navigator>利用的这个标签,
当我们点击搜索的时候,跳转到搜索页面,这是跳转到搜索页面的
2:我们在来看首页的导航部分
我们这4个图片还有图片下面的文字,是根据接口渲染出来的,然后再做好布局排列位置,效果就出来了,
<navigator wx:for="{{wjsdh}}" wx:key='index' url="/pages/category/category" open-type="switchTab" class="tpq"> <image class="img" src="{{item.image_src}}"></image>
</navigator>
当我们点击导航的时候,会跳转到商品分类页面,我们也是利用上一个标签在套的url='路径'
点击后的样子,就跳转到商品分类了,点击这4个都是可以跳转到商品分类页面的
3:接下来我们看首页的楼层部分
效果图:
我们这里是分为3块的,也就是3个楼层这些数据也是我们通过后台接口获取到的,然后我们通过wx:for="{{}}"和wx:key=""循环出来的
<view class="index_floor" wx:for="{{lc}}" wx:key='index'>
<!-- 标题 -->
<view class="bt">
<image class="wjs-img" src="{{item.floor_title.image_src}}"></image>
</view>
<!-- 标题 -->
<!-- 内容 -->
<view class="nr">
<image class="imgmm" wx:for="{{item.product_list}}" wx:key="index" src="{{item.image_src}}"></image>
</view>
<!-- 内容 -->
</view>
我们在通过css样式进行详细的调整就可以了,这样我们的首页的功能就简单的完成了
优选商城的(商品分类)
我们先看一下效果
这个头部是我们之前组件拆分的,然后再商品分类里面直接引用就可以了,
关键的是接下来看的部分,大家看这个灰色的滚动条,我们滑动商品的时候,这个滚动条就出现了,然而页面不会跟着往下滚动这里我们用的是微信小程序提供给我们的一个组件<scroll-view scroll-y="true" class="left_menu">内容</scroll-view>这样我们就实现这个滚动条了,当然我们右侧分类出来的数据也是用的这个标签,效果和左侧分类标签一样
<scroll-view scroll-y="true" class="left_menu">
<view wx:for="{{you}}" wx:key='index' class="gjb {{index === cat_id ? 'active':''}}" bindtap='bindtawp'
data-index="{{index}}">
{{item.cat_name}}
</view>
</scroll-view>
<!-- you侧菜单 -->
<scroll-view class="right" scroll-y="true" scroll-top="{{scrollTop}}">
<view wx:for="{{you[cat_id].children}}" wx:key="index">
<view>
<view style="text-align:center">/{{item.cat_name}}/</view>
<view class="qqq">
<view wx:for="{{item.children}}" wx:key="index" class="www">
<navigator class="eee" url="/pages/goods_list/goods_list?cat_id={{item.cat_id}}">
<image style="width:50%;height:70rpx" src="{{item.cat_icon}}"></image>
<view>{{item.cat_name}}</view>
</navigator>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 右侧菜单 -->
js部分
/**
* 页面的初始数据
*/
data: {
leftlsit: [],
rightlist: [], // 右侧
// 左侧点击事件
// 距离顶部距离
scrollTop: 0,
cat_id: 0,
},
// ji接口返回的数据
cates: [],
onLoad: function (options) {
app.http.cd().then(res => {
let {
data: {
message
}
} = res
console.log(res)
this.setData({
cd: message
})
}),
app.http.you().then(res => {
let {
data: {
message
}
} = res
this.setData({
you: message
})
})
},
// 左侧菜单点击事件
bindtawp(e) {
const index = e.currentTarget.dataset.index
this.setData({
cat_id: index,
scrollTop:0
})
},
附加:
我们在原有基础目录上创建一个文件夹(http),然后里面在创建3个js文件 然后我们在全局的app.js对底部编写 ,进行全局的
// app.js
var http = require ('./http/http')
App({
onLaunch() {
// 展示本地存储能力
const logs = wx.getStorageSync('logs') || []
logs.unshift(Date.now())
wx.setStorageSync('logs', logs)
// 登录
wx.login({
success: res => {
// 发送 res.code 到后台换取 openId, sessionKey, unionId
}
})
},
globalData: {
userInfo: null
},
// 全局请求
http
})
api.js文件中编写后面的路由的后面的地址
module.exports={
home:'/home/swiperdata',
dh:'/home/catitems',//导航
lc:'/home/floordata',// 楼层
cd:'/categories',// 商品页面
you:'/categories',
cates:'/categories',
// 购物车 收货地址
dz:'/wx.chooseAddress',
// 商品列表
sp:'/goods/search',
// 详情页面轮播图
picture:'/goods/detail',
}
fetch.js中编写,网络封装的对象
// 封装网络请求
module.exports=(url,data,module)=>{
let p = new Promise((resolve,reject)=>{
wx.request({
url: url,
data:data,
module:module,
success(res){
resolve(res)
},
fail(err){
reject(err)
}
})
})
exports=(url,data,module)=>{
return new Promise((resolve,reject)=>{
wx.request({
url: url,
data:data,
module:module,
success(res){
resolve(res)
},
fail(err){
reject(err)
}
})
})
}
return p
}
http.js文件中编写,基地址
let p = require('./fetch')
const api = require('./api')
let baseUrl = 'https://api-hmugo-web.itheima.net/api/public/v1'
function banner() {
return p(baseUrl + api.home, {}, 'get')
}
// 导航
function dh(){
return p(baseUrl+api.dh,{},'get')
}
function lc(){
return p (baseUrl+api.lc,{},'get')
}
// 商品页面 左侧菜单
function cd(){
return p (baseUrl+api.cd,{},'get')
}
function you(){
return p (baseUrl+api.you,{},'get')
}
function cates(){
return p (baseUrl+api.cates,{},'get')
}
// 收货地址
function dz(){
return p (baseUrl+api.dz,{},'get')
}
// 商品列表页面
function sp(){
return p (baseUrl+api.sp,{},'get')
}
// 详情页面的轮播图
function picture(){
return p (baseUrl+api.picture,{},'get')
}
module.exports = {
banner,
dh,
// 首页楼层
lc,
// 商品页面菜单
cd,
you,
cates,
// 收货地址
dz,
// 商品列表
sp,
// 详情页面的轮播图
picture
}
当我们点击后分类出来的商品后,我们可以点击商品进入(商品列表页面),商品点击后的样子,我们通过
<navigator class="eee" url="/pages/goods_list/goods_list?cat_id={{item.cat_id}}">
<image style="width:50%;height:70rpx" src="{{item.cat_icon}}"></image>
<view>{{item.cat_name}}</view>
</navigator>
然后我们就进入商品列表页面了
上面的分类页面就简单的讲述了一下
###购物车页面
当我们没有收货地址和商品的时候是下面这样子的
我们通过wx:if=""和wx:else来判断收货地址是否存在
<view class="box">
<!-- 当收货地址不存在时候 -->
<view class="wjs_list" wx:if="{{!address.userName}}">
<button class="btn" bindtap="handleChooseAddress">
添加收货地址
</button>
</view>
<!-- 当存在的时候 -->
<view class="wjs_no" wx:else>
<view class="nr">
<view>{{address.userName}}</view>
<view>{{address.all}}</view>
</view>
<view>
{{address.telNumber}}
</view>
</view>
</view>
下面这个小红人和购物车我们也是通过wx:if=""和wx:else来判断的
<block wx:if="{{cart.length !== 0}}">
<view class="gwc_title">购物车</view>
<!-- 列表 -->
<view class="gwc_box" wx:for="{{cart}}" wx:key='index'>
<view class="gwc_list">
<view class="left">
<checkbox-group data-id="{{item.goods_id}} " bindchange="handeItemChange" >
<checkbox checked="{{item.checked}}"></checkbox>
</checkbox-group>
</view>
<view class="zhong">
<image src="{{item.goods_small_logo}}"></image>
</view>
<view class="right">
<view>{{item.goods_name}}</view>
<view class="jg">
<view class="xia">¥{{item.goods_price}}</view>
</view>
<!-- 计步器 -->
<view class="bu" >
<view class="jian" bindtap="jian" data-id="{{item.goods_id}}" data-operation="{{-1}}">-</view>
<view>{{item.num}}</view>
<view class="jia" bindtap="jian" data-id="{{item.goods_id}}" data-operation="{{1}}">+</view>
</view>
</view>
</view>
</view>
</block>
<block wx:else>
<image mode="widthFix" src="http://hbimg.b0.upaiyun.com/e1b1467beea0a9c7d6a56b32bac6d7e5dcd914f7c3e6-YTwUd6_fw658">
</image>
</block>
js逻辑
import {
getSetting,
chooseAddress,
openSetting,
showModal
} from '../.././utils/asyncWx'
Page({
data() {
address: {}
cart: []
allchecked: false
// 初始价格
totalPrice: 0
// 数量
totalNum: 0
},
onShow() { //页面加载中
// 获取缓存中的收货地址信息
const address = wx.getStorageSync('address')
// 1获取缓存中的数据
const cart = wx.getStorageSync('cart') || [];
this.setData({
address
});
this.setCart(cart);
// console.log(cart)
// 1计算全选
// every 数组方法 会遍历 会接收一个回调函数 必须要确保每一个函数都返回true 他的返回值就是true 只要有一函数返回false 就不会在执行循环,直接返回false v是每一个循环项
// 空数组调用 every 返回值就是true 当cart长度不为0 在调用cart.every(v=>v.checked) 否则直接返回false
// const allchecked = cart.length ? cart.every(v => v.checked) : false;
// let allchecked = true
// // 1总价格 2总数量
// 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
// // 给data赋值
// this.setData({
// address,
// cart,
// allchecked,
// totalPrice, // 价格
// totalNum, // 数量 然后把这个写在页面上去
// })
},
// 点击收货地址
async handleChooseAddress() {
try {
// 1 获取 权限状态
const res1 = await getSetting();
const scopeAddress = res1.authSetting["scope.address"];
// 2 判断 权限状态
if (scopeAddress === false) {
await openSetting();
}
// 4 调用获取收货地址的 api
let address = await chooseAddress();
address.all = address.provinceName + address.cityName + address.countyName + address.detailInfo;
// 5 存入到缓存中
wx.setStorageSync("address", address);
} catch (error) {
console.log(error);
}
},
//商品的选中
handeItemChange(e) {
// 获取被修改的商品的id
const goods_id = e.currentTarget.dataset.id;
console.log(goods_id)
// 获取购物车的数组
let {
cart
} = this.data
console.log(cart)
// 3找到被修改的商品对象
let index = cart.findIndex(v => v.goods_id == goods_id);
// 4选中状态取反
console.log(index)
cart[index].checked = !cart[index].checked;
// 优化后
this.setData(cart)
//5 6 把购物车数组重新设置会data中和缓存中
// this.setData({
// cart
// })
// wx.setStorageSync('cart', cart)
// let allchecked = true
// // 1总价格 2总数量
// 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
// })
},
// 重新计算 底部工具栏的数据 全选,总价格 购买的数量 优化的代码
setCart(cart) {
// this.setData({
// cart
// })
console.log(cart)
let allchecked = true
// 1总价格 2总数量
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() {
// 获取购物车中的数据
let {
cart,
allchecked
} = this.data
// 修改值
allchecked = !allchecked;
// 循环修改cart的状态
cart.forEach(v => v.checked = allchecked);
// 4修改后的值,都填充会data中或者缓存中
this.setCart(cart)
},
async jian(e) {
// 获取传递过来的参数
const {
operation,
id
} = e.currentTarget.dataset
// 获取购物车数组
let {
cart
} = this.data;
// 3找到需要修改的商品索引值
const index = cart.findIndex(v => v.goods_id === id)
// 4判断是否执行删除
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);
}
},
// 结算
js(e) {
// 判断收货地址
const {
address,
totalNum
} = this.data
if (!address.userName) {
wx.showToast({
title: '你还没填写收货地址'
})
return
}
if (totalNum === 0) {
wx.showToast({
title: '你还没选择商品'
})
return
}
// 跳转到支付页面
wx.navigateTo({
url: '/pages/pay/pay'
})
}
})
在原有的基础目录上创建一个文件夹,在里面封装方法,简化我们代码,
/**
* promise 形式 getSetting
*/
export const getSetting=()=>{
return new Promise((resolve,reject)=>{
wx.getSetting({
success: (result) => {
resolve(result);
},
fail: (err) => {
reject(err);
}
});
})
}
/**
* promise 形式 chooseAddress
*/
export const chooseAddress=()=>{
return new Promise((resolve,reject)=>{
wx.chooseAddress({
success: (result) => {
resolve(result);
},
fail: (err) => {
reject(err);
}
});
})
}
/**
* promise 形式 openSetting
*/
export const openSetting=()=>{
return new Promise((resolve,reject)=>{
wx.openSetting({
success: (result) => {
resolve(result);
},
fail: (err) => {
reject(err);
}
});
})
}
/**
* promise 形式 showModal
* @param {object} param0 参数
*/
export const showModal=({content})=>{
return new Promise((resolve,reject)=>{
wx.showModal({
title: '提示',
content: content,
success :(res) =>{
resolve(res);
},
fail:(err)=>{
reject(err);
}
})
})
}
// 支付
export const showToast=({title})=>{
return new Promise((resolve,reject)=>{
wx.showToast({
title: title,
icon: 'none',
success :(res) =>{
resolve(res);
},
fail:(err)=>{
reject(err);
}
})
})
}
/**
* promise 形式 login
*/
export const login=()=>{
return new Promise((resolve,reject)=>{
wx.login({
timeout:10000,
success: (result) => {
resolve(result);
},
fail: (err) => {
reject(err);
}
});
})
}
效果图
我的页面
上面这是我们没有登录时状态
<view class="box">
<!-- 以登录 -->
<view class="box_yes" wx:if="{{userInfo.avatarUrl}}">
<!-- 模糊背景 -->
<image src="{{userInfo.avatarUrl}}"></image>
<!-- 头像 -->
<view class="toux">
<image class="image" src="{{userInfo.avatarUrl}}"></image>
<view>{{userInfo.nickName}}</view>
</view>
</view>
<!-- 未登录 -->
<view class="box_no" wx:else>
<navigator class="zong" url="/pages/login/login">
登录
</navigator>
</view>
</view>
js逻辑
// pages/my/my.js
Page({
/**
* 页面的初始数据
*/
data: {
userinfo: {}, //这个是缓存中的个人信息
collectNums:0// 收藏的数量
},
onShow1() {
const userInfo = wx.getStorageSync("userInfo");
const wjs = wx.getStorageSync('collect') || []
console.log(wjs)
console.log(userInfo)
this.setData({
userInfo,
collectNums:wjs.length
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
this.onShow1()
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
登录后的状态
下面我们完成个人中心的其他的功能
<!-- 下面 -->
<view class="xia">
<!-- 收藏店铺 -->
<view class="dp">
<navigator class="sc">
<view>0</view>
<view>收藏店铺</view>
</navigator>
<navigator class="sc" url="/pages/collect/collect">
<view>{{collectNums}}</view>
<view>收藏商品</view>
</navigator>
<navigator class="sc">
<view>0</view>
<view>关注商品</view>
</navigator>
<navigator class="sc">
<view>0</view>
<view>我的足迹</view>
</navigator>
</view>
<!-- 我的订单 -->
<view class="ding">
<view class="wod">我的订单</view>
<view class="mode">
<view class="tan">
<view class="iconfont icon-dingdan"></view>
<view class="tx">全部订单</view>
</view>
<view class="tan">
<view class="iconfont icon-daifukuan"></view>
<view class="tx">待付款</view>
</view>
<view class="tan">
<view class="iconfont icon-daishouhuo"></view>
<view class="tx"> 待收货</view>
</view>
<view class="tan">
<view class="iconfont icon-tuikuan"></view>
<view class="tx">退款/退货</view>
</view>
</view>
</view>
<!-- 收货地址管理 -->
<view class="guanli">
收货地址管理
</view>
<!-- 联系客服 -->
<view class="kef">
<!-- 联系客服 -->
<view class="md">
<view>联系客服</view>
<view>0324-5579-555</view>
</view>
<navigator class="kui" url="/pages/feedback/feedback">意见反馈</navigator>
<view class="wm">关于我们</view>
</view>
<!-- 推荐 -->
<view class="tuijian">
把应用推荐给其他人
</view>
</view>
最终的效果