# 飲料點餐版面(Vue最終進階版) ###### codepen在這邊:https://codepen.io/liu_0821/pen/GRBrwzW?editors=0011 ![](https://i.imgur.com/YuIE3eA.jpg) ## 前情提要 ###### 前面的版本在這邊~~ [**版本1**](https://hackmd.io/@LindaLiu/HkTU3gSqi) [**版本2**](https://hackmd.io/@LindaLiu/ryBeOxt9i) ##### 修修改改終於來到最後一版,目前增加的內容一定會有更好的寫法(Computed),但未來的路還很長,想說目前先改這樣,等後續再來做修改d(`・∀・)b #### 額外增加的內容 ##### 1. 點選品項下方的訂單會隱藏起來~ ##### 2. 個別修改訂單資料~ #### 目前先這樣~一樣下方都有註解,直接看下方程式碼瞜 ## HTML ``` <div id="app"> <div class="container gx-2"> <div class="row gx-3 bg-light py-3"> <div class="col-md-4"> <div class="list-group"> <a href="#" class="list-group-item list-group-item-action" @click.prevent="selectedItem(item)" v-for="(item,key) in products" :key="item.name" :class="{'active':selectProduct.name === item.name}" > <h6 class="card-title mb-1">{{item.name}}</h6> <div class="d-flex align-items-center justify-content-between" > <p class="mb-0"><small>{{item.engName}}</small></p> <p class="mb-0"><small>NT$ {{item.price}}</small></p> </div> </a> </div> </div> <div class="col-md-8"> <div class="card mb-2"> <div v-if="!selectProduct.name " class="position-absolute text-white d-flex align-items-center justify-content-center" style=" top: 0; bottom: 0; left: 0; right: 0; background-color: rgba(0, 0, 0, 0.65); z-index: 100; " > 請先選擇飲品 </div> <div class="card-body px-4"> <div class="mb-3"> <label for="productNum" class="form-label">數量</label> <input type="number" class="form-control" id="productNum" placeholder="數量" min="1" v-model="selectProduct.count" /> </div> <div class="mb-3"> <label for="productNum" class="form-label d-block" >冰塊*</label > <div class="form-check form-check-inline" v-for="(item,key) in iceType" :key="'ice'+ key" > <input class="form-check-input" name="iceType" type="radio" :value="item" :id="'ice'+ key" v-model="selectProduct.ice" :disabled="!selectProduct.hasOwnProperty('defaults') || (selectProduct.defaults.ice !== '' && selectProduct.defaults.ice !== item)" /> <!--hasOwnProperty 測試屬性是否存在--> <label class="form-check-label" :for="'ice'+ key" >{{item}}</label > </div> </div> <div class="mb-3"> <label for="productNum" class="form-label d-block" >甜度*</label > <div class="form-check form-check-inline" v-for="(item,key) in sugarType" :key="'sugar'+ key" > <input class="form-check-input" name="sugarType" type="radio" :id="'sugar'+ key" :value="item" v-model="selectProduct.sugar" :disabled="!selectProduct.hasOwnProperty('defaults') || (selectProduct.defaults.sugar !== '' && selectProduct.defaults.sugar !== item)" /> <label class="form-check-label" :for="'sugar'+ key" >{{item}}</label > </div> </div> <div class="mb-3"> <label for="productNum" class="form-label d-block" >加料</label > <div class="form-check form-check-inline" v-for="(item,key) in toppingsType" :key="'topping'+key" > <input class="form-check-input" type="checkbox" :id="'topping'+key" :value="item" v-model="selectProduct.topping" :disabled="!selectProduct.hasOwnProperty('defaults') || selectProduct.defaults.toppings.includes(item)" /> <label class="form-check-label" :for="'topping'+key" >{{item}}</label > </div> </div> <div class="mb-3"> <label for="productNotice" class="form-label" >備註</label > <textarea class="form-control" id="productNotice" rows="2" v-model="selectProduct.text" ></textarea> </div> <div class="d-flex gap-2"> <button class="btn btn-outline-primary w-100" type="button" > 取消 </button> <button class="btn btn-primary w-100" @click.prevent="addToShoppingCart(selectProduct)" type="button" > 加入 </button> </div> </div> </div> <div class="card"> <div class="card-body"> <table class="table"> <thead> <tr> <th scope="col">品項</th> <th scope="col">冰塊</th> <th scope="col">甜度</th> <th scope="col">加料</th> <th scope="col">單價</th> <th scope="col">數量</th> <th scope="col">小計</th> <th scope="col">修改</th> <th scope="col"></th> </tr> </thead> <tbody> <tr v-for="(item,key) in orderList" :key="'data'+key"> <th scope="row"> {{item.name}}<br /> <small class="text-muted fw-normal" >備註:{{item.text}}</small > </th> <td>{{item.ice}}</td> <td>{{item.sugar}}</td> <td>{{item.topping.toString()}}</td> <td>{{item.calculatePrice}}</td> <td>{{item.count}}</td> <td>{{item.calculatePrice*item.count}}</td> <td class="text-end"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil-square editIcon" viewBox="0 0 16 16" @click.prevent="editOrder(item)" > <path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z" /> <path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z" /> </svg> </td> <td class="text-end"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x delIcon" viewBox="0 0 16 16" @click.prevent="deleteOrder(item)" > <path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z" /> </svg> </td> </tr> </tbody> </table> <p class="text-end">共 NT$ {{totalPrice}} 元</p> <div class="d-flex gap-2"> <button class="btn btn-sm btn-outline-secondary w-100" @click.prevent="clearOrder" > 清空購物車 </button> <button class="btn btn-sm btn-secondary w-100" @click.prevent="outputOrder(orderList,totalPrice)" :disabled="orderList.length === 0" > 產生訂單 </button> </div> </div> </div> </div> </div> </div> <div class="bg-light p-3 mt-3" v-if="checkedOrder.orders.length >0 " > <div class="bg-white p-3 d-flex flex-column" style="min-height: 450px" > <table class="table"> <thead> <tr> <th scope="col">品項</th> <th scope="col">冰塊</th> <th scope="col">甜度</th> <th scope="col">加料</th> <th scope="col">單價</th> <th scope="col">數量</th> <th scope="col">小計</th> </tr> </thead> <tbody> <tr v-for="(item,id) in checkedOrder.orders" :key="'id'+id"> <th scope="row"> {{item.name}}<br /> <small class="text-muted fw-normal" >備註:{{item.text}}</small > </th> <td>{{item.ice}}</td> <td>{{item.sugar}}</td> <td>{{item.topping.toString()}}</td> <td>{{item.calculatePrice}}</td> <td>{{item.count}}</td> <td class="text-end">{{item.calculatePrice*item.count}}</td> </tr> </tbody> </table> <p class="mt-3 mb-1">訂單成立時間:{{checkedOrder.date}}</p> <p class="mb-1">餐點數: {{checkedOrder.orderCount}}</p> <p class="mb-1">付款狀態:未付款</p> <p class="text-end mt-auto"> 共 NT$ {{checkedOrder.checkOrderPrice}} 元 </p> </div> </div> </div> ``` ## JS(Vue) ###### 這邊避免太長太拖(? 直接放上我有改的部分 (。◕∀◕。) > `selectProduct` 放被選擇的項目 `orderList` 購物車 `totalPrice` 總價 `checkedOrder` 結帳 `calculatePrice` 計算金額 **data的部分** ``` // 在原本的資料結構上有更動 selectProduct: { id: "", -> 判斷修改還是新增的依據 calculatePrice:0, -> 計算小計 }, orderList: [], checkedOrder: { orders: [], date: "", orderCount: 1, checkOrderPrice: "", } ``` **計算價格** ``` addToShoppingCart(selectProduct) { /* 把小計的部份另外拉出來算 */ this.selectProduct.calculatePrice = this.selectProduct.price + this.selectProduct.topping.length * 10; if (!this.selectProduct.id) { this.selectProduct.id = new Date().getTime(); const order = { ...selectProduct, total:0 -> 預設 }; this.orderList.push(order); } else { this.orderList.map((item, i) => { if (item.id === this.selectProduct.id) { this.orderList[i] = this.selectProduct; } }); } this.countTotal(); this.resetOrder(); // 加入購物車後清空 } ``` ``` countTotal() { this.totalPrice = 0; this.orderList.forEach((item) => { // 計算整筆訂單的總價格(暴力拆解?) this.totalPrice += (item.price + item.topping.length*10)*item.count; }); }, ``` ## 後續補充 ##### 有發現一些小問題有在做重新調整,以及有一些建議把它紀錄下來 * ##### 更新訂單`outputOrder()`時建議如下的寫法,除了更為精簡,其次是創造一筆全新的資料保持資料不變 (immutable) (這就比較偏架構程式的方法,並沒有對錯)。 ##### 補充資料: ##### [前端的 immutable 設計樣式](https://blog.yyisyou.tw/c30df041/) --- #### ヽ(∀゚ )人(゚∀゚)人( ゚∀)人(∀゚ )人(゚∀゚)人( ゚∀)ノヽ(∀゚ )人(゚∀゚)人( ゚∀)人(∀゚ )人(゚∀゚)人( ゚∀)人 ##### 以上 如果註解哪裡有錯誤或有問題,歡迎提出來一起討論~~~~