# Vue3 購物車-1列表 ## 購物清單 1. 渲染購物清單,在要放迴圈的範圍加上v-for 2. 官方說 加個key會比較好各自操作 => :key 3. 單純顯示資料,用 {{ }} 裝參數。如果要做四則運算ok {{a+b}}。 但不要額外加其他字串做合併,渲染會出問題。像是圖片路徑加資料夾。所以路徑的字串都要直接給完整。 5. 如果有綁屬性,則要在屬性前加「:」 => :alt, :src 不需再用{{ }} 包住 6. 按扭事件如click, change,則加「@」 => @click, @change 7. v-if 的結果 都是true/false,控制是否顯示 8. 購買數量比較特殊,沒有用v-model雙向綁定 改用@change處理,裡面有$event.target.value 這是數量確認更新後所取得的資料,相當於jQuery的onchange,也就是資料key完,按「enter」後的結果。單純用item.qty會是取得修改前資料 *如果是用v-model則是即時處理(一個指令一個動作),如果改資料是先刪再打,再刪完後沒按「enter」,資料是空的=0,商品就刪除了。 ```html= <table> <thead> <tr> <th>商品名稱</th> <th>購買數量</th> <th>小計</th> <th v-if="del_func">刪除</th> </tr> </thead> <tbody> <tr v-for="(item, ind) in cart_list" :key="item.row"> <td> <div> <img :alt="item.name" :src="item.img" :title="item.name"> <p>{{item.name}}</p> </div> </td> <td data-th="購買數量" v-if="del_func"> <div> <button @click="edit(item.row, 'up')">+</button> <input type="number" :value="item.qty" @change="edit(item.row, $event.target.value)"> <button @click="edit(item.row, 'down')">-</button> </div> </td> <td data-th="購買數量" v-else> {{item.qty}} </td> <td data-th="小計">{{ item.price * item.qty }}</td> <td data-th="刪除" v-if="del_func"><i @click="edit(item.row, 'del')"></i>del</td> </tr> <tr v-if="feight > 0"> <td>運費</td> <td>{{feight}}</td> </tr> <tr> <td>總計</td> <td>{{total + feight}}</td> </tr> </tbody> </table> ``` ## 購物操作 後面則是 vue的操作 1. 結構順序都是 data, computed, methods, mounted 2. 要綁定的變數都打在data return裡面 3. computed 會做自動運算,但不可代入任何參數。像是計算總額。 表示的方式跟一般data的一樣,純顯示用途 => {{ }} *比較特別的是,computed 裡面用的方法,不可出現在data裡面 4. 事件都寫在methods裡面,不過他可以代參數。像是對商品變更數量 methods edit,會更新到cart_list。而數量會影響總金額,所以連帶觸發到computed total的計算,總金額的計算還包括了運費的判斷。 5. mounted 是用來初始化要做的事,這邊是寫一個初始化購物車列單 6. 裡面有額外cdn載入axios,用來處理ajax更新至系統,寫法更為簡潔 7. del_func是控制是否要開放變更數量 ```javascript= const app = { data() { return { feed: 0, feight: 0, un_cost: 0, cart_list: [], del_func: true, } }, computed: { total() { let total = 0; let items = 0; this.cart_list.forEach((i) => { items+= Number(i.qty); total+= i.qty * i.price; }); this.feight = 0; if (total < this.un_cost) this.feight = this.feed; return total; }, }, methods: { edit(id, qty) { let q = 0; this.cart_list.forEach((i, ind) => { if (id == i.row) { switch(qty) { case 'up': i.qty++; q = i.qty; break; case 'down': q = i.qty-1; i.qty--; if (q == 0) this.cart_list.splice(ind, 1); break; case 'del': this.cart_list.splice(ind, 1); break; default: i.qty = qty; q = qty; if (qty <= 0) this.cart_list.splice(ind, 1); } } }); if (this.cart_list.length == 0) location.href = '/'; }, carts() { axios.get('/cart/cart_list') .then((all) => { let rs = all.data; this.cart_list = rs.cart_list; if (rs.cart_list.length == 0) location.href = '/'; }); }, }, mounted() { this.carts(); } } Vue.createApp(app).mount('#app'); ```