# 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');
```