iT邦幫忙

2025 iThome 鐵人賽

DAY 25
0

前面示範了套版的改造,今天要說明一個常見需求──匯出 Excel。需求很單純:查詢資料後,將結果匯出為 Excel。我們預計達成以下目標:

  • 在前端查詢畫面新增「匯出」按鈕。
  • 點擊匯出後,向後端取得資料。
  • 前端收到回應後,將資料匯出為 Excel,提供使用者下載。

前端

  1. 開啟 Vue 頁面,在 <script setup> 區塊匯入套件 exceljsfile-saver
    • exceljs:用於在前端建立/讀取/編輯 Excel(.xlsx)檔案的函式庫。
    • file-saver:在瀏覽器端將 Blob(檔案位元資料容器)另存為本機檔案。
<script setup lang="ts">
 import ExcelJS from 'exceljs';
 import { saveAs } from 'file-saver';
</script>
  1. 新增 softwareStore.ts
    • 匯入 piniafetch-wrapper.ts
    • 建立前端承接後端 API 回應的 Dto:SoftwareDto
    • softwareStore 中定義 state softwareList
    • softwareStore 中定義 action fetchSoftwareList,向後端發出請求,取得資料後將回應寫入 state 的 softwareList 陣列中。
import { defineStore } from 'pinia';
import { fetchWrapper , type ApiRspMessage ,type ActionRsp } from '@/utils/helpers/fetch-wrapper';
export interface SoftwareDto {
  assetId?: string;
  softDefine?: string;
  softDefineNM?: string;
  buId?: string;
  buNM?: string;
  softwareItem?: string;
  softwareItemNM?: string;
  assetName?: string;
  assetType?: string;
  assetTypeNM?: string;
  assetStatus?: string;
  assetStatusNM?: string;
  serverName?: string;
  assetAmount: number;
  version?: string;
  modInfo?: string;
}
const querySoftwareUrl = `${import.meta.env.VITE_API_URL}/Software`;
export const  useSoftewareStore = defineStore({
  id: 'softewareStore',
  state: () => ({
    softwarList : []  as SearchSoftwareDto[], 
  }),

  actions: {
      async fetchSoftwareList() {
        try {
         const respData: SearchSoftwareDto[] = await fetchWrapper.post(querySoftwareUrl) as SearchSoftwareDto[];
         this.softwarList = respData;
        }catch (error) {
           if (error instanceof Response) {
             const errorText = await error.text();
             console.error('Failed to create software:', errorText);
           } else {
             console.error('Failed to create software:', error);
           }
       }
    },
  },
});
  1. 建立處理匯出 Excel 的 method(主要作業邏輯)
    • 使用 softwareStore.softwareList 取得並同步後端資料。
    • 建立 workbook:新增一個活頁簿(Workbook)。
    • 建立 sheet:新增工作表並可自行命名。
    • 定義表頭 worksheet.columns:設定各欄的標題、對應資料物件的 Key,以及欄寬。
    • 透過迴圈讀取資料陣列,將資料物件依據 Key 綁定到表格內容中。
<script setup lang="ts">
const handelExportExcel = async () => {
    //透過 softwareStore.ts 發送同步資料的請求,將後端回傳的資料存放於  softwareStore.softwarList 陣列中
   await softwareStore.fetchSoftwarList();
   const workbook = new ExcelJS.Workbook(); //新增 Excel
   const worksheet = workbook.addWorksheet('sheetXXXX'); // sheet 名稱
   //定義表頭
   worksheet.columns = [
      { header: '公司別', key: 'buNM', width: 20 },
      { header: '資產編號', key: 'assetId', width: 20 },
      { header: '定義項目', key: 'softDefineNM', width: 20 },
      { header: '軟體簡稱', key: 'softwareItemNM', width: 20 },
      { header: '使用類別', key: 'assetTypeNM', width: 20 },
      { header: '資產名稱', key: 'assetName', width: 30 },
      { header: 'ServerName', key: 'serverName', width: 20 },
      { header: '廠牌', key: 'brand', width: 20 },
      { header: '數量', key: 'assetAmount', width: 20 },
      { header: '版本', key: 'version', width: 20 },
      { header: '維護部門', key: 'keyinBgNM', width: 20 },
      { header: '異動資訊', key: 'modInfo', width: 20 },
    

    ];
    //使用foreach 迴圈softwareStore中的讀取approveDetailDatas 的陣列資料
    //讀取每一列會綁定對應的 Excel 行
    softwareStore.softwarList.forEach((item) => {
      worksheet.addRow({
        buNM: item.buNM,
        assetId: item.assetId,
        softDefineNM: item.softDefineNM,
        softwareItemNM: item.softwareItemNM,
        assetTypeNM: item.assetTypeNM,
        assetName: item.assetName,
        serverName: item.serverName,
        brand: item.brand,
        assetAmount: item.assetAmount,
        version: item.version,
        modInfo: item.modInfo,

      });

    });
    // 將 Excel 文件保存為 Blob
    const buffer = await workbook.xlsx.writeBuffer();
    //取得目前時間,且傳為字串 YYYYMMDD_HHMMSS
    const now = new Date();
    const year = now.getFullYear(); // 取得年份
    const month = String(now.getMonth() + 1).padStart(2, '0'); // 取得月份,補零到兩位數
    const day = String(now.getDate()).padStart(2, '0'); // 取得日期,補零到兩位數
    const hours = String(now.getHours()).padStart(2, '0'); // 取得小時,補零到兩位數
    const minutes = String(now.getMinutes()).padStart(2, '0'); // 取得分鐘,補零到兩位數
    const formattedDateTime = `${year}${month}${day}_${hours}${minutes}`; // 組合成
    const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
    // 使用 file-saver 將檔案下載
    saveAs(blob, '軟體資產_' + formattedDateTime + '.xlsx')

  
  
};
</script>

後端作業

  1. 建立回傳資料 Dto 模型
    • 新增 `SoftwareDto.cs。
    • 建立軟體資訊使用到的資料欄位型別。
namespace Core.Models.Dto
{
    public class SoftwareDto
    {
        public string? AssetId { get; set; }
        public string? SoftDefine { get; set; }
        public string? SoftDefineNM { get; set; }
        public string? BuId { get; set; }
        public string? BuNM { get; set; }
        public string? SoftwareItem { get; set; }
        public string? SoftwareItemNM { get; set; }
        public string? AssetName { get; set; }
        public string? AssetType { get; set; }
        public string? AssetTypeNM { get; set; }
        public string? AssetStatus { get; set; }
        public string? AssetStatusNM { get; set; }
        public string? ServerName { get; set; }
        public int AssetAmount { get; set; }
        public string? Version { get; set; }
        public string? ModInfo { get; set; }
    }
}
  1. 新增承接資料請求的 API Controller
  • 建立 SoftwareController.cs:負責所有軟體相關的 API 端點。
  • 透過 DI 注入 SoftwareService,由服務取得軟體清單資料,並彙整為符合前端 SearchSoftwareDto 的結構後回傳。
namespace backendAPI.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class SoftwareController : ControllerBase
    {
        private readonly SoftwareService _softwareService;
        public SoftwareController(SoftwareService softwareService)
        {
            _softwareService = softwareService;
        }

        /// <summary>取得軟體資產列表</summary>
        /// <param name="softwareDto"></param>
        /// <returns></returns>>
        [HttpPost("GetSoftwares")]
        [ProducesResponseType(typeof(List<SearchSoftwareDto>), 200)]
        public async Task<IActionResult> GetSoftwareList()
        {
            try
            {
                var emailClaim = User.FindFirst(ClaimTypes.Upn)?.Value;
                 List<SearchSoftwareDto> resultList  = await _softwareService.querySoftwareList(softwareDto, emailClaim);
                return Ok(resultList);
            }
            catch (Exception ex)
            {
                Log.Error("Error ex : " + ex.TargetSite);
                return StatusCode(500, "Internal server error");
            }
        }
    }
}

驗證成果

圖25-1:在查詢介面中,點擊「匯出」功能按鈕。
26-1

圖25-2:打開 F12 開發者工具,檢查後端回應為 200 成功狀態。
圖26-2

圖25-3:檢視後端回應,確認已成功回傳資料內容。
圖26-3

圖25-4:前端成功取得資料並匯出 Excel。
圖26-4

圖25-5:開啟匯出檔案,驗證檔案內容是否正確。
圖26-5


上一篇
Day24 使用模板來進行改造範例:使用者群組功能授權實作(二)
系列文
全端工程師團隊的養成計畫25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言