taro 實現購物車邏輯
- 效果
- taro是什麼?
- Taro 是一套遵循 React 語法規範的 多端開發 解決方案。
現如今市面上端的形態多種多樣,Web、React-Native、微信小程序等各種端大行其道,當業務要求同時在不同的端都要求有所表現的時候,針對不同的端去編寫多套代碼的成本顯然非常高,這時候只編寫一套代碼就能夠適配到多端的能力就顯得極爲需要。
使用 Taro,我們可以只書寫一套代碼,再通過 Taro 的編譯工具,將源代碼分別編譯出可以在不同端(微信/百度/支付寶/字節跳動/QQ/京東小程序、快應用、H5、React-Native 等)運行的代碼。 - 本代碼是基於Taro UI 開發的,雖然是基於 taro框架開發的,但購物車的整體邏輯與微信小程序邏輯是基本一樣的
Taro UI是一款基於 Taro 框架開發的多端 UI 組件庫
需要安裝taro ui
$ npm install taro-ui
cart/index.jsx頁面代碼
import Taro, { Component } from '@tarojs/taro'
import { View, Checkbox, CheckboxGroup } from '@tarojs/components'
//用到了taro的三個組件
//想了解可以去查看taro的官方文檔
import './index.scss'
import { AtButton, AtInputNumber, AtCard } from 'taro-ui'
import { request, toast } from '../../utils/index'
class Index extends Component {
constructor(props) {
super(props)
this.state = {
message: '', //購物車爲空時顯示的信息
cartdata: [], //購物車的數據列表
isactive: false, //全選按鈕是否選中
check:false, //單個商品購物車是否被選中
totalnum:0, //總數量
totalprice:0, //總價格
activedata:[] //複選框選中的數據列表
}
}
componentDidShow () {
//獲取購物車數據
try {
const token = Taro.getStorageSync('token') //這兩個數據是我在登錄頁面,登錄時添加到本地的token和用戶id
const userid = Taro.getStorageSync('userid')
if (token) { //如果登錄了
const usrename = Taro.getStorageSync('username') //同樣登錄時添加到本地的用戶名
Taro.setNavigationBarTitle({ //改變導航欄的標題
title: usrename + '---購物車'
})
request({ //這裏的request是封裝後的方法
url: '/cart', //接口
data: { //需要傳遞的數據
token,
userid
}
}).then(res => {
console.log(res.data)
const { code } = res.data
if (code === '10119') { //後端返回的值 ,判斷狀態
toast({ title: '登錄已經過期,請從新登錄' })
Taro.navigateTo({ //跳轉到登錄頁
url: '/pages/login/index'
})
} else if (code === '10012') {
this.setState({
message: '購物車空空如也'
})
} else {
//因爲taro是基於react的,在react中,狀態不能直接改變,要用this.setState
this.setState({ //登錄成功,購物車有數據時,將購物車的列表數據添加到購物車數據中
cartdata: res.data.data
})
}
})
} else { //如果沒登錄
toast({ title: '請登錄' })
Taro.navigateTo({ //跳轉到登錄頁面
url: '/pages/login/index'
})
}
} catch (e) {
}
}
componentDidUpdate(){
//計算總數量,總價格
let num=0;
let price=0;
if(this.state.activedata.length!=0){ //如果選中的數組長度不爲0時,就是有商品被選中了
this.state.activedata.map((item)=>{ //map遍歷數組
num+= +item.num //將數量相加 + 號爲一元運算符,將字符串類型轉換爲數值類型
price+=item.num*item.price //求價格
})
this.setState({ //設置值
totalnum:num,
totalprice:price
})
}else{ //如果沒有商品被選中
this.setState({
totalnum:0,
totalprice:0
})
}
}
render() {
return ( //結構開始
<View>{
this.state.message.length === 0 ? null : //如果 message不爲空的話,就代表着購物車沒有數據,所以顯示購物車空空如也,去選購,如果爲空,代表着購物車有數據,不顯示
<View onClick={() => { //點擊事件 去主頁選購商品
Taro.switchTab({
url: '/pages/home/index'
})
}}> {this.state.message}去選購</View>
}
<Checkbox checked={this.state.isactive} onClick={()=>{ //全選按鈕 check代表着按鈕是否選中 因爲taro中的checkbox的onchange方法,不支持小程序,所以沒辦法,只能用 onClick方法
let active=!this.state.isactive //實現點擊選中狀態取反
this.setState({
isactive:active
})
if(active===true){ //如果全選,就代表着 購物車的所有商品都被選中,所以,將購物車列表數據全給選中的數組,將單個商品的狀態全部設爲選中
this.setState({
check:true,
activedata:this.state.cartdata
})
}else{//否則,選中商品數組爲空,將單個商品的狀態全部設爲未選中
this.setState({
check:false,
activedata:[]
})
}
}}>全選</Checkbox>
<CheckboxGroup onChange={(evt)=>{ //複選框組,<CheckboxGroup/>中選中項發生改變是觸發 change 事件,detail = value:[選中的 Checkbox 的 value 的數組]
const {detail:{value}}=evt
if(value.length===this.state.cartdata.length){ //選中的數組的長度如果等於購物車列表的長度是全選
this.setState({
isactive:true, //全選按鈕被選中
activedata:this.state.cartdata //選中商品數組爲購物車的列表數組
})
}else{ //否則未全選
var i;
var data=[];
for ( i in value){ //因爲value數組裏的值爲選中的checkbox的value的值,我設置的爲cartid
data.push(...(this.state.cartdata.filter(item=>{ //過濾下購物車的列表數據,將cartid相等的對象取出來,放進data數組中,...是展開運算符,加他是因爲在控制檯打印的時候發現,每個對象外面都加了一個【】,沒辦法,這裏應該是有簡單的寫法的,但因爲當時累了,也沒有細想,就只能寫成這樣了,
return item.cartid==value[i]
})))
}
console.log(data,this.state.cartdata)
this.setState({
isactive:false,//全選按鈕未被選中
activedata:data //設置選中商品的數組
//至此,計算總數量,總價格、全選、單選的邏輯就全完成了,至於爲什麼寫成這樣,是因爲taro是基於react的標準的,沒有計算屬性,沒有雙向綁定
})
}
}}>
{
this.state.cartdata.map((item, index) => //循環顯示購物車數據
<AtCard
title={item.proname}
thumb={item.proimg}
extra={'$'+item.price}
key={item.proid}
>
<View><Checkbox value={item.cartid} checked={this.state.check}></Checkbox>
{/* 每個商品前的複選框 */}
<AtInputNumber //數量加減
min={0}
max={10}
step={1}
value={item.num} //之間的值
onChange={this.change.bind(this, item,index)} //onchange輸入框值改變時觸發的事件,開發者需要通過 onChange 事件來更新 value 值變化,onChange 函數必填
/>
<AtButton type='primary' size='small' onClick={this.del.bind(this,item)}>刪除</AtButton>
{/* 刪除按鈕 */}
</View>
</AtCard>
)
}
</CheckboxGroup>
<View>總數量:{this.state.totalnum}</View>
<View>總價格:{this.state.totalprice}</View>
</View>
)
}
del(item){ //刪除方法
//item代表着商品的數據
try{
const token = Taro.getStorageSync('token')
if(token){ //如果有token值
request({ //數據請求 刪除接口
url: '/cart/delete',
data: {
token,
cartid: item.cartid
}
}).then(res => {
const { code } = res.data
if (code === '10119') { //後端接口 返回值
toast({ title: '登錄狀態過期,請重新登錄' })
Taro.navigateTo({ //跳轉到登錄頁面
url: '/pages/login/index'
})
}else{
toast({title:'刪除成功!'}) //顯示提示框 封裝的一個方法 其實到這步,商品就已經刪除了,但頁面還沒有發生變化,所以我們要處理下頁面
let id=item.cartid
let data1=this.state.cartdata.filter(item=>{ //過濾下不等於被刪除的商品id,將未刪除的商品,放到data1中
return item.cartid!=id
})
let data2=this.state.activedata.filter(item=>{ //在選中情況下
return item.cartid!=id
})
this.setState({ //設置下購物車列表數據
cartdata:data1,
activedata:data2
})
}
})
}else{ //如果沒有token值
toast({ title: '請登錄' })
Taro.navigateTo({ //跳轉到登錄頁面
url: '/pages/login/index'
})
}
}catch(e){
}
}
change(item,index,evt) {
//數量改變
console.log(evt)
//item代表着商品的數據
//index,爲當前改變的是那個商品的值,
//evt爲改變後的數值
try {
const token = Taro.getStorageSync('token')
if (token) { //如果有token值
if (evt === '0') { //數量爲0 我設置的爲刪除商品,與上面的刪除一致,這裏我就不再解釋了
request({
url: '/cart/delete',
data: {
token,
cartid: item.cartid
}
}).then(res => {
const { code } = res.data
if (code === '10119') {
toast({ title: '登錄狀態過期,請重新登錄' })
Taro.navigateTo({
url: '/pages/login/index'
})
}else{
toast({title:'刪除成功!'})
let id=item.cartid
let data1=this.state.cartdata.filter(item=>{
return item.cartid!=id
})
let data2=this.state.activedata.filter(item=>{ //在選中情況下
return item.cartid!=id
})
this.setState({
cartdata:data1,
activedata:data2
})
}
})
}else{ //改變的值不爲0 ,
request({
url: '/cart/update', //更新接口
data: {
token,
cartid: item.cartid,
num:evt //將改變的值直接付給num
}
}).then(res => {
const { code } = res.data
if (code === '10119') { //後端驗證
toast({ title: '登錄狀態過期,請重新登錄' })
Taro.navigateTo({ //跳轉到登錄頁
url: '/pages/login/index'
})
}else{
toast({title:'更新成功!'})
item.num=evt //改變下數量
// var newitem=item
// var data=this.state.cartdata.map(item=>{
// return item.cartid===newitem.cartid ?newitem :item
// })
var data=this.state.cartdata //將購物車裏邊數據賦給data ,因爲在react中,狀態不能直接改變
data[index]=item // 將新的對象賦給數組的第index對象
this.setState({ //設置下
cartdata:data
})
}
})
}
} else {//如果沒有token值
toast({ title: '請登錄' })
Taro.navigateTo({
url: '/pages/login/index'
})
}
} catch (e) {
}
}
}
export default Index
cart/index.scss頁面代碼
@import "~taro-ui/dist/style/components/card.scss";
@import "~taro-ui/dist/style/components/button.scss";
@import "~taro-ui/dist/style/components/loading.scss";
@import "~taro-ui/dist/style/components/icon.scss";
@import "~taro-ui/dist/style/components/input-number.scss";
utils/index.js代碼
const publicurl ='',//接口就不放上去了,因爲也不是我的,這裏就放接口前的公共網址
import Taro from '@tarojs/taro'
export function request(options){
const {url,data,method}=options
wx.showLoading({ //顯示loading框
title: '加載中',
})
return new Promise((resolve,reject)=>{
Taro.request({ //數據請求 與小程序類似
url: publicurl+url,
data:data || {},
method:method || 'GET',
success(res){
//成功
resolve(res)
},
fail(err){
//失敗
reject(err)
},
complete(){
// complete 接口調用結束的回調函數
wx.hideLoading(); //隱藏loading框
}
})
})
}
export function toast(options){
const {title,icon, duration}=options
Taro.showToast({
title,
icon: icon || 'none',
duration:duration || 1000
})
}
至此特殊時期,我們更應該沉下心,去學習、去提升、去"逆戰"