mirror of
https://github.com/henry4682/fin_buddy_fe.git
synced 2026-07-16 01:30:00 +00:00
initial project
This commit is contained in:
143
src/Pages/Dashboard.vue
Normal file
143
src/Pages/Dashboard.vue
Normal file
@@ -0,0 +1,143 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from "vue"; // 💡 補上 ref, onMounted
|
||||
import axios from "axios"; // 💡 引入你的 axios 或全域 API 工具
|
||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue'
|
||||
import { Pie, Bar } from 'vue-chartjs'
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
ArcElement,
|
||||
Tooltip,
|
||||
Legend,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
BarElement,
|
||||
} from 'chart.js'
|
||||
|
||||
ChartJS.register(ArcElement, Tooltip, Legend, CategoryScale, LinearScale, BarElement)
|
||||
|
||||
// 🟢 響應式狀態,初始為預設值,防止載入中畫面崩潰
|
||||
const monthlyTotal = ref(0)
|
||||
const monthlyCount = ref(0)
|
||||
const categoryStats = ref([])
|
||||
const monthlyTrend = ref([])
|
||||
const loading = ref(true)
|
||||
|
||||
// 圓餅圖資料
|
||||
const pieData = computed(() => ({
|
||||
labels: categoryStats.value.map(c => c.name),
|
||||
datasets: [{
|
||||
data: categoryStats.value.map(c => c.total),
|
||||
backgroundColor: categoryStats.value.map(c => c.color),
|
||||
}],
|
||||
}))
|
||||
|
||||
const pieOptions = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: { position: 'bottom' },
|
||||
},
|
||||
}
|
||||
|
||||
// 長條圖資料
|
||||
const barData = computed(() => ({
|
||||
labels: monthlyTrend.value.map(m => m.month),
|
||||
datasets: [{
|
||||
label: '每月花費',
|
||||
data: monthlyTrend.value.map(m => m.total),
|
||||
backgroundColor: '#3B82F6',
|
||||
borderRadius: 4,
|
||||
}],
|
||||
}))
|
||||
|
||||
const barOptions = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
callback: (val) => '$' + val.toLocaleString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// 🟢 組件掛載時,主動去向後端 API 撈取統計數據
|
||||
onMounted(async () => {
|
||||
try {
|
||||
// 🟢 修正:精準指定後端 Laragon 的完整 API 網址
|
||||
const response = await axios.get('http://127.0.0.1:8000/api/dashboard/stats', {
|
||||
// 如果你是用 localStorage 存 token 的話,要在這邊帶上 Bearer Token:
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem('auth_token')}` // 💡 依據你儲存 Token 的 key 調整
|
||||
},
|
||||
withCredentials: true // 如果是用 Cookie/Sanctum 驗證則需要這行
|
||||
})
|
||||
|
||||
monthlyTotal.value = response.data.monthlyTotal
|
||||
monthlyCount.value = response.data.monthlyCount
|
||||
categoryStats.value = response.data.categoryStats
|
||||
monthlyTrend.value = response.data.monthlyTrend
|
||||
} catch (error) {
|
||||
console.error("無法加載總覽數據:", error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AuthenticatedLayout>
|
||||
<template #header>
|
||||
<h2 class="text-xl font-semibold">總覽</h2>
|
||||
</template>
|
||||
|
||||
<div class="py-8 max-w-5xl mx-auto px-4 space-y-6">
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||
<div class="bg-white rounded-lg shadow p-6">
|
||||
<p class="text-sm text-gray-500">本月花費</p>
|
||||
<p class="text-3xl font-bold text-blue-500 mt-1">
|
||||
${{ monthlyTotal.toLocaleString() }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg shadow p-6">
|
||||
<p class="text-sm text-gray-500">本月記帳筆數</p>
|
||||
<p class="text-3xl font-bold text-gray-700 mt-1">
|
||||
{{ monthlyCount }} <span class="text-lg font-normal text-gray-500">筆</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg shadow p-6">
|
||||
<p class="text-sm text-gray-500">最高花費分類</p>
|
||||
<p class="text-3xl font-bold text-gray-700 mt-1">
|
||||
{{ categoryStats.length > 0
|
||||
? categoryStats.reduce((a, b) => a.total > b.total ? a : b).name
|
||||
: '—' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-6">
|
||||
<div class="bg-white rounded-lg shadow p-6">
|
||||
<h3 class="text-lg font-medium mb-4">本月分類佔比</h3>
|
||||
<div v-if="categoryStats.length > 0" class="max-w-xs mx-auto">
|
||||
<Pie :data="pieData" :options="pieOptions" />
|
||||
</div>
|
||||
<div v-else class="py-12 text-center text-gray-400">
|
||||
本月還沒有記錄
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg shadow p-6">
|
||||
<h3 class="text-lg font-medium mb-4">近6個月趨勢</h3>
|
||||
<Bar :data="barData" :options="barOptions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</AuthenticatedLayout>
|
||||
</template>
|
||||
Reference in New Issue
Block a user