taro 實現購物車邏輯

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

taro官方文檔
taroUI 官方文檔

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

至此特殊時期,我們更應該沉下心,去學習、去提升、去"逆戰"

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章