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

2024-05-03 12:12

本文主要是介绍附录6-4 黑马优购项目-分类和购物车,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

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() {}
})

这篇关于附录6-4 黑马优购项目-分类和购物车的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/956722

相关文章

Python 中 requests 与 aiohttp 在实际项目中的选择策略详解

《Python中requests与aiohttp在实际项目中的选择策略详解》本文主要介绍了Python爬虫开发中常用的两个库requests和aiohttp的使用方法及其区别,通过实际项目案... 目录一、requests 库二、aiohttp 库三、requests 和 aiohttp 的比较四、requ

SpringBoot项目启动后自动加载系统配置的多种实现方式

《SpringBoot项目启动后自动加载系统配置的多种实现方式》:本文主要介绍SpringBoot项目启动后自动加载系统配置的多种实现方式,并通过代码示例讲解的非常详细,对大家的学习或工作有一定的... 目录1. 使用 CommandLineRunner实现方式:2. 使用 ApplicationRunne

使用IntelliJ IDEA创建简单的Java Web项目完整步骤

《使用IntelliJIDEA创建简单的JavaWeb项目完整步骤》:本文主要介绍如何使用IntelliJIDEA创建一个简单的JavaWeb项目,实现登录、注册和查看用户列表功能,使用Se... 目录前置准备项目功能实现步骤1. 创建项目2. 配置 Tomcat3. 项目文件结构4. 创建数据库和表5.

Python项目打包部署到服务器的实现

《Python项目打包部署到服务器的实现》本文主要介绍了PyCharm和Ubuntu服务器部署Python项目,包括打包、上传、安装和设置自启动服务的步骤,具有一定的参考价值,感兴趣的可以了解一下... 目录一、准备工作二、项目打包三、部署到服务器四、设置服务自启动一、准备工作开发环境:本文以PyChar

多模块的springboot项目发布指定模块的脚本方式

《多模块的springboot项目发布指定模块的脚本方式》该文章主要介绍了如何在多模块的SpringBoot项目中发布指定模块的脚本,作者原先的脚本会清理并编译所有模块,导致发布时间过长,通过简化脚本... 目录多模块的springboot项目发布指定模块的脚本1、不计成本地全部发布2、指定模块发布总结多模

SpringBoot项目删除Bean或者不加载Bean的问题解决

《SpringBoot项目删除Bean或者不加载Bean的问题解决》文章介绍了在SpringBoot项目中如何使用@ComponentScan注解和自定义过滤器实现不加载某些Bean的方法,本文通过实... 使用@ComponentScan注解中的@ComponentScan.Filter标记不加载。@C

javafx 如何将项目打包为 Windows 的可执行文件exe

《javafx如何将项目打包为Windows的可执行文件exe》文章介绍了三种将JavaFX项目打包为.exe文件的方法:方法1使用jpackage(适用于JDK14及以上版本),方法2使用La... 目录方法 1:使用 jpackage(适用于 JDK 14 及更高版本)方法 2:使用 Launch4j(

Docker集成CI/CD的项目实践

《Docker集成CI/CD的项目实践》本文主要介绍了Docker集成CI/CD的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录一、引言1.1 什么是 CI/CD?1.2 docker 在 CI/CD 中的作用二、Docke

SpringBoot项目引入token设置方式

《SpringBoot项目引入token设置方式》本文详细介绍了JWT(JSONWebToken)的基本概念、结构、应用场景以及工作原理,通过动手实践,展示了如何在SpringBoot项目中实现JWT... 目录一. 先了解熟悉JWT(jsON Web Token)1. JSON Web Token是什么鬼

手把手教你idea中创建一个javaweb(webapp)项目详细图文教程

《手把手教你idea中创建一个javaweb(webapp)项目详细图文教程》:本文主要介绍如何使用IntelliJIDEA创建一个Maven项目,并配置Tomcat服务器进行运行,过程包括创建... 1.启动idea2.创建项目模板点击项目-新建项目-选择maven,显示如下页面输入项目名称,选择