附录6-4 黑马优购项目-分类和购物车

news/2024/11/14 13:06:50/

目录

1  分类

1.1  接口

1.2  窗口限制

1.3  选中状态样式判断

1.4  点击左侧时右侧会到顶点

1.5  源码 

2  购物车

2.1  store

2.2  tabBar徽标

2.3  滑动删除

2.4  结算

2.4.1  结算前登录

2.4.2  结算功能

2.5  触发组件事件

2.6  源码


1  分类

分类最上部是搜索区域,与首页的搜索区域相同,这里直接复用

右侧区域的内容 会根据点击不同左侧区域 而变化

左侧区域和右侧区域均可以向下滚动

滚动后切换到别的页面,然后再切换回来,滚动的位置不变(记录位置是scroll-view的默认效果,不用额外进行修改)

1.1  接口

接口中包含三级标题。紫色框子是一级标题,橙色框子是二级标题,绿色框子是三级标题

url是https://api-hmugo-web.itheima.net/api/public/v1/categories 直接发get请求就行了

1.2  窗口限制

分类页面的最外层只有两个部分,一个是搜索框,除了搜索框剩下都属于另外一部分。为了让左侧和右侧都有拖动了效果,所以这里对高度进行了限制

如果取消高度限制,页面中的scroll-view-container就废了,左侧和右侧就会同时进行拖动

限制高度的值根据机型进行调节,wx.getWindowInfo()可以获得当前机型的高度,减50是减去屏幕最下方的tarbar高度。高度在页面初次加载的时候就进行计算

1.3  选中状态样式判断

左侧列表被选中后会有这个红色的部分

每次点击左侧列表会记录left_active_num,讲left_active_num与循环时的index进行比对,如果相同的多给一个active的类名

1.4  点击左侧时右侧会到顶点

每一次点击左侧的时候,右侧的内容是不同的,但实际上元素都是一个。所以点击左侧时,右侧不会默认回到顶点

这里用变量的原因是给0这个常量第二次就懒加载了,只能点击左侧之后每次都赋值一个新的0

1.5  源码 

wxml

<!--pages/category/category.wxml-->
<black_horse_search></black_horse_search>
<view class="scroll-view-container" style="height:{{windowHeight}};">
<!-- <view class="scroll-view-container" style="height:200px;"> --><!-- 左侧滚动区域 --><scroll-view class="left-scroll-view" scroll-y="true"><view  wx:for="{{cateList}}" wx:key="cat_id" class="{{['scroll-view-item',index==left_active_num?'active':'']}}" bindtap="left_scroll_click" data-click_item_index="{{index}}">{{item.cat_name}}</view></scroll-view><!-- 右侧滚动区域 --><!-- 老版本的scroll-top不能给相同的值,也就是你一直给0是不行的 --><scroll-view class="right-scroll-view" scroll-y="true" scroll-top="{{right_scrollTop}}"><view class="cate-lv2" wx:for="{{cateLevel2}}" wx:key="index"><view class="cate-lv2-title">/ {{item.cat_name}} /</view><view class="cate-lv3-list"><view class="cate-lv3-item" wx:for="{{item.children}}" wx:for-index="index3" wx:for-item="item3" wx:key="index3" bindtap="cate_lv3_item_click" data-cat_name="{{item3.cat_name}}" data-cat_id="{{item3.cat_id}}"><image src="{{item3.cat_icon}}" mode="" data-cat_name="{{item3.cat_name}}" data-cat_id="{{item3.cat_id}}"/><text data-cat_name="{{item3.cat_name}}" data-cat_id="{{item3.cat_id}}" >{{item3.cat_name}}</text></view></view></view></scroll-view>
</view>

wxss

/* pages/category/category.wxss */
.scroll-view-container {display: flex;
}.scroll-view-container .left-scroll-view {width: 120px;
}.scroll-view-container .left-scroll-view .scroll-view-item.active {position: relative;background-color: #ffffff;
}.scroll-view-container .left-scroll-view .scroll-view-item.active::before {position:absolute;top:50%;left:0;content:' ';display:block;width: 3px;height:30px;background-color: #C00000;transform: translateY(-50%);
}.scroll-view-container .left-scroll-view .scroll-view-item {background-color: #f7f7f7;line-height: 60px;text-align: center;font-size: 12px;
}.cate-lv2-title {font-size:12px;font-weight:bold;text-align: center;padding: 15px 0;
}.cate-lv3-list {display:flex;flex-wrap:wrap
}.cate-lv3-list .cate-lv3-item {width:33.33%;display:flex;flex-direction:column;justify-content: center;align-items: center;margin-bottom:10px;
}.cate-lv3-list .cate-lv3-item image{width:60px;height:60px;
}.cate-lv3-list .cate-lv3-item text{font-size:12px;
}

js

// pages/category/category.js
import {createStoreBindings} from 'mobx-miniprogram-bindings'
import {store} from '../../store/store.js'const App = getApp()Page({cate_lv3_item_click(e) {wx.navigateTo({url:'/subpackage_goods_list/goods_list?cid=' + e.target.dataset.cat_id + '&&query=' + e.target.dataset.cat_name})},left_scroll_click(e) {const click_item_index = e.target.dataset.click_item_indexthis.setData({left_active_num:click_item_index})this.setData({cateLevel2:this.data.cateList[Number(click_item_index)].children})this.setData({right_scrollTop:0})},get_cateList() {wx.request({url:App.base_url + '/api/public/v1/categories',method:'GET',success:(val) => {// 获取所有分类this.setData({cateList:val.data.message})// 获取二级分类,二级分类的内容在一级分类中有,我们在这里弄好处理,这里给一个初始值,后面在左侧点击事件中还会再改this.setData({cateLevel2:val.data.message[0].children})},fail:() => {console.log('获取左侧失败')wx.showToast({title:'获取左侧失败',icon:'error',duration:2000})}})},/*** 页面的初始数据*/data: {windowHeight:0 + 'px',cateList:[],cateLevel2:[],left_active_num:0,right_scrollTop:0},/*** 生命周期函数--监听页面加载*/onLoad(options) {this.storeBindings = createStoreBindings(this,{store,actions:['set_cart_tabBar_badge']})this.setData({windowHeight:(wx.getWindowInfo().windowHeight-50) + 'px'})},/*** 生命周期函数--监听页面初次渲染完成*/onReady() {// 获取分类列表this.get_cateList()// 设置购物车tabBar的徽标this.set_cart_tabBar_badge()},/*** 生命周期函数--监听页面显示*/onShow() {},/*** 生命周期函数--监听页面隐藏*/onHide() {},/*** 生命周期函数--监听页面卸载*/onUnload() {},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh() {},/*** 页面上拉触底事件的处理函数*/onReachBottom() {},/*** 用户点击右上角分享*/onShareAppMessage() {}
})

2  购物车

购物车中的内容在这个项目中都是放在本地的,在实际开发中一般是放在云端

购物车为空时会显示这个页面

2.1  store

store.js是放全局变量的js文件,创建方法可以参考 18.全局数据共享 mobx-miniprogram与mobx-miniprogram-bindings-CSDN博客

源码

import {action,observable
} from 'mobx-miniprogram'export const store = observable({// 购物车总数的计算属性// 计算购物车一共多少件get cart_shop_total() {let total = 0this.cart_list.forEach(goods => total += goods.goods_count)return total},// 计算购物车选中的一共多少钱get cart_shop_choosed_total_price() {let total_price = 0this.cart_list.forEach(goods => {if (goods.goods_state == true) {total_price = total_price + goods.goods_count * goods.goods_price}})return total_price.toFixed(2)},// 计算购物车选中的一共多少件get cart_shop_choosed_total_num() {let total_num = 0this.cart_list.forEach(goods => {if (goods.goods_state == true) {total_num = total_num + goods.goods_count}})return total_num},// 计算是否全选get is_all_checked() {let all_checked = truethis.cart_list.forEach(goods => {if (goods.goods_state == false) {all_checked = false}})return all_checked},// 从本地存储中获取购物车商品信息cart_list: wx.getStorageSync('cart_list') || [],// 添加购物车方法add_cart_obj: action(function (goods_obj) {let findResult = this.cart_list.find((x) => x.goods_id === goods_obj.goods_id)if (!findResult) {this.cart_list = [...this.cart_list, goods_obj]} else {findResult.goods_count++}wx.setStorageSync('cart_list', this.cart_list)}),// 设置购物车tabBar的徽标set_cart_tabBar_badge:action(function() {wx.setTabBarBadge({index: 2,text: String(this.cart_shop_total),})}),// 改变购物车商品所有选中状态updateAllGoodsState:action(function(all_checked_now_state) {this.cart_list.forEach(goods => {goods.goods_state = !all_checked_now_state})let new_cart_list = JSON.stringify(this.cart_list)this.cart_list = JSON.parse(new_cart_list)wx.setStorageSync('cart_list', this.cart_list)}),// 改变购物车商品选中状态updateGoodsState:action(function(goods) {let findResult = this.cart_list.find(x=>x.goods_id === goods.goods_id)if (findResult) {findResult.goods_state = !goods.goods_statuslet new_cart_list = JSON.stringify(this.cart_list)this.cart_list = JSON.parse(new_cart_list)}wx.setStorageSync('cart_list', this.cart_list)}),// 购物车商品数量 + 1add_one_GoodsNum:action(function(goods) {let findResult = this.cart_list.find(x=>x.goods_id === goods.goods_id)if (findResult) {findResult.goods_count = findResult.goods_count + 1let new_cart_list = JSON.stringify(this.cart_list)this.cart_list = JSON.parse(new_cart_list)}wx.setStorageSync('cart_list', this.cart_list)}),// 购物车商品数量 - 1sub_one_GoodsNum:action(function(goods) {let findResult = this.cart_list.find(x=>x.goods_id === goods.goods_id)if (findResult) {if (findResult.goods_count > 1) {findResult.goods_count = findResult.goods_count - 1let new_cart_list = JSON.stringify(this.cart_list)this.cart_list = JSON.parse(new_cart_list)}}wx.setStorageSync('cart_list', this.cart_list)}),// 删除购物车商品delete_store_goods:action(function(goods) {this.cart_list = this.cart_list.filter(x=>x.goods_id != goods.goods_id)wx.setStorageSync('cart_list', this.cart_list)}),// 是否是从“购物车”跳转到“我的”界面且需要跳转回“购物车”页面need_navigate_back:false,yes_need_navigate_back:action(function() {this.need_navigate_back = true}),cancel_need_navigate_back:action(function() {this.need_navigate_back = false})
})

2.2  tabBar徽标

tarBar上的徽标是购物车中商品的数量

用到的是wx.setTabBarBadge()这个api

index是第几个tarbar,按0、1、2这么排,购物车是第二个

text是要写什么东西,类型为字符串,也就是说可以写其他的文字上去

当切换到任意tarbar的时候会更新徽标,在加入购物车的时候会更新徽标,在改变商品数量的时候会更新徽标

使用方法时引入store然后用this调用就行了

2.3  滑动删除

用的是movable-area与movable-view

movable-view必须放在movable-area中,其余就没什么好讲的了,有例子照抄就行了

2.4  结算

2.4.1  结算前登录

这个功能需要“购物车”和“我的”这两个tarbar共同完成,所以需要用到全局数据

这个变量是给"我的"中的一键登录按钮用的,直接点一键登录按钮就不需要跳转,从购物车界面过来再点一键登录按钮就需要跳转

2.4.2  结算功能

目前小程序结算功能是不开放给个人开发者的,如果要开通的话只有一个公司的营业执照也是不行的,需要有实际的开发需求才能用微信支付的接口

2.5  触发组件事件

这些是触发组件的事件,感兴趣可以看一下这个 15-5.自定义组件的通信-CSDN博客

2.6  源码

wxml

<!--pages/cart/cart.wxml--><!-- 收货地址区域 -->
<view class="cart-container" wx:if="{{cart_list.length != 0}}"><view><!-- 选择收货地址的盒子 --><view class="address-choose-box" wx:if="{{!choosed_address.username}}"><button type="primary" size="mini" class="btnChooseAddress" bind:tap="go_to_choose_address">请选择收货地址+</button></view><!-- 渲染收货信息的盒子 --><view class="address-info-box" wx:else bind:tap="go_to_choose_address"><view class="row1"><view class="row1-left"><view class="username">收货人:<text>{{choosed_address.username}}</text></view></view><view class="row1-right"><view class="phone">电话:<text>{{choosed_address.phone}}</text></view><image src="/src/arrowright.png" mode="" style="width:16px;height:16px" /></view></view><view class="row2"><view class="row2-left">收货地址:</view><view class="row2-right">{{choosed_address.address_detail}}</view></view></view><!-- 底部的边框线 --><image src="/src/cart_border@2x.png" class="address-border"></image></view><!-- 购物车标题 --><view class="cart-title"><image src="../../src/shop.png" mode="" style="width:18px;height:18px;"/><text class="cart-title-text">购物车</text></view><!-- 购物车内容 --><view class="cart_content_view"><block wx:for="{{cart_list}}" wx:key="index" ><movable-area data-goods_id="{{item.goods_id}}"><movable-view direction="horizontal" data-goods_id="{{item.goods_id}}"><goods_list_item goods_small_logo="{{item.goods_small_logo}}" goods_id="{{item.goods_id}}" goods_price="{{item.goods_price}}" goods_name="{{item.goods_name}}" goods_count="{{item.goods_count}}" showRadio="true" showNumberBox="true" goods_checked="{{item.goods_state}}" bind:radio_change="change_radio_state" bind:add_one="add_one" bind:sub_one="sub_one"></goods_list_item><view class="itemDelet" bindtap="delete_goods" data-goods_id="{{item.goods_id}}">删除</view></movable-view></movable-area></block></view><!-- 底部结算区域 --><view class="my-settle-container"><label class="radio"><checkbox color="#C00000" checked="{{is_all_checked}}" bind:tap="click_all_checked_button"/><text>全选</text></label><view class="amount-box">合计:<text class="amount">¥{{cart_shop_choosed_total_price}}</text></view><view class="btn-settle" bind:tap="buy_cart">结算({{cart_shop_choosed_total_num}})</view></view>
</view>
<!-- 当购物车里什么都没有时出现的东西 -->
<view class="empty-cart" wx:else><image src="/src/empty_cart.png" class="empty-img"></image><text class="tip-text">空空如也~</text></view>

wxss

/* pages/cart/cart.wxss *//* 购物车标题 */
.cart-title {height:40px;display:flex;align-items:center;font-size:14px;padding-left:5px;border-bottom:1px solid #efefef;white-space: wrap;
}.cart-title .cart-title-text {margin-left:10px;white-space: wrap;
}/* 购物车内容的滑动效果 */
movable-area {display: flex;flex-direction: row;width: calc(100% + 120rpx);justify-content: center;left: -120rpx;height: 130px;z-index:0;
}movable-view {display: flex;flex-direction: row;width: calc(100% - 120rpx);z-index: 1001;left: 120rpx;
}.itemDelet {position: absolute;text-align: center;right: -125rpx;line-height: 130px;height:130px;background-color: rgb(194,0,2);margin-top: 0rpx;margin-right: 6rpx;width: 100rpx;text-align: right;padding-right: 20rpx;color: #fff;
}/* 收货地址 */
.address-border {display: block;width: 100%;height: 5px;
}.address-choose-box {height: 90px;display: flex;align-items: center;justify-content: center;
}.address-info-box {font-size:12px;height:90px;display:flex;flex-direction:column;justify-content:center;padding:0 5px;
}.address-info-box .row1 {display: flex;justify-content: space-between;
}.address-info-box .row1 .row1-right {display: flex;align-items: center;
}.phone {margin-right: 5px;
}.address-info-box .row2 {display: flex;align-items: center;margin-top: 10px;
}.address-info-box .row2 .row2-left {white-space: nowrap;
}/* 底部结算区域 */
.my-settle-container {position: fixed;bottom: 0;left: 0;width: 100%;height: 50px;background-color: white;display: flex;justify-content: space-between;align-items: center;padding-left: 5px;font-size: 14px;z-index:999;
}.my-settle-container .radio {display:flex;align-items:center;
}.my-settle-container .amount {color:#c00000
}.my-settle-container .btn-settle {height: 50px;min-width: 100px;background-color: #c00000;color: white;line-height: 50px;text-align: center;padding: 0 10px;
}.cart_content_view {padding-bottom:50px;
}/* 当购物车什么都没有的时候的区域 */
.empty-cart {display: flex;flex-direction: column;align-items: center;padding-top: 150px;
}.empty-cart .empty-img {width:90px;height:90px;
}.empty-cart .tip-text {font-size:12px;color:gray;margin-top:15px;
}

js

// pages/cart/cart.js
import {createStoreBindings} from 'mobx-miniprogram-bindings'
import {store} from '../../store/store.js'const App = getApp()Page({buy_cart() {if (this.data.cart_shop_choosed_total_num == 0) {wx.showToast({title: '请选择要结算的商品',mask:true,icon:'none'})setTimeout(function() {wx.hideToast()},800)return}if (!this.data.choosed_address.username) {wx.showToast({title: '请选择收货地址',mask:true,icon:'none'})setTimeout(function() {wx.hideToast()},800)return}if (this.data.userinfo == '') {wx.showToast({title: '请先登录',mask:true,icon:'none'})setTimeout(()=>{wx.hideToast()wx.switchTab({url: '/pages/my/my',success:()=>{this.yes_need_navigate_back()}})},800)return }},click_all_checked_button() {this.updateAllGoodsState(this.data.is_all_checked)},go_to_choose_address() {wx.navigateTo({url:'/subpackage_choose_address/choose_address'})},change_radio_state(e) {this.updateGoodsState(e.detail)},add_one(e) {this.add_one_GoodsNum(e.detail)this.set_cart_tabBar_badge()},sub_one(e) {this.sub_one_GoodsNum(e.detail)this.set_cart_tabBar_badge()},delete_goods(e) {this.delete_store_goods(e.target.dataset)this.set_cart_tabBar_badge()},/*** 页面的初始数据*/data: {// address:{'name':'suyu'}choosed_address:{},userinfo:'',from:''},/*** 生命周期函数--监听页面加载*/onLoad(options) {this.storeBindings = createStoreBindings(this,{store,fields:['cart_list','cart_shop_choosed_total_price','cart_shop_choosed_total_num','is_all_checked'],actions:['set_cart_tabBar_badge','updateGoodsState','add_one_GoodsNum','sub_one_GoodsNum','delete_store_goods','updateAllGoodsState','yes_need_navigate_back']})},/*** 生命周期函数--监听页面初次渲染完成*/onReady() {// 设置购物车tabBar的徽标this.set_cart_tabBar_badge()this.setData({choosed_address:wx.getStorageSync('choosed_address') || {}})},/*** 生命周期函数--监听页面显示*/onShow() {this.setData({choosed_address:wx.getStorageSync('choosed_address') || {}})this.setData({userinfo:wx.getStorageSync('userinfo') || ''})},/*** 生命周期函数--监听页面隐藏*/onHide() {},/*** 生命周期函数--监听页面卸载*/onUnload() {},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh() {},/*** 页面上拉触底事件的处理函数*/onReachBottom() {},/*** 用户点击右上角分享*/onShareAppMessage() {}
})


http://www.ppmy.cn/news/1453391.html

相关文章

c++中map与set的基本使用

c中的map容器与set容器 map的所有函数方法及其用法 在C中&#xff0c;std::map 是一个关联容器&#xff0c;它包含可以重复的键值对&#xff08;实际上&#xff0c;std::map中的键是唯一的&#xff09;。每个元素都有一个唯一的键和一个与之关联的值。std::map通常按照其键的…

C++成员初始化列表

我们在类的构造函数中使用成员初始化列表可以带来效率上的提升&#xff0c;那么成员初始化列表在编译后会发生什么就是这篇文章要探究的问题 文章目录 引入成员初始化列表用成员初始化列表优化上面的代码成员初始化列表展开成员初始化列表的潜在危险 参考资料 引入 考虑下面这…

HTML_CSS学习:CSS盒子模型

一、CSS中常用的长度单位 相关代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>CSS中常用的长度单位</title><style>html{font-size: 40px;}#d1{/*第一种长度单位&…

【微服务 开发】微服务介绍,服务拆分,远程调用

微服务 微服务SpringCloud 拆分如何拆分 远程调用 微服务 微服务是一种软件架构风格&#xff0c;它是以专注于单一职责的很多小型项目为基础&#xff0c;组合成复杂的大型应用 单体架构 将业务的所有功能集中在一个项目中进行开发&#xff0c;打成一个包部署 微服务的特征&…

python制作可执行文件(cython)

使用Cython将Python脚本编译成可执行文件涉及几个步骤。以下是一个基本的指南&#xff1a; 1. 安装Cython 首先&#xff0c;你需要安装Cython。你可以使用pip来安装&#xff1a; pip install cython 2. 编写Cython文件 通常&#xff0c;Cython源文件的后缀是.pyx。你可以将…

Containerd方式部署K8s集群

1.1 Kubernetes基础环境部署 kubernetes有多种部署方式&#xff0c;目前主流的方式有kubeadm、minikube、二进制包 minikube&#xff1a;一个用于快速搭建单节点kubernetes的工具 kubeadm&#xff1a;一个用于快速搭建kubernetes集群的工具 二进制包 &#xff1a;从官网下载…

yarn -v在vscode中报错

前言&#xff1a;积累小知识 1、问题描述 yarn属于类似于npm的安装工具&#xff0c;成功用npm install --global yarn 之后 在cmd终端可以查询yarn的版本号&#xff0c;但是在vscode的终端里却会报错&#xff0c;如下图&#xff1a; 2、解决 分析原因&#xff1a;VSCode的终…

【C++并发编程】(三)互斥锁:std::mutex

文章目录 互斥锁数据竞争C互斥锁lock() 和 unlock()std::lock_guard 互斥锁 数据竞争 在并发编程中&#xff0c;数据竞争是指多个线程同时对共享数据进行读写操作&#xff0c;并且至少有一个线程进行写操作&#xff0c;从而导致未定义的行为或结果。 下面给出一个例子&#x…