2025-01-03 旅行分帳應用程式-步驟05

2025-01-03 旅行分帳應用程式-步驟05

好的!我們現在來進行以下的更新:


你要的新功能改動摘要:

中間區域分成兩邊:

  • 左邊:成員清單(原本就有)
  • 右邊:支出紀錄列表
    • 顯示:付款人、金額、支出項目名稱
    • 每筆帳款旁邊有一個 ❌ 刪除按鈕

我幫你修改整個排版、資料結構,並加上支出清單與刪除功能。


🔧 Vue Template:更新後程式碼

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<template>
<div class="app-container">
<!-- 上方標題 -->
<div class="header">
<h1>旅遊分帳應用程式</h1>
</div>

<!-- 中間:分成左右 -->
<div class="middle-section">
<!-- 左:成員列表 -->
<div class="member-list" ref="memberListRef">
<h2>分帳成員</h2>
<ul>
<li v-for="(member, index) in members"
:key="index" @click="selectMember(index)"
:class="{ selected: selectedMemberIndex === index }">
{{ member.name }} <span class="owe">應付 $0</span>
<div v-if="selectedMemberIndex === index" class="add-expense-section">
<button class="btn small-btn" @click.stop="showAddExpense = true">➕ 新增支出款項</button>
</div>
</li>
</ul>
</div>

<!-- 右:帳款列表 -->
<div class="expense-list">
<h2>支出清單</h2>
<div v-if="expenses.length === 0">尚無支出紀錄</div>
<ul v-else>
<li v-for="(expense, index) in expenses" :key="index">
<strong>{{ expense.member }}</strong> 支出 <strong>${{ expense.amount }}</strong> - {{ expense.category }}
<button class="btn delete-btn" @click="deleteExpense(index)">❌</button>
</li>
</ul>
</div>
</div>

<!-- 底部:新增成員 -->
<div class="bottom-section">
<input
v-model="newMemberName"
class="member-input"
placeholder="輸入成員姓名"
@keyup.enter="addMember"
/>
<button class="btn add-btn" @click="addMember">➕ 新增成員</button>
</div>

<!-- Modal: 新增支出款項 -->
<div v-if="showAddExpense" class="modal-backdrop">
<div class="modal">
<h3>新增支出</h3>
<label>支出項目:</label>
<input
list="category-options"
v-model="selectedCategory"
placeholder="輸入或選擇項目"
/>
<datalist id="category-options">
<option v-for="cat in expenseCategories" :key="cat" :value="cat" />
</datalist>

<label>金額:</label>
<input
v-model.number="expenseAmount"
type="number"
placeholder="輸入金額"
/>

<div class="modal-buttons">
<button class="btn confirm" @click="addExpense">確認</button>
<button class="btn cancel" @click="showAddExpense = false">取消</button>
</div>
</div>
</div>
</div>
</template>

🎯 Script 區

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<script setup>
import { ref, nextTick } from 'vue'

const members = ref([])
const newMemberName = ref('')
const memberListRef = ref(null)
const selectedMemberIndex = ref(null)
const showAddExpense = ref(false)

// 支出項目選項
const expenseCategories = ref(['餐飲', '住宿', '門票', '交通', '其他'])
const selectedCategory = ref('')
const expenseAmount = ref(0)

// 支出清單資料
const expenses = ref([])

// ➕ 新增成員
function addMember() {
const name = newMemberName.value.trim()
if (name !== '' && !members.value.some(m => m.name === name)) {
members.value.push({ name })
newMemberName.value = ''
nextTick(() => {
const el = memberListRef.value
if (el) el.scrollTop = el.scrollHeight
})
}
}

// ✋ 選取成員
function selectMember(index) {
if (selectedMemberIndex.value === index) {
selectedMemberIndex.value = null
showAddExpense.value = false
} else {
selectedMemberIndex.value = index
showAddExpense.value = false
}
}

// 💰 新增支出
function addExpense() {
const member = members.value[selectedMemberIndex.value]
const trimmedCategory = selectedCategory.value.trim()

if (member && trimmedCategory && expenseAmount.value > 0) {
expenses.value.push({
member: member.name,
category: trimmedCategory,
amount: expenseAmount.value
})

if (!expenseCategories.value.includes(trimmedCategory)) {
expenseCategories.value.push(trimmedCategory)
}

selectedCategory.value = ''
expenseAmount.value = 0
showAddExpense.value = false
} else {
alert('請輸入有效的項目與金額')
}
}

// ❌ 刪除支出
function deleteExpense(index) {
expenses.value.splice(index, 1)
}
</script>

💄 Style 更新(加上左右分欄)

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
<style scoped>
.app-container {
max-width: 800px;
margin: auto;
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
height: 100vh;
justify-content: space-between;
}

.header {
font-size: 1.5em;
font-weight: 600;
color: #5e3a4a;
text-align: center;
padding: 10px;
background-color: #f8e6e6;
border-bottom: 2px solid #d16a7b;
}

.middle-section {
flex-grow: 1;
display: flex;
padding: 10px;
gap: 10px;
overflow-y: auto;
}

.member-list, .expense-list {
flex: 1;
background: #fdfdfd;
border: 1px solid #ddd;
border-radius: 8px;
padding: 10px;
overflow-y: auto;
}

.member-list ul, .expense-list ul {
list-style: none;
padding: 0;
}

li {
margin: 5px 0;
background: #f8f8f8;
padding: 8px;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}

.owe {
color: red;
}

.bottom-section {
padding: 10px;
background-color: #f8e6e6;
border-top: 2px solid #d16a7b;
display: flex;
gap: 10px;
align-items: center;
justify-content: center;
}

.member-input {
flex: 1;
min-width: 200px;
padding: 10px;
font-size: 16px;
border-radius: 5px;
border: 1px solid #ccc;
}

.add-btn {
padding: 10px 20px;
}

.btn {
background-color: #d16a7b;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}

.btn:hover {
background-color: #b85967;
}

.selected {
background-color: #ffe6e6;
}

.small-btn {
margin-top: 8px;
font-size: 14px;
padding: 6px 10px;
}

.delete-btn {
background-color: #f44336;
padding: 4px 8px;
font-size: 14px;
}

.modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.4);
display: flex;
justify-content: center;
align-items: center;
}

.modal {
background-color: white;
padding: 20px;
border-radius: 10px;
min-width: 300px;
text-align: center;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
}

.modal input {
width: 80%;
padding: 8px;
margin: 10px 0;
border-radius: 5px;
border: 1px solid #ccc;
}

.modal-buttons {
display: flex;
justify-content: space-between;
gap: 10px;
}
</style>

這樣畫面就會是左右分佈:

  • 👈 左邊:成員清單(點選可新增支出)
  • 👉 右邊:支出紀錄,每筆有付款人、金額、名稱、刪除按鈕

要不要我再幫你加上:

  1. 金額統計?
  2. 自動算出誰該付誰多少?

你先看看這版對不對勁,我再幫你擴充 💪