iT邦幫忙

2024 iThome 鐵人賽

DAY 25
0
Software Development

我獨自開發 - 30天進化之路,掌握 Laravel + Nuxt系列 第 25

D25 - 實作報表功能:建立財務報表與圖表呈現

  • 分享至 

  • xImage
  •  

大家好!在前面的文章中,我們已經完成了交易紀錄管理、銀行帳戶管理和分類管理的功能。

今天,我們將繼續完善我們的個人財務管理系統,專注於報表功能的開發。

報表功能是財務管理系統中非常重要的一環,透過報表,我們可以直觀地了解自己的財務狀況,分析收入與支出的趨勢,從而做出更明智的財務決策。

接下來,我們將實作以下內容:

  • 建立報表頁面,呈現財務數據的概況
  • 使用圖表庫來視覺化地展示數據(例如:Bar Chart、Pie Chart)
  • 實現按時間範圍、分類等條件篩選報表數據

一、準備工作

1. 安裝圖表庫

我們將使用 Chart.js 來實現圖表的呈現。首先,安裝相關的套件。

npm install vue-chartjs chart.js

2. 建立報表頁面檔案

在 pages/ 目錄下建立 reports.vue 檔案。

touch pages/reports.vue

二、建立報表頁面

1. 基本頁面結構

在 reports.vue 中,我們建立基本的頁面結構。

<template>
  <div>
    <h2 class="text-2xl font-bold mb-4">財務報表</h2>
    <div v-if="error" class="text-red-500">
      {{ error }}
    </div>
    <div v-else>
      <!-- 篩選條件 -->
      <div class="mb-4">
        <label>選擇日期範圍:</label>
        <input type="date" v-model="startDate" /> - 
        <input type="date" v-model="endDate" />
        <button @click="fetchReportData" class="bg-blue-600 text-white px-4 py-2 ml-2">更新報表</button>
      </div>
      <!-- 報表圖表 -->
      <div>
        <canvas id="incomeExpenseChart"></canvas>
      </div>
      <!-- 分類支出圓餅圖 -->
      <div class="mt-8">
        <canvas id="categoryExpenseChart"></canvas>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { Chart } from 'chart.js/auto'

const error = ref(null)
const startDate = ref('')
const endDate = ref('')
let chartInstance = null
let pieChartInstance = null

const fetchReportData = async () => {
  error.value = null
  try {
    const data = await fetchReportAPI()
    updateChart(data)
    updatePieChart(data.categoryExpense)
  } catch (err) {
    error.value = '無法獲取報表資料,請稍後再試。'
    console.error('Error fetching report data:', err)
  }
}

const fetchReportAPI = async () => {
  const params = {}
  if (startDate.value) params.start_date = startDate.value
  if (endDate.value) params.end_date = endDate.value

  const response = await $fetch('/api/reports', {
    method: 'GET',
    params,
  })

  if (response.status === 'success') {
    return response
  } else {
    throw new Error(response.message || '無法獲取報表資料')
  }
}

const updateChart = (data) => {
  const ctx = document.getElementById('incomeExpenseChart').getContext('2d')
  if (chartInstance) {
    chartInstance.destroy()
  }
  chartInstance = new Chart(ctx, {
    type: 'bar',
    data: {
      labels: data.labels,
      datasets: [
        {
          label: '收入',
          data: data.income,
          backgroundColor: 'rgba(75, 192, 192, 0.5)',
        },
        {
          label: '支出',
          data: data.expense,
          backgroundColor: 'rgba(255, 99, 132, 0.5)',
        },
      ],
    },
    options: {
      responsive: true,
      scales: {
        x: {
          stacked: true,
        },
        y: {
          stacked: true,
        },
      },
    },
  })
}

const updatePieChart = (categoryData) => {
  const ctx = document.getElementById('categoryExpenseChart').getContext('2d')
  if (pieChartInstance) {
    pieChartInstance.destroy()
  }
  pieChartInstance = new Chart(ctx, {
    type: 'pie',
    data: {
      labels: categoryData.labels,
      datasets: [
        {
          data: categoryData.data,
          backgroundColor: categoryData.colors,
        },
      ],
    },
    options: {
      responsive: true,
    },
  })
}

onMounted(() => {
  const today = new Date()
  startDate.value = new Date(today.getFullYear(), today.getMonth(), 1).toISOString().substr(0, 10)
  endDate.value = today.toISOString().substr(0, 10)
  fetchReportData()
})
</script>

<style scoped>
/* 頁面專屬的樣式 */
</style>

2. 說明

  • 篩選條件:提供開始日期和結束日期的選擇,使用者可以自訂報表的時間範圍。
  • 圖表呈現:使用 Chart.js 繪製收入與支出的柱狀圖和分類支出圓餅圖。
  • 資料取得:在 fetchReportData 方法中,使用 Nuxt 3 推薦的 $fetch 方法調用後端 API 獲取報表數據。

三、後端 API 實作(簡述)

補充後端的部分,後端 API 的實作這裡簡要說明。

1. 建立報表 API 路由

在後端框架中(例如 Laravel),新增一個報表的 API 路由。

Route::middleware('auth:api')->get('/reports', [ReportController::class, 'index']);

2. 實作 ReportController

在 ReportController 中,根據日期範圍,查詢交易紀錄,並按月彙總收入與支出。

public function index(Request $request)
{
    $user = $request->user();

    $startDate = $request->query('start_date');
    $endDate = $request->query('end_date');

    $transactions = Transaction::where('user_id', $user->id)
        ->whereBetween('date', [$startDate, $endDate])
        ->get()
        ->groupBy(function($item) {
            return Carbon::parse($item->date)->format('Y-m');
        });

    $labels = [];
    $incomeData = [];
    $expenseData = [];

    foreach ($transactions as $month => $items) {
        $labels[] = $month;
        $income = $items->where('type', 'income')->sum('amount');
        $expense = $items->where('type', 'expense')->sum('amount');
        $incomeData[] = $income;
        $expenseData[] = $expense;
    }

    // 新增分類支出數據
    $categoryExpenseData = Transaction::where('user_id', $user->id)
        ->whereBetween('date', [$startDate, $endDate])
        ->where('type', 'expense')
        ->groupBy('category_id')
        ->selectRaw('category_id, SUM(amount) as total')
        ->get();

    $categoryLabels = [];
    $categoryData = [];
    $categoryColors = [];

    foreach ($categoryExpenseData as $item) {
        $category = Category::find($item->category_id);
        if ($category) {
            $categoryLabels[] = $category->name;
            $categoryData[] = $item->total;
            $categoryColors[] = '#' . substr(md5($category->name), 0, 6); // 隨機顏色
        }
    }

    return response()->json([
        'status' => 'success',
        'labels' => $labels,
        'income' => $incomeData,
        'expense' => $expenseData,
        'categoryExpense' => [
            'labels' => $categoryLabels,
            'data' => $categoryData,
            'colors' => $categoryColors,
        ],
    ]);
}

說明:

  • 根據日期範圍篩選交易紀錄。
  • 按月份彙總收入與支出。
  • 提取分類支出的數據,用於圓餅圖。
  • 返回的數據包括 labels(月份)、income(收入數據)、expense(支出數據)、categoryExpense(分類支出數據)。

四、優化圖表呈現

1. 添加分類支出圓餅圖

我們已經在報表頁面中,添加了一個圓餅圖,顯示各分類的支出比例。

2. 說明

  • 更新圖表方法:在 fetchReportData 方法中,取得數據後,同時更新柱狀圖和圓餅圖。
  • 圖表更新邏輯:在 updateChart 和 updatePieChart 方法中,分別處理柱狀圖和圓餅圖的繪製。

五、測試與驗證

1. 測試報表頁面

  • 登入應用程式,點擊導航列中的「報表」連結。
  • 應該看到報表頁面,預設顯示本月的數據。
  • 確認收入與支出柱狀圖正確呈現。

2. 測試篩選功能

  • 更改日期範圍,點擊「更新報表」按鈕。
  • 確認圖表根據新的日期範圍更新。

3. 測試分類圓餅圖

  • 確認圓餅圖正確顯示各分類的支出比例。
  • 確認圖例和顏色對應正確。

小結

今天,我們成功地實作了報表功能,建立了財務報表頁面,並使用圖表視覺化地呈現數據。

  • 如何在 Vue 中使用 Chart.js 來繪製圖表。
  • 如何設計後端 API,提供前端所需的數據。
  • 如何處理數據,並在圖表中正確呈現。

最後

大部分的實作也都告一段落了,剩下就是做細節的調整,其實前面的內容,拿來任何一個人都可以直接照著copy,但是要走到與別人不一樣的道路上,對於細節的著墨就要更講究,甚至是一個讓人可以買單的服務或是產品。


上一篇
D24 - 從 Options API 轉換到 Composition API:關鍵知識點解析
下一篇
D26 - 第三階段驗收:功能總結與未來規劃
系列文
我獨自開發 - 30天進化之路,掌握 Laravel + Nuxt30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言