Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国产精品久久久久久久人人看 ,99精品视频在线观看免费播放,中文字幕一区二区三区亚洲精品

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          訂單及其狀態(tài)機(jī)的設(shè)計實現(xiàn)

          訂單及其狀態(tài)機(jī)的設(shè)計實現(xiàn)

          態(tài)機(jī)簡介:

          狀態(tài)機(jī)是有限狀態(tài)自動機(jī)的簡稱,是現(xiàn)實事物運(yùn)行規(guī)則抽象而成的一個數(shù)學(xué)模型。【規(guī)則的抽象】

          有限狀態(tài)機(jī)一般都有以下特點:

          (1)可以用狀態(tài)來描述事物,并且任一時刻,事物總是處于一種狀態(tài);

          (2)事物擁有的狀態(tài)總數(shù)是有限的;

          (3)通過觸發(fā)事物的某些行為,可以導(dǎo)致事物從一種狀態(tài)過渡到另一種狀態(tài);

          (4)事物狀態(tài)變化是有規(guī)則的,A狀態(tài)可以變換到B,B可以變換到C,A卻不一定能變換到C;

          (5)同一種行為,可以將事物從多種狀態(tài)變成同種狀態(tài),但是不能從同種狀態(tài)變成多種狀態(tài)。

          狀態(tài)機(jī)這種描述客觀世界的方式就是將事物抽象成若干狀態(tài),然后所有的事件和規(guī)則導(dǎo)致事物在這些狀態(tài)中游走。最終使得事物“自圓其說”。

          很多通信協(xié)議的開發(fā)都必須用到狀態(tài)機(jī);一個健壯的狀態(tài)機(jī)可以讓你的程序,不論發(fā)生何種突發(fā)事件都不會突然進(jìn)入一個不可預(yù)知的程序分支。

          • 狀態(tài)機(jī)示例:

          四大概念:


          狀態(tài)(state)

          一個狀態(tài)機(jī)至少要包含兩個狀態(tài)。

          分為:現(xiàn)態(tài)(源狀態(tài))、次態(tài)(目標(biāo)狀態(tài))

          狀態(tài)可以理解為一種結(jié)果,一種穩(wěn)態(tài)形式,沒有擾動會保持不變的。

          狀態(tài)命名形式:

          1.副詞+動詞;例如:待審批、待支付、待收貨

          這種命名方式體現(xiàn)了:狀態(tài)機(jī)就是事件觸發(fā)狀態(tài)不斷遷徙的本質(zhì)。表達(dá)一種待觸發(fā)的感覺。

          2.動詞+結(jié)果;例如:審批完成、支付完成

          3.已+動詞形式;例如:已發(fā)貨、已付款

          以上兩種命名方式體現(xiàn)了:狀態(tài)是一種結(jié)果或者穩(wěn)態(tài)的本質(zhì)。表達(dá)了一種已完成的感覺。

          角色很多的時候,為了表示清晰,可以加上角色名:例如:待財務(wù)審批、主管批準(zhǔn)

          命名考慮從用戶的理解的角度出發(fā)。



          事件(event)

          or

          觸發(fā)條件

          又稱為“條件”,就是某個操作動作的觸發(fā)條件或者口令。當(dāng)一個條件滿足時,就會觸發(fā)一個動作,或者執(zhí)行一次狀態(tài)遷徙

          這個事件可以是外部調(diào)用、監(jiān)聽到消息、或者各種定時到期等觸發(fā)的事件。

          對于燈泡,“打開開關(guān)”就是一個事件。

          條件命名形式:動詞+結(jié)果;例如:支付成功、下單時間>5分鐘


          動作(action)

          事件發(fā)生以后要執(zhí)行動作。例如:事件=“打開開關(guān)指令”,動作=“開燈”。一般就對應(yīng)一個函數(shù)。

          條件滿足后執(zhí)行動作。動作執(zhí)行完畢后,可以遷移到新的狀態(tài),也可以仍舊保持原狀態(tài)。

          動作不是必需的,當(dāng)條件滿足后,也可以不執(zhí)行任何動作,直接遷移到新狀態(tài)。

          那么如何區(qū)分“動作”和“狀態(tài)”?

          “動作”是不穩(wěn)定的,即使沒有條件的觸發(fā),“動作”一旦執(zhí)行完畢就結(jié)束了;

          而“狀態(tài)”是相對穩(wěn)定的,如果沒有外部條件的觸發(fā),一個狀態(tài)會一直持續(xù)下去。


          變換(transition)

          即從一個狀態(tài)變化到另外一個狀態(tài)

          例如:“開燈過程”就是一個變化



          狀態(tài)機(jī)其他表達(dá)方式:

          狀態(tài)機(jī)的設(shè)計:

          信息系統(tǒng)中有很多狀態(tài)機(jī),例如:業(yè)務(wù)訂單的狀態(tài)。

          狀態(tài)機(jī)的設(shè)計存在的問題:什么是狀態(tài)?到底有多少個狀態(tài)?要細(xì)分到什么程度?

            信息系統(tǒng)是現(xiàn)實世界的一種抽象和描述。而業(yè)務(wù)領(lǐng)域中那些已經(jīng)發(fā)生的事件就是事實信息系統(tǒng)就是將這些事實以信息的形式存儲到數(shù)據(jù)庫中,即:信息就是一組事實

          信息系統(tǒng)就是存儲這些事實,對這些事實進(jìn)行管理與追蹤,進(jìn)而起到提高工作效率的作用。

          信息系統(tǒng)就是記錄已經(jīng)發(fā)生的事實,信息系統(tǒng)中的狀態(tài)基本和事實匹配。即:標(biāo)識某個事實的完成度。

          業(yè)務(wù)系統(tǒng),根據(jù)實際業(yè)務(wù),具體會有哪些發(fā)生的事實需要記錄,基本上這些事實就至少對應(yīng)一個狀態(tài)。需要記錄的事實就是一種穩(wěn)態(tài),一種結(jié)果。

          例如:【待支付】->【已支付】->【已收貨】->【已評價】

          這些都是系統(tǒng)需要記錄的已發(fā)生的客觀事實。而這些事實就對應(yīng)了狀態(tài),而發(fā)生這些事實的事件就對應(yīng)了觸發(fā)狀態(tài)機(jī)的轉(zhuǎn)換的事件。

          根據(jù)自己的業(yè)務(wù)實際進(jìn)行分析,并畫出狀態(tài)圖即可。

          狀態(tài)機(jī)實現(xiàn)方式:狀態(tài)模式

          下面是經(jīng)典的自動販賣機(jī)例子來說明狀態(tài)模式的用法,狀態(tài)圖如下:

          分析一個這個狀態(tài)圖:

          a、包含4個狀態(tài)(我們使用4個int型常量來表示)
          b、包含3個暴露在外的方法(投幣、退幣、轉(zhuǎn)動手柄、(發(fā)貨動作是內(nèi)部方法,售賣機(jī)未對外提供方法,售賣機(jī)自動調(diào)用))
          c、我們需要處理每個狀態(tài)下,用戶都可以觸發(fā)這三個動作。

          我們可以做沒有意義的事情,在【未投幣】狀態(tài),試著退幣,或者同時投幣兩枚,此時機(jī)器會提示我們不能這么做。


          實現(xiàn)邏輯:

              任何一個可能的動作,我們都要檢查,看看我們所處的狀態(tài)和動作是否合適。

          狀態(tài)機(jī)使用if-else或switch實現(xiàn)

          測試自動售賣機(jī)

          使用if-else/switch的方式實現(xiàn)狀態(tài)有如下問題:

          • 沒有遵守【開閉】原則,沒有【封裝變化】,所以沒有彈性,應(yīng)對需求變更非常吃力。

             例如:現(xiàn)在增加一個狀態(tài)。每個方法都需要添加if-else語句。

          • 狀態(tài)如何轉(zhuǎn)換看得不是很清楚,隱藏在if-else/switch邏輯中。

          升級策略:

          【封裝變化】,局部化每個狀態(tài)的行為,將每個狀態(tài)的行為放到各自類中,每個狀態(tài)只要實現(xiàn)自己的動作就可以了。

          販賣機(jī)只要將動作委托給代表當(dāng)前狀態(tài)的狀態(tài)對象即可。


          public interface State
          {
              /**
               * 放錢
               */
              public void insertMoney();
              /**
               * 退錢
               */
              public void backMoney();
              /**
               * 轉(zhuǎn)動曲柄
               */
              public void turnCrank();
              /**
               * 出商品
               */
              public void dispense();
          }
          public class NoMoneyState implements State
          {
           
              private VendingMachine machine;
           
              public NoMoneyState(VendingMachine machine)
              {
                  this.machine=machine;
                  
              }
              
              @Override
              public void insertMoney()
              {
                  System.out.println("投幣成功");
                  machine.setState(machine.getHasMoneyState());
              }
           
              @Override
              public void backMoney()
              {
                  System.out.println("您未投幣,想退錢?...");
              }
           
              @Override
              public void turnCrank()
              {
                  System.out.println("您未投幣,想拿東西么?...");
              }
           
              @Override
              public void dispense()
              {
                  throw new IllegalStateException("非法狀態(tài)!");
              }
           
          }
          public class HasMoneyState implements State
          {
           
              private VendingMachine machine;
           
              public HasMoneyState(VendingMachine machine)
              {
                  this.machine=machine;
              }
           
              @Override
              public void insertMoney()
              {
                  System.out.println("您已經(jīng)投過幣了,無需再投....");
              }
           
              @Override
              public void backMoney()
              {
                  System.out.println("退幣成功");
                      machine.setState(machine.getNoMoneyState());
              }
           
              @Override
              public void turnCrank()
              {
                  System.out.println("你轉(zhuǎn)動了手柄");
                  machine.setState(machine.getSoldState());
              }
           
              @Override
              public void dispense()
              {
                  throw new IllegalStateException("非法狀態(tài)!");
              }
           
          }
              
          public class SoldOutState implements State
          {
           
              private VendingMachine machine;
           
              public SoldOutState(VendingMachine machine)
              {
                  this.machine=machine;
              }
           
              @Override
              public void insertMoney()
              {
                  System.out.println("投幣失敗,商品已售罄");
              }
           
              @Override
              public void backMoney()
              {
                  System.out.println("您未投幣,想退錢么?...");
              }
           
              @Override
              public void turnCrank()
              {
                  System.out.println("商品售罄,轉(zhuǎn)動手柄也木有用");
              }
           
              @Override
              public void dispense()
              {
                  throw new IllegalStateException("非法狀態(tài)!");
              }
           
          }
          public class SoldState implements State
          {
           
              private VendingMachine machine;
           
              public SoldState(VendingMachine machine)
              {
                  this.machine=machine;
              }
           
              @Override
              public void insertMoney()
              {
                  System.out.println("正在出貨,請勿投幣");
              }
           
              @Override
              public void backMoney()
              {
                  System.out.println("正在出貨,沒有可退的錢");
              }
           
              @Override
              public void turnCrank()
              {
                  System.out.println("正在出貨,請勿重復(fù)轉(zhuǎn)動手柄");
              }
           
              @Override
              public void dispense()
              {
                  machine.releaseBall();
                  if (machine.getCount() > 0)
                  {
                      machine.setState(machine.getNoMoneyState());
                  } else
                  {
                      System.out.println("商品已經(jīng)售罄");
                      machine.setState(machine.getSoldOutState());
                  }
              }
          }
          public class VendingMachine
          {
              private State noMoneyState;
              private State hasMoneyState;
              private State soldState;
              private State soldOutState;
              private State winnerState ; 
          
          
              private int count=0;
              private State currentState=noMoneyState;
           
              public VendingMachine(int count)
              {
                  noMoneyState=new NoMoneyState(this);
                  hasMoneyState=new HasMoneyState(this);
                  soldState=new SoldState(this);
                  soldOutState=new SoldOutState(this);
                  winnerState=new WinnerState(this);
           
                        if (count > 0)
                       {
                      this.count=count;
                      currentState=noMoneyState;
                       }
              }
           
                 //將這些動作委托給當(dāng)前狀態(tài).
              public void insertMoney()
              {
                  currentState.insertMoney();
              }
           
              public void backMoney()
              {
                  currentState.backMoney();
              }
                 
                  // 機(jī)器不用提供dispense動作,因為這是一個內(nèi)部動作.用戶不可以直 
                  //接要求機(jī)器發(fā)放糖果.我們在狀態(tài)對象的turnCrank()方法中調(diào)用 
                  //dispense方法;
          
                 //dispense無論如何,即使在nomoney狀態(tài)也會被執(zhí)行.
                 //讓不合法的情形下,dispense拋出異常處理。
              public void turnCrank()
              {
                  currentState.turnCrank();
                      currentState.dispense();
              }
           
          
              public void releaseBall()
              {
                  System.out.println("發(fā)出一件商品...");
                  if (count !=0)
                  {
                      count -=1;
                  }
              }
           
              public void setState(State state)
              {
                  this.currentState=state;
              }
           
              //getter setter omitted ...
           
          }

          我們之前說過,if-else/switch實現(xiàn)方式?jīng)]有彈性,那現(xiàn)在按照這種實現(xiàn)模式,需求變更修改起來會輕松點嗎?

          紅色部分標(biāo)記了我們的需求變更:當(dāng)用戶每次轉(zhuǎn)動手柄的時候,有10%的幾率贈送一瓶。

          實現(xiàn)方式:

          我們遵守了【開閉】原則,只要新建一個WinnerState的類即可。然后有限的修改has_money的轉(zhuǎn)向即可。

          為什么WinnerState要獨(dú)立成一個狀態(tài),其實它和sold狀態(tài)一模一樣。我把代碼寫在SoldState中不行嗎?

          • 第一個原因就是上面說的信息系統(tǒng)的本質(zhì)就是記錄【事實】,中獎是需要記錄的事實,它應(yīng)該是一個狀態(tài)。
          • 第二個原因:【單一職責(zé)】問題,我們一個類的責(zé)任是明確的。

             如果sold需求變化不一定影響到winner代碼實現(xiàn),winner需求變化時,也不一定要修改sold,比如促銷方案結(jié)束了,中獎概率變了等。

             如果他們的變化不是一定互相影響到彼此的,那我們就該將他們分離,即是【隔離變化】也是遵守【單一職責(zé)】的原則。


          public class WinnerState implements State
          {
           
              private VendingMachine machine;
           
              public WinnerState(VendingMachine machine)
              {
                  this.machine=machine;
              }
           
              @Override
              public void insertMoney()
              {
                  throw new IllegalStateException("非法狀態(tài)");
              }
           
              @Override
              public void backMoney()
              {
                  throw new IllegalStateException("非法狀態(tài)");
              }
           
              @Override
              public void turnCrank()
              {
                  throw new IllegalStateException("非法狀態(tài)");
              }
           
              @Override
              public void dispense()
              {
                  System.out.println("你中獎了,恭喜你,將得到2件商品");
                  machine.releaseBall();
           
              if (machine.getCount()==0)
              {
                  System.out.println("商品已經(jīng)售罄");
                  machine.setState(machine.getSoldOutState());
              } else
              {
                  machine.releaseBall();
                  if (machine.getCount() > 0)
                  {
                      machine.setState(machine.getNoMoneyState());
                  } else
                  {
                      System.out.println("商品已經(jīng)售罄");
                      machine.setState(machine.getSoldOutState());
                  }
                  
              }
           
              }
           
          }
          public class HasMoneyState implements State
          {
           
              private VendingMachine machine;
              private Random random=new Random();
           
              public HasMoneyState(VendingMachine machine)
              {
                  this.machine=machine;
              }
           
              @Override
              public void insertMoney()
              {
                  System.out.println("您已經(jīng)投過幣了,無需再投....");
              }
           
              @Override
              public void backMoney()
              {
                  System.out.println("退幣成功");
           
              machine.setState(machine.getNoMoneyState());
              }
           
              @Override
              public void turnCrank()
              {
                  System.out.println("你轉(zhuǎn)動了手柄");
                  int winner=random.nextInt(10);
                  if (winner==0 && machine.getCount() > 1)
                  {
                      machine.setState(machine.getWinnerState());
                  } else
                  {
                      machine.setState(machine.getSoldState());
                  }
              }
           
              @Override
              public void dispense()
              {
                  throw new IllegalStateException("非法狀態(tài)!");
              }
           
          }

          總結(jié)狀態(tài)模式:

          狀態(tài)模式:允許對象在內(nèi)部狀態(tài)改變時改變它的行為,對象看起來好像修改了他的類。

          解釋:

          狀態(tài)模式將狀態(tài)封裝成為獨(dú)立的類,并將動作委托到代表當(dāng)前狀態(tài)的對象。
          所以行為會隨著內(nèi)部狀態(tài)改變而改變。
          我們通過組合簡單引用不同狀態(tài)對象來造成類改變的假象.


          狀態(tài)模式策略模式


          1.行為封裝的n個狀態(tài)中,不同狀態(tài)不用行為。

          2.context的行為委托到不同狀態(tài)中。

          3.[當(dāng)前狀態(tài)]在n個狀態(tài)中游走,context的行為也隨之[當(dāng)前狀態(tài)]的改變而改變。

          4.用戶對context的狀態(tài)改變渾然不知。

          5.客戶不會直接和state交互,只能通過context暴露的方法交互,state轉(zhuǎn)換是context內(nèi)部事情。

          6.state可以是接口也可以是抽象類,取決于有沒公共功能可以放進(jìn)抽象類中。抽象類方便,因為可以后續(xù)加方法。

          可以將重復(fù)代碼放入抽象類中。例如:"你已投入25元,不能重復(fù)投" 這種通用代碼放入抽象類中。

          7.context可以決定狀態(tài)流轉(zhuǎn),如果這個狀態(tài)流轉(zhuǎn)是固定的,就適合放在context中進(jìn)行。但是如果狀態(tài)流轉(zhuǎn)是動態(tài)的就適合放在狀態(tài)中進(jìn)行。

          例如通過商品的剩余數(shù)目來決定流向[已售完]或[等待投幣],這個時候放在狀態(tài)類中,因為dispense要根據(jù)狀態(tài)判斷流轉(zhuǎn)。

          這個寫法決定了,有新需求時候,你是改context還是改state類。

          8.可以共享所有的state對象,但是需要修改context的時候時候,需要handler中傳入context引用

          1.context主動指定需要組合的策略對象是哪一個。

          2.可以在啟動的時候通過工廠動態(tài)指定具體是哪個策略對象,但是沒有在策略對象之間游走,即:只組合了一個策略對象。

          3.策略作為繼承之外一種彈性替代方案。因為繼承導(dǎo)致子類繼承不適用的方法,且每個類都要維護(hù),策略模式通過不同對象組合來改變行為。

          4.策略模式聚焦的是互換的算法來創(chuàng)建業(yè)務(wù)。



          狀態(tài)機(jī)典型應(yīng)用:訂單狀態(tài)控制

          建表語句

          如上圖所示:

          一種典型的訂單設(shè)計。業(yè)務(wù)訂單和支付退款訂單組合,他們分別有自己的狀態(tài)機(jī)。

          • 業(yè)務(wù)訂單狀態(tài)機(jī)負(fù)責(zé)業(yè)務(wù)邏輯,并和支付退款狀態(tài)機(jī)聯(lián)動。
          • 一切以業(yè)務(wù)狀態(tài)機(jī)為主。例如:業(yè)務(wù)狀態(tài)已經(jīng)【關(guān)單】,此時收到支付成功通知,需要進(jìn)行退款
          • 每個狀態(tài)有自己的過期時間。異常訂單的撈取通過過期時間判斷。

          狀態(tài)機(jī)模式實現(xiàn)訂單狀態(tài)機(jī):

          日常開發(fā)過程中,狀態(tài)機(jī)模式應(yīng)用場景之一的就是訂單模型中的狀態(tài)控制。但是區(qū)別于狀態(tài)模式的點有以下幾個:

          • 狀態(tài)模式,所有的操作都在內(nèi)存。而訂單狀態(tài)機(jī)是要落庫的。為了防止訂單的并發(fā)操作,更新訂單的時候需要使用樂觀鎖機(jī)制。
          • 狀態(tài)模式的狀態(tài)對象是新建狀態(tài)機(jī)的時候初始化進(jìn)去的。在實際開發(fā)中,狀態(tài)對象要復(fù)用,被spring管理。
          • 而訂單狀態(tài)機(jī)對象對應(yīng)了一條數(shù)據(jù)庫中實體的訂單,是要每次從數(shù)據(jù)庫中查出來的即時新建對象,所以必須將該新建的訂單狀態(tài)機(jī)對象傳入到狀態(tài)對象中。使用狀態(tài)對象處理該訂單狀態(tài)機(jī)對象。

          以支付訂單為例:

          /*
             Title: PaymentInfo Description:
            支付訂單狀態(tài)機(jī)
             該類不可被spring管理,需要new出來,一個類就對應(yīng)一條數(shù)據(jù)庫中支付訂單記錄
             本文來自博客園,作者:wanglifeng,轉(zhuǎn)載請注明原文鏈接:https://www.cnblogs.com/wanglifeng717/p/16214122.html
             
             @author wanglifeng
           */
          
          
          public class PaymentStateMachine {
          
              // 數(shù)據(jù)庫中當(dāng)前支付訂單實體
              private SapoPayment payment;
          
              // 當(dāng)前狀態(tài)
              private PaymentState currentState;
          
              // 需要更新入庫的支付訂單實體。與payment屬性配合,payment為當(dāng)前數(shù)據(jù)庫中訂單實體,用于樂觀鎖的前置內(nèi)容校驗。
              private SapoPayment paymentForUpdate;
          
              /* 將最新內(nèi)容(含狀態(tài))更新入庫,并當(dāng)前狀態(tài)機(jī)狀態(tài) */
              public void updateStateMachine() {
          
                  // 從Spring容器中獲取操作數(shù)據(jù)的dao
                  SapoDao dao=SpringUtil.getBean(SapoDao.class);
          
                  // 更新數(shù)據(jù)庫,樂觀鎖機(jī)制:帶前置內(nèi)容數(shù)據(jù)校驗,其中payment為前置內(nèi)容,paymentForUpdate為要更新的內(nèi)容,如果更新結(jié)果=0,說明該訂單被其他線程修改過。拋異常,放棄此次修改。
                  dao.updateSapoPaymentByNull(paymentForUpdate, payment);
          
                  // 記錄訂單操作流水
                  dao.insertSapoOrderStatusLog(SapoOrderStatusLog.getInstance().setOrderId(paymentForUpdate.getId())
                          .setOrderType(SapoOrderStatusLog.ORDER_TYPE_PAYMENT).setStatus(paymentForUpdate.getStatus()));
          
                  // 更新當(dāng)前PaymentStateMachine狀態(tài)機(jī)
                  this.setPayment(paymentForUpdate);
                  this.setCurrentState(paymentForUpdate.getStatus());
              }
          
              // 通過條件獲取一個支付訂單PaymentStateMachine實體
              public static PaymentStateMachine getInstance(SapoPayment sapoPaymentForQuery) {
                  // 1.從spring容器中獲取dao;
                  SapoDao dao=SpringUtil.getBean(SapoDao.class);
          
                  // 2.查出該支付訂單
                  SapoPayment paymentResult=dao.getSapoPayment(sapoPaymentForQuery);
          
                  // 3.初始化訂單狀態(tài)機(jī)
                  PaymentStateMachine paymentStateMachine=new PaymentStateMachine();
                  paymentStateMachine.setPayment(paymentResult);
                  paymentStateMachine.setCurrentState(paymentResult.getStatus());
                  paymentStateMachine.setPaymentForUpdate(SapoPayment.getInstance(paymentResult));
          
                  return paymentStateMachine;
              }
          
              // 設(shè)置當(dāng)前狀態(tài)機(jī)的狀態(tài)。輸入數(shù)據(jù)庫中status字段,映射成對應(yīng)的狀態(tài)類實體。
              public void setCurrentState(Integer status) {
          
                  PaymentState currentState=null;
          
                  // status數(shù)字,映射成對應(yīng)的狀態(tài)類實體
                  if (SapoPayment.STATUS_APPLY.equals(status)) {
                      currentState=SpringUtil.getBean(PaymentStateApply.class);
                  } else if (SapoPayment.STATUS_WAIT_PAY.equals(status)) {
                      currentState=SpringUtil.getBean(PaymentStateWaitPay.class);
                  } else if (SapoPayment.STATUS_PAY_FINISH.equals(status)) {
                      currentState=SpringUtil.getBean(PaymentStatePayFinish.class);
                  } else if (SapoPayment.STATUS_FAIL.equals(status)) {
                      currentState=SpringUtil.getBean(PaymentStateFail.class);
                  } else if (SapoPayment.STATUS_CANCEL.equals(status)) {
                      currentState=SpringUtil.getBean(PaymentStateCancel.class);
                  } else {
                      throw new BusinessException(ResultInfo.SYS_INNER_ERROR.getCode(),
                              "status not in state machine ,status: " + status);
                  }
          
                  this.currentState=currentState;
              }
          
              // TODO 待實現(xiàn),申請支付訂單
              public void apply() {
                  // 委托給當(dāng)前狀態(tài)執(zhí)行,將當(dāng)前訂單狀態(tài)機(jī)對象傳進(jìn)去,使用狀態(tài)對象處理訂單
                  currentState.apply(this);
              }
          
              // TODO 待實現(xiàn),通知支付結(jié)果
              public void resultNotify() {
                  // 委托給當(dāng)前狀態(tài)執(zhí)行
                  currentState.resultNotify(this);
              }
          
              // TODO 同步給當(dāng)前狀態(tài)執(zhí)行
              public void sync() {
                  // 委托給當(dāng)前狀態(tài)執(zhí)行
                  currentState.sync(this);
              }
          
              // 取消訂單
              public void cancel() {
                  // 委托給當(dāng)前狀態(tài)執(zhí)行
                  currentState.cancel(this);
              }
          
          
          }


          public  interface PaymentState {
          
              public void apply(PaymentStateMachine paymentStateMachine);
          
              public void resultNotify(PaymentStateMachine paymentStateMachine);
              
              public void sync(PaymentStateMachine paymentStateMachine);
              
              public void cancel(PaymentStateMachine paymentStateMachine);
          
          }
          @Service
          public class PaymentStateApply extends BaseLogger implements PaymentState {
          
              @Autowired
              FmPayClientService fmPayClientService;
          
              @Autowired
              SapoDao dao;
          
              @Autowired
              private JacksonComponent jacksonComponent;
          
              public void apply(PaymentStateMachine paymentStateMachine) {
          
                 
          
              }
          
              public void sync(PaymentStateMachine paymentStateMachine) {
          
              }
          
              public void resultNotify(PaymentStateMachine paymentStateMachine) {
                  // TODO Auto-generated method stub
          
              }
          
              public void cancel(PaymentStateMachine paymentStateMachine) {
                  
                  SapoPayment sapoPaymentForUpdate=paymentStateMachine.getPaymentForUpdate();
                  sapoPaymentForUpdate.setStatus(SapoPayment.STATUS_CANCEL);
                  sapoPaymentForUpdate.setExpireTime(null);
                  
                  paymentStateMachine.updateStateMachine();
                  
                  
          
              }
          
          }
          @Service
          public class PaymentStateCancel extends BaseLogger implements PaymentState {
          
              public void apply(PaymentStateMachine paymentStateMachine) {
                  // TODO Auto-generated method stub
          
              }
          
              public void resultNotify(PaymentStateMachine paymentStateMachine) {
                  // TODO Auto-generated method stub
          
              }
          
              public void sync(PaymentStateMachine paymentStateMachine) {
                  // TODO Auto-generated method stub
          
              }
          
              public void cancel(PaymentStateMachine paymentStateMachine) {
                  // TODO Auto-generated method stub
          
              }
          
          }
          @Service
          public class PaymentStateFail extends BaseLogger implements PaymentState {
          
              public void apply(PaymentStateMachine paymentStateMachine) {
                  // TODO Auto-generated method stub
          
              }
          
              public void resultNotify(PaymentStateMachine paymentStateMachine) {
                  // TODO Auto-generated method stub
          
              }
          
              public void sync(PaymentStateMachine paymentStateMachine) {
                  // TODO Auto-generated method stub
          
              }
          
              public void cancel(PaymentStateMachine paymentStateMachine) {
                  throw new BusinessException(ResultInfo.SYS_INNER_ERROR.getCode(), "fail status can not cancel");
          
              }
          
          }
          @Service
          public class PaymentStatePayFinish extends BaseLogger implements PaymentState {
          
              public void apply(PaymentStateMachine paymentStateMachine) {
                  // TODO Auto-generated method stub
          
              }
          
              public void resultNotify(PaymentStateMachine paymentStateMachine) {
                  // TODO Auto-generated method stub
          
              }
          
              public void sync(PaymentStateMachine paymentStateMachine) {
                  // TODO Auto-generated method stub
          
              }
          
              public void cancel(PaymentStateMachine paymentStateMachine) {
                  throw new BusinessException(ResultInfo.SYS_INNER_ERROR.getCode(), "payfinish status can not cancel");
          
              }
          
          }
          @Service
          public class PaymentStateWaitPay extends BaseLogger implements PaymentState {
          
              @Autowired
              FmPayClientService fmPayClientService;
          
              @Autowired
              SapoDao dao;
          
              @Autowired
              private JacksonComponent jacksonComponent;
          
              public void payResultNotify() {
                  // TODO implement here
              }
          
              public void apply(PaymentStateMachine paymentStateMachine) {
                  throw new BusinessException(ResultInfo.SYS_INNER_ERROR.getCode(),
                          "applyPayPlatform not match payment state machine,currentStatus:"
                                  + paymentStateMachine.getPayment().getStatus());
          
              }
          
              public void sync(PaymentStateMachine paymentStateMachine) {
          
          
                  // TODO 過期去統(tǒng)一支付查詢
          
                  String payStatus=queryPayResultResponse.getPayStatus();
          
                  // 1:初始化輸入 2:支付中 3:支付成功 4:支付失敗 5:撤銷
                  if (QueryPayResultResponse.PAY_STATUS_INIT.equals(payStatus)) {
                      throw new BusinessException(ResultInfo.SYS_INNER_ERROR.getCode(),
                              "FMpay queryPay return init status ,we are waitpay");
                  }
          
                  if (QueryPayResultResponse.PAY_STATUS_ING.equals(payStatus)) {
                      return;
                  }
          
                  SapoPayment sapoPaymentForUpdate=paymentStateMachine.getPaymentForUpdate();
          
                  if (QueryPayResultResponse.PAY_STATUS_CANCEL.equals(payStatus)) {
                      sapoPaymentForUpdate.setStatus(SapoPayment.STATUS_CANCEL);
          
                  } else if (QueryPayResultResponse.PAY_STATUS_FAIL.equals(payStatus)) {
                      sapoPaymentForUpdate.setStatus(SapoPayment.STATUS_FAIL);
          
                  } else if (QueryPayResultResponse.PAY_STATUS_SUCCESS.equals(payStatus)) {
                      sapoPaymentForUpdate.setStatus(SapoPayment.STATUS_PAY_FINISH);
          
                  }
                  sapoPaymentForUpdate.setExpireTime(null);
          
                  paymentStateMachine.updateStateMachine();
          
              }
          
              public void resultNotify(PaymentStateMachine paymentStateMachine) {
                  // TODO Auto-generated method stub
          
              }
          
              public void cancel(PaymentStateMachine paymentStateMachine) {
                  throw new BusinessException(ResultInfo.SYS_INNER_ERROR.getCode(), "wait pay status can not cancel");
          
              }
          
          }

          文章來自https://www.cnblogs.com/wanglifeng717/p/16214122.html

          著過年放假在家復(fù)習(xí)了之前學(xué)的JS知識,用原生擼了一個購物車模塊,下面我來整理一下我的思路分享給大家。

          一、功能和效果圖

          1.1 廢話不多說,首先上個效果圖,如下:

          購物車功能效果圖

          1.2 功能介紹:

          1. 點擊全選按鈕,每一項商品的復(fù)選框處于被勾選的狀態(tài),同時計算出商品數(shù)量和商品總價;
          2. 點擊數(shù)量切換的按鈕,能自動計算出修改數(shù)量之后的商品數(shù)量和價格;
          3. 商品的總計數(shù)量和總價格應(yīng)該只計算被勾選的商品的數(shù)量和金額。

          功能介紹完畢,下面開始介紹我寫這個購物車的步驟。


          二、購物車的頁面結(jié)構(gòu)

          2.1 HTML代碼

          <table>
                  <caption>
                      購物車
                  </caption>
                  <thead>
                      <tr>
                          <!-- 全選復(fù)選框 -->
                          <th>
                              <input type="checkbox" name="checkAll" id="check-all" checked /><label for="check-all">全選</label>
                          </th>
                          <th>圖片</th>
                          <th>品名</th>
                          <th>單位</th>
                          <th>單價/元</th>
                          <th>數(shù)量</th>
                          <th>金額/元</th>
                      </tr>
                  </thead>
                  <tbody>
                      <tr>
                          <td>
                              <input type="checkbox" name="item" value="SN-1020" checked />
                          </td>
                          <td>
                              <a href=""><img src="images/p1.jpg" alt="" /></a>
                          </td>
                          <td>iPhone 11</td>
                          <td>臺</td>
                          <td class="price">4799</td>
                          <td><input type="number" min="1" value="1" /></td>
                          <td class="amount">xxxx</td>
                      </tr>
                      <tr>
                          <td>
                              <input type="checkbox" name="item" value="SN-1020" checked />
                          </td>
                          <td>
                              <a href=""><img src="images/p2.jpg" alt="" /></a>
                          </td>
                          <td>小米pro 11</td>
                          <td>部</td>
                          <td class="price">3999</td>
                          <td><input type="number" min="1" value="2" /></td>
                          <td class="amount">xxxx</td>
                      </tr>
                      <tr>
                          <td>
                              <input type="checkbox" name="item" value="SN-1030" checked />
                          </td>
                          <td>
                              <a href=""><img src="images/p3.jpg" alt="" /></a>
                          </td>
                          <td>MacBook Pro</td>
                          <td>臺</td>
                          <td class="price">18999</td>
                          <td><input type="number" min="1" value="1" /></td>
                          <td class="amount">xxxx</td>
                      </tr>
                      <tr>
                          <td>
                              <input type="checkbox" name="item" value="SN-1040" checked />
                          </td>
                          <td>
                              <a href=""><img src="images/p4.jpg" alt="" /></a>
                          </td>
                          <td>小米75電視</td>
                          <td>臺</td>
                          <td class="price">5999</td>
                          <td><input type="number" min="1" value="2" /></td>
                          <td class="amount">xxxx</td>
                      </tr>
                      <tr>
                          <td>
                              <input type="checkbox" name="item" value="SN-1050" checked />
                          </td>
                          <td>
                              <a href=""><img src="images/p5.jpg" alt="" /></a>
                          </td>
                          <td>Canon 90D單反</td>
                          <td>臺</td>
                          <td class="price">9699</td>
                          <td><input type="number" min="1" value="1" /></td>
                          <td class="amount">xxxx</td>
                      </tr>
                  </tbody>
                  <tfoot>
                      <tr style="font-weight: bolder; font-size: 1.2em">
                          <td colspan="5">總計:</td>
                          <td id="sum">xxxx</td>
                          <td id="total-amount">xxxx</td>
                      </tr>
                  </tfoot>
              </table>

          2.2 CSS代碼

          table {
              border-collapse: collapse;
              width: 90%;
              text-align: center;
              margin: auto;
          }
          
          table caption {
              margin-bottom: 15px;
              font-size: 1.5rem;
          }
          
          table th, table td {
              border-bottom: 1px solid #ccc;
              padding: 5px;
              font-weight: normal;
          }
          
          table thead tr:first-of-type {
              background-color: #e6e6e6;
              height: 3em;
          }
          
          table input[type="checkbox"] {
              width: 1.5em;
              height: 1.5em;
          }
          
          table tbody tr {
              border-bottom: 1px solid #ccc;
          }
          
          table tbody tr:hover {
              background-color: #f6f6f6;
              cursor: pointer;
          }
          
          tbody img {
              width: 3em;
          }
          
          tbody input[type="number"] {
              width: 3em;
          }
          
          button {
              width: 150px;
              height: 30px;
              outline: none;
              border: none;
              background-color: teal;
              color: white;
              letter-spacing: 5px;
          }
          
          button:hover {
              opacity: 0.7;
              cursor: pointer;
          }

          2.3 效果圖

          購物車效果圖

          以上就是一個簡單的購物車頁面的HTML和CSS樣式代碼。


          三、完成相關(guān)JS代碼

          首先,我們先完成商品的全選與取消全選的功能,所以肯定是需要拿到全選復(fù)選框元素和商品前面的復(fù)選框元素,代碼如下:

          // 獲取全選復(fù)選框,所有的商品都有一個獨(dú)立的復(fù)選框
          const checkAll=document.querySelector('#check-all');
          const checkItems=document.getElementsByName('item');

          拿到全選和每個商品的復(fù)選框元素之后,給全選框添加一個change事件,監(jiān)聽它的checked值的變化。此時全選框的checked值可以通過事件監(jiān)聽回調(diào)函數(shù)中的ev參數(shù)下的ev.target.checked拿到。

          checkALl.onchange=ev=> {
              // 如果全選框處于選中狀態(tài),ev.target.checked的值就為true,反之,為false。
              console.log(ev.target.checked);
          };

          如果想讓全選框的的狀態(tài)和每個商品前的復(fù)選框狀態(tài)保持一致,那么就使他們的checked值一致即可。因此,我們可以在全選復(fù)選框的change事件中遍歷每個商品的復(fù)選框元素。

          checkALl.onchange=ev=> {
              // 如果全選框處于選中狀態(tài),ev.target.checked的值就為true,反之,為false。
              console.log(ev.target.checked);
              checkItems.forEach(item=> item.checked=ev.target.checked);
          };

          這樣點擊全選框的時候,就可以實現(xiàn)全部選中,和取消全選的功能了。效果如圖:

          全選與取消全選

          全選和取消全選的功能完成之后,下面開始完善逐個勾選商品,直至勾選全部商品,讓全選按鈕自動變成被選中的狀態(tài)。

          要完成這個功能,我們可以通過對每個商品的復(fù)選框添加一個change事件來監(jiān)聽checked的變化。因此需要通過forEach()方法對遍歷每一個商品。

          checkItems.forEach(item=> item.onchange=ev=> {
              // 在這里處理每一項的checked值
          });

          此時,我們可以這樣考慮:當(dāng)每個商品的復(fù)選框都被勾選,即:所有商品復(fù)選框的checked的值全部為true時,全選復(fù)選框才會顯示被勾選的狀態(tài),也就是全選復(fù)選框的checked的值也要為true。

          由于checkAll的狀態(tài)依賴于每一項商品的checked值,那么可以利用一個數(shù)組函數(shù):Array.every()遍歷每一項商品,當(dāng)所有商品的checked值都為true時,every()方法的返回值就是一個true,然后再賦值給checkAll即可。注意:由于我們拿到的checkItems是一個NodeList數(shù)組,需要先將其轉(zhuǎn)換成數(shù)組后再進(jìn)行操作。

          checkItems.forEach(item=> item.onchange=ev=> {
              checkAll.checked=Array.from(checkItems).every(checkItem=> checkItem.checked);
          });

          點擊選中每個商品

          至此,全選和單選功能全部完成了。下面開始寫自動計算金額的和總數(shù)的功能。

          購物車的數(shù)量和金額不僅包含每一項商品的數(shù)量和每一項商品的總金額,還包含了計算選中的商品總數(shù),以及所有選中的商品的總金額。

          下面首先完成單個商品的總金額計算,總金額=單價 * 數(shù)量,根據(jù)這個公式,我們首先拿到商品的單價和數(shù)量元素。

          // 獲取單價組成的數(shù)組
          const priceLists=document.querySelectorAll('.price');
          // 獲取數(shù)量組成的數(shù)組
          const numberLists=document.querySelectorAll('body input[type=number]');

          以上單價(priceLists)數(shù)量(numberLists)都是NodeList類型的,需要先將它們轉(zhuǎn)換成數(shù)組,由于表單中獲取的內(nèi)容都是string類型,而參與計算的需要的是整型,所以這里需要進(jìn)行一下轉(zhuǎn)換,使用parseInt()方法即可。

          // 獲取商品單價組成的數(shù)組
          const priceLists=document.querySelectorAll('.price');
          const priceArr=Array.from(priceLists).map(item=> parseInt(item.textContent)); // [ 4799, 3999, 18999, 5999, 9699 ]
          // 獲取商品數(shù)量組成的數(shù)組
          const numberLists=document.querySelectorAll('body input[type=number]');
          const numbersArr=Array.from(numberLists).map(item=> parseInt(item.value)); // 默認(rèn)值:[ 1, 1, 1, 1, 1 ]

          注意:商品價格和商品數(shù)量在取值時有些不同。商品的單價是普通元素直接使用textContent即可拿到它內(nèi)部的值,而數(shù)量這個用的是表單控件,所以需要使用value才可以拿到值。 我剛開始寫這個功能的時候懵逼了半天,此處一定要注意。

          拿到商品的單價和數(shù)量之后就可以按照上面的公式進(jìn)行計算了,由于商品的價格和商品的數(shù)量都是一個數(shù)組,并且價格和數(shù)量在數(shù)組中都是一一對應(yīng)的關(guān)系,因此可以使用JS數(shù)組的reduce()方法進(jìn)行遍歷。

          let amountArr=[priceArr, numbersArr].reduce((prev, curr)=> {
              return prev.map((item, index)=> {
                  return item * curr[index];
              });
          });

          總感覺上述寫法有點怪怪的,是不是可以進(jìn)行簡化呢?根據(jù)箭頭函數(shù)的特征,當(dāng)只有一條返回語句的時候可以省略掉return關(guān)鍵字和大括號,因此上述方法可以簡寫成下面這樣:

          let amountArr=[priceArr, numbersArr].reduce((prev, curr)=> prev.map((item, index)=> item * curr[index]));
          console.log(amountArr); // [ 4799, 3999, 18999, 5999, 9699 ]
          

          (PS:上面的方法我一開始也沒有發(fā)現(xiàn)可以簡寫,我是把代碼發(fā)給我朋友看了之后,朋友給我點醒了。還是才疏學(xué)淺呀。)

          這時已經(jīng)計算出來了每個商品的總金額,那么我們將其渲染到頁面中。

          // 獲取單個商品總金額的元素數(shù)組
          const amountDOM=document.querySelectorAll('.amount');
          amountDOM.forEach((item, index)=> item.textContent=amountArr[index]);

          計算每個商品的金額并渲染到頁面中

          單個商品的總金額渲染到頁面之后,下面就開始計算商品的總數(shù),和總金額了。根據(jù)某東、某寶的購物車功能,我們可以發(fā)現(xiàn),總計那里統(tǒng)計的商品總數(shù)是一般是我們勾選上的商品總數(shù),總金額也是一樣的,那么我們就需要根據(jù)商品的狀態(tài)來進(jìn)行計算了。

          首先聲明一個數(shù)組,用于存儲被選中的商品的狀態(tài),如果被選中,值為1,未被選中,則為0。

          let  isChecked=[];
          checkItems.forEach(item=> isChecked.push(item.checked===true ? 1 : 0));
          // 打印出商品狀態(tài)值
          console.log(isChecked);
          • 效果如圖:

          打印商品狀態(tài)值

          商品的狀態(tài)已經(jīng)記錄好了,那么現(xiàn)在就需要統(tǒng)計選中的商品對應(yīng)的數(shù)量了。

          // 聲明一個用于存儲商品數(shù)量的數(shù)組,該數(shù)組的作用是用于與對應(yīng)的商品的狀態(tài)值的數(shù)組進(jìn)行相乘,得到實際的被選中的商品的數(shù)組。
          let checkedNumbers=[];
          numbersArr.forEach((item, index)=> checkedNumbers.push(item * isChecked[index]));
          // 打印被選中的商品的數(shù)量
          console.log(checkedNumbers);

          打印出選中的商品的數(shù)量數(shù)組

          計算出被選中的商品數(shù)量的總數(shù)并渲染到頁面中:

          let checkedSum=checkedNumbers.reduce((prev, curr)=> prev + curr);
          // 將獲取的數(shù)量結(jié)果渲染到頁面中
          document.querySelector('#sum').textContent=checkedSum;

          效果如上圖已經(jīng)出來了。

          下面開始計算被選中的商品的總金額,該總金額等于上面所有被選中的商品的總金額之和。計算出結(jié)果之后渲染到頁面中。

          // 聲明一個數(shù)組用于存儲每一個被選中的商品的總金額
          let checkedPrice=[];
          checkedNumbers.forEach((item, index)=> checkedPrice.push(item * priceArr[index]));
          // 打印被選中的每個被選中的商品總金額
          console.log(checkedPrice);
          // 計算被選中的商品總金額
          let totalAmount=checkedPrice.reduce((prev, curr)=> prev + curr);
          // 將選中的商品總金額渲染到頁面中
          document.querySelector('#total-amount').textContent=totalAmount;
          • 效果圖:

          將總金額渲染到頁面

          至此,關(guān)于計算單個商品的總金額以及被選中商品的數(shù)量和總金額的功能已經(jīng)全部完成了,但是我們還需要實現(xiàn)在頁面加載以及更改某個商品數(shù)量時自動計算的功能。那么就需要將上述的計算功能封裝成一個函數(shù),以便后面每一次執(zhí)行計算時使用。

          • 封裝后的代碼如下:
          function autoCalculate() {
              // 獲取單價組成的數(shù)組
              const priceLists=document.querySelectorAll('.price');
              const priceArr=Array.from(priceLists).map(item=> parseInt(item.textContent));
              // 獲取數(shù)量組成的數(shù)組
              const numberLists=document.querySelectorAll('body input[type=number]');
              const numbersArr=Array.from(numberLists).map(item=> parseInt(item.value));
              console.log(priceArr, numbersArr);
            
              // 由于拿到的表單里的數(shù)據(jù)都是string類型的,所以需要先將其轉(zhuǎn)換成int類型,因此需要使用`map()`方法操作一下
              let amountArr=[priceArr, numbersArr].reduce((prev, curr)=> prev.map((item, index)=> item * curr[index]));
              console.log(amountArr);
              const amountDOM=document.querySelectorAll('.amount');
              amountDOM.forEach((item, index)=> item.textContent=amountArr[index]);
          
              // 首先聲明一個數(shù)組,用于存儲被選中的商品的狀態(tài),如果被選中,值為1,未被選中,則為0
              let isChecked=[];
              checkItems.forEach(item=> isChecked.push(item.checked===true ? 1 : 0));
              console.log(isChecked);
              // 聲明一個用于存儲是商品數(shù)量的數(shù)組,該數(shù)組的作用是:如果商品處于被選中的狀態(tài),那么就存儲它真實的數(shù)量值,
              // 如果沒有被選中,那么數(shù)量就是0
              let checkedNumbers=[];
              numbersArr.forEach((item, index)=> checkedNumbers.push(item * isChecked[index]));
              console.log(checkedNumbers);
              // 此時,被選中的商品的總數(shù)為:
              let checkedSum=checkedNumbers.reduce((prev, curr)=> prev + curr);
              console.log(checkedSum);
              // 將獲取的數(shù)量結(jié)果渲染到頁面中
              document.querySelector('#sum').textContent=checkedSum;
              // 下面開始計算被選中的商品的總金額,該總金額等于上面所有被選中的商品的總金額之和。
              // 聲明一個數(shù)組用于存儲每一個被選中的商品的總金額
              let checkedPrice=[];
              checkedNumbers.forEach((item, index)=> checkedPrice.push(item * priceArr[index]));
              console.log(checkedPrice);
              // 計算被選中的商品總金額
              let totalAmount=checkedPrice.reduce((prev, curr)=> prev + curr);
              // 將選中的商品總金額渲染到頁面中
              document.querySelector('#total-amount').textContent=totalAmount;
          }

          將代碼封裝后我們會發(fā)現(xiàn),單個商品的總金額,商品總數(shù)以及總金額的值都沒了,如下圖:

          封裝代碼后的效果

          這是因為,代碼在第一次加載的時候并沒有執(zhí)行封裝后的函數(shù),因此需要加一行代碼:

          // 頁面第一次加載的時候自動執(zhí)行一次。
          window.onload=autoCalculate;

          這樣頁面中的數(shù)據(jù)在第一次加載的時候就全部都正常了。

          下面完成最后一個功能:調(diào)整商品的數(shù)量,會自動計算總數(shù)和金額。該功能還是通過change事件監(jiān)聽某個表單數(shù)據(jù)的變化來完成。效果圖下圖:

          • 代碼實現(xiàn):
          // 監(jiān)聽某個控件的事件,首先需要拿到控件元素。
          const numInput=document.querySelectorAll('body input[type=number]');
          // 上面都用了onchange來監(jiān)聽,這里換個方法使用addEventListener。
          numInput.forEach(item=> item.addEventListener('change', autoCalculate));

          但是我們會發(fā)現(xiàn)這里有個小bug,就是如果勾選沒有選中的商品,并不會自動計算商品數(shù)量和總價,原因很簡單,我們在監(jiān)聽單個商品選中和全選的時候根本就沒有執(zhí)行自動計算函數(shù),只需要在二者的事件監(jiān)聽中加上自動計算的函數(shù)即可。

          checkAll.onchange=ev=> {
              checkItems.forEach(item=> item.checked=ev.target.checked);
              // 解決勾選全選框不會自動計算的bug
              autoCalculate();
          };
          checkItems.forEach(item=> item.onchange=ev=> {
              checkAll.checked=Array.from(checkItems).every(checkItem=> checkItem.checked);
              // 解決勾選全選框不會自動計算的bug
              autoCalculate();
          });

          寫到這里,我們購物車的所有功能都已經(jīng)完成了。購物車這個模塊看似不難,其實這里面的坑也是不少的,例如:

          1. 在操作獲取的元素節(jié)點時,我們有時候需要將其轉(zhuǎn)換成一個數(shù)組才可以使用數(shù)組函數(shù)進(jìn)行操作,因為我們通過document.querySelector獲取的元素并不是一個真正的數(shù)組,而是一個類數(shù)組(NodeList);
          2. 案例中使用了多個數(shù)組函數(shù),Array.from()、Array.reduce()、Array.every()等等,由此可見,熟練掌握J(rèn)S常用的數(shù)組也是非常重要的;
          3. 掌握事件監(jiān)聽:addEventListener;
          4. 箭頭函數(shù)的簡寫方法;
          5. 表單事件的監(jiān)聽,只能通過onchange方法,千萬不要使用onclick。

          以上就是我個人在寫這個購物車功能的全部新的,由于本人也是新手,可能還有其他更簡介方便的寫法,如果有問題,請各位大佬批評指正,不勝感激。

          如果有剛開始學(xué)習(xí)JS的同學(xué),想要源碼的各位親,可以關(guān)注并私信回復(fù)“購物車”即可。

          單狀態(tài) JavaScript 資產(chǎn)

          您可以通過使用 Shopify 后臺中的自定義腳本框,將 JavaScript 添加到結(jié)帳的訂單狀態(tài)頁面(以前稱為“感謝”頁面)。

          Shopify.Checkout.OrderStatus JavaScript 資產(chǎn)可用于向訂單狀態(tài)頁面添加多種類型的內(nèi)容,包括:

          -特定產(chǎn)品的備注

          -單個發(fā)貨方式的說明

          -數(shù)字產(chǎn)品的下載鏈接。

          也可以通過 ScriptTag 訪問此 JavaScript 資源。

          如何實現(xiàn)

          Shopify.Checkout.OrderStatus 包含用于生成新內(nèi)容的函數(shù):

          addContentBox(params)

          此函數(shù)添加一個內(nèi)容框,從其中傳遞給該函數(shù)的每個參數(shù)都呈現(xiàn)為單獨(dú)的行。

          函數(shù)內(nèi)部支持 HTML5,并且您可以在函數(shù)外部使用 liquid。

          示例

          您可以在使用了特定的發(fā)貨方式時添加內(nèi)容:

          {% if checkout.shipping_method.title=='Pick-up at the store' %}

          Shopify.Checkout.OrderStatus.addContentBox(

          '

          Pick-up in store

          ',

          '

          We are open everyday from 9am to 5pm.

          '

          )

          {% endif %}

          (來源:Shopify)

          以上內(nèi)容屬作者個人觀點,不代表雨果網(wǎng)立場!如有侵權(quán),請聯(lián)系我們。

          相關(guān)鏈接:Shopify后臺怎么復(fù)制現(xiàn)有訂單?Shopify復(fù)制現(xiàn)有訂單操作一覽


          主站蜘蛛池模板: 色综合视频一区二区三区| 无码人妻一区二区三区免费手机| 国产免费无码一区二区| 在线免费一区二区| 日本一区二区三区高清| 日韩视频在线观看一区二区| 国产精品久久久久久一区二区三区| 精品日韩在线视频一区二区三区 | 国产韩国精品一区二区三区 | 无码AV中文一区二区三区| 日韩一区二区在线免费观看| 波多野结衣久久一区二区| 无码人妻精品一区二区三区夜夜嗨| 精品伦精品一区二区三区视频 | 精品一区二区三区在线观看l| 亚洲av无码一区二区乱子伦as| 国产福利电影一区二区三区,日韩伦理电影在线福 | 亚洲一区二区三区高清在线观看 | 亚洲一区二区三区91| 亚洲午夜精品一区二区| 日本不卡一区二区三区| 日本一区二区不卡在线| 一区二区免费视频| 中文字幕一区二区精品区| 中文字幕日韩一区二区三区不卡| 亚洲蜜芽在线精品一区| 精品久久一区二区三区| 日本一区二区不卡视频| 中文字幕精品一区二区日本| 日韩一区二区在线播放| 无码人妻精一区二区三区| 糖心vlog精品一区二区三区 | 久久er99热精品一区二区| 亚洲午夜在线一区| 精品日韩一区二区| 一区二区三区高清视频在线观看| 国产在线精品一区二区高清不卡 | 无人码一区二区三区视频| 久久久精品一区二区三区| 日日摸夜夜添一区| 精品乱码一区内射人妻无码|