2025-01-07 旅行分帳應用程式-步驟08

2025-01-07 旅行分帳應用程式-步驟08

好,回到比較簡單的目標 👇
我們先完成這個功能:

🔧 功能目標:顯示每位成員應付的金額(取代現在的「應付 $0」)


🧮 分帳計算邏輯簡化說明

  1. 每筆支出會由某位成員支付一筆金額。
  2. 所有支出總額,平均分配給所有人。
  3. 每個人「應付」的金額 = 平均金額 - 他實際支付的金額
  4. 「應付金額」為正,代表他還要付給其他人;為負代表他已經付比較多。

✅ 要做的步驟

  1. 計算所有人平均應付的金額。
  2. 計算每個人實際支出的總額。
  3. 計算每個人的應付金額 = 平均 - 實支。
  4. 顯示在 UI 上 {{ member.name }} <span>應付 $xx</span>

🛠️ 修改 script setup 代碼

<script setup> 區塊中加上這段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 計算每人應付金額
import { computed } from 'vue'

const memberOwedAmounts = computed(() => {
const totalAmount = expenses.value.reduce((sum, e) => sum + e.amount, 0)
const memberCount = members.value.length
const avg = memberCount > 0 ? totalAmount / memberCount : 0

// 建立一個 { [name]: 應付金額 } 的物件
const actualPaidMap = {}
members.value.forEach(m => actualPaidMap[m.name] = 0)

expenses.value.forEach(e => {
if (actualPaidMap[e.member] !== undefined) {
actualPaidMap[e.member] += e.amount
}
})

const result = {}
for (const name in actualPaidMap) {
result[name] = Math.round((avg - actualPaidMap[name]) * 100) / 100
}

return result
})

👁️‍🗨️ 修改模板中的「應付 $0」

找到這段 HTML:

1
{{ member.name }} <span class="owe">應付 $0</span>

改為:

1
2
{{ member.name }} 
<span class="owe">應付 ${{ memberOwedAmounts[member.name] ?? 0 }}</span>

🧪 測試方式

  1. 新增三位成員,例如 A、B、C。
  2. 假設 A 支出 300。
  3. 平均每人要出 100 → A 應付 -200(已多付),B/C 應付 100。

要不要下一步再幫你把「應收」顯示為綠色、「應付」顯示為紅色?還是你想先這樣?

你可以這樣修改:

  1. <template> 裡的「應付 $0」區塊改為動態顯示「應收 / 應付」
  2. 根據金額正負變化 class 來變色
  3. <style scoped> 裡新增 .credit.debt 樣式

✅ 修改 <template>(成員清單那邊)

把這段:

1
<span class="owe">應付 $0</span>

改成這段:

1
2
3
<span :class="balanceClass(member.name)">
{{ balanceText(member.name) }}
</span>

✅ 在 <script setup> 裡新增 balanceText 與 balanceClass:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 計算個別成員的淨支出金額
function getMemberBalance(name) {
const paid = expenses.value
.filter(e => e.member === name)
.reduce((sum, e) => sum + e.amount, 0)

const total = expenses.value.reduce((sum, e) => sum + e.amount, 0)
const average = members.value.length > 0 ? total / members.value.length : 0

return paid - average
}

// 回傳文字描述
function balanceText(name) {
const balance = getMemberBalance(name)
if (balance > 0) return `應收 $${balance.toFixed(2)}`
if (balance < 0) return `應付 $${Math.abs(balance).toFixed(2)}`
return '結清'
}

// 回傳對應的 class
function balanceClass(name) {
const balance = getMemberBalance(name)
if (balance > 0) return 'credit'
if (balance < 0) return 'debt'
return ''
}

✅ 在 <style scoped> 裡新增樣式:

1
2
3
4
5
6
7
8
9
.credit {
color: green;
font-weight: bold;
}

.debt {
color: red;
font-weight: bold;
}

這樣就可以依照支出情況,讓成員旁邊顯示「應收 $」為綠色,「應付 $」為紅色,「結清」不加樣式。

如果你希望我幫你一起整合進整份 code,我也可以幫你貼上完整版 😄
要嗎?