2014年9月28日 星期日

ASP.NET MVC 匯出 Excel 簡單做 - 自訂匯出 Excel 檔案的檔名

這一篇是使用「ASP.NET MVC 匯出 Excel 簡單做 - 使用 ClosedXML」的基礎,那一篇的內容是使用 ClosedXML 來完成資料的匯出 Excel 檔,但是那一篇所完成的匯出 Excel 檔案功能是無法讓使用者自行決定匯出檔案的名稱,所以這一篇要做的就是加上一個簡單的小功能,在匯出檔案之前讓使用者可以自行輸入匯出檔案的檔案名稱,如果不輸入的話,匯出的檔案名稱就會使用預設值。

 


雖然說是以「ASP.NET MVC 匯出 Excel 簡單做 - 使用 ClosedXML」這一篇為基礎,不過這一篇使用的資料與程式內容則是重新再做一個新的,這次會使用 Northwind 的 Customer 資料。

首先在 Visual Studio 2013 裡建立一個 ASP.NET MVC 5 的專案,並且新增 LocalDB 以及把 Northwind 資料給匯入,然後建立 ADO.NET 實體資料模型。

image

接著先完成 Customer 的資料列表顯示與資料匯出的基本功能,

image

CustomerController.cs

using BlogDemo.Infrastructure.ActionResults;
using BlogDemo.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 
namespace BlogDemo.Controllers
{
    public class CustomerController : Controller
    {
        NorthwindEntities db = new NorthwindEntities();
 
        public ActionResult Index()
        {
            var query = db.Customers.OrderBy(x => x.CustomerID).ToList();
            ViewData.Model = query;
            return View();
        }
 
        [HttpPost]
        public ActionResult HasData()
        {
            JObject jo = new JObject();
            bool result = !db.Customers.Count().Equals(0);
            jo.Add("Msg", result.ToString());
            return Content(JsonConvert.SerializeObject(jo), "application/json");
        }
 
        public ActionResult Export()
        {
            var exportSpource = this.GetExportDataWithAllColumns();
            var dt = JsonConvert.DeserializeObject<DataTable>(exportSpource.ToString());
 
            var exportFileName = 
                string.Concat("CustomerData_", DateTime.Now.ToString("yyyyMMddHHmmss"), ".xlsx");
 
            return new ExportExcelResult
            {
                SheetName = "客戶資料",
                FileName = exportFileName,
                ExportData = dt
            };
        }
 
        private JArray GetExportDataWithAllColumns()
        {
            var query = db.Customers.OrderBy(x => x.CustomerID);
 
            JArray jObjects = new JArray();
 
            foreach (var item in query)
            {
                var jo = new JObject
                {
                    {"CompanyName", item.CompanyName},
                    {"ContactName", item.ContactName},
                    {"ContactTitle", item.ContactTitle},
                    {"Address", item.Address},
                    {"City", item.City},
                    {"PostalCode", item.PostalCode},
                    {"Country", item.Country},
                    {"Phone", item.Phone},
                    {"Fax", item.Fax}
                };
                jObjects.Add(jo);
            }
            return jObjects;
        }
 
    }
}

~/Views/Customer/Index.cshtml

@model IEnumerable<BlogDemo.Models.Customer>
 
@{
    ViewBag.Title = "Customer - List";
}
 
<h2>Customer - List</h2>
 
<div class="well">
    <button class="btn btn-primary" id="ButtonExport" name="ButtonExport">
        匯出資料
    </button>
</div>
 
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.CompanyName)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.ContactName)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.ContactTitle)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Address)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.City)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Region)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.PostalCode)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Country)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Phone)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Fax)
        </th>
    </tr>
 
    @foreach (var item in Model)
    {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.CompanyName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ContactName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ContactTitle)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Address)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.City)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Region)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.PostalCode)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Country)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Phone)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Fax)
            </td>
        </tr>
    }
 
</table>
 
@section scripts
{
    <script src="~/Scripts/Customer-Export.js"></script>
}

~/Scripts/Customer-Export.js

;
(function (windows) {
    if (typeof (jQuery) === 'undefined') { alert('jQuery Library NotFound.'); return; }
 
    var HasData = 'False';
 
    $(function () {
 
        $('#ButtonExport').click(function () {
            ExportData();
        });
 
    });
 
    function AlertErrorMessage(title, message) {
        /// <summary>
        /// 使用 Bootbox.js 的錯誤訊息顯示
        /// </summary>
        /// <param name="title"></param>
        /// <param name="message"></param>
 
        bootbox.dialog({
            title: title,
            message: message,
            buttons: {
                danger: {
                    label: "確認",
                    className: "btn-danger"
                }
            }
        });
    }
 
    function ExportData() {
        /// <summary>
        /// 資料匯出
        /// </summary>
 
        $.ajax({
            type: 'post',
            url: Router.action('Customer', 'HasData'),
            dataType: 'json',
            cache: false,
            async: false,
            success: function (data) {
                if (data.Msg) {
                    HasData = data.Msg;
                    if (HasData == 'False') {
                        AlertErrorMessage("錯誤", "尚未建立任何資料, 無法匯出資料.");
                    }
                    else {
                        window.location = Router.action('Customer', 'Export');
                    }
                }
            },
            error: function (xhr, textStatus, errorThrown) {
                AlertErrorMessage("錯誤", "資料匯出錯誤");
            }
        });
    }
})
(window);

執行匯出

image

匯出結果

image

此外這一次有加入使用先前所介紹的 RouteJs

ASP.NET MVC - 使用 RouteJs - Part.1

ASP.NET MVC - 使用 RouteJs - Part.2

ASP.NET MVC - 使用 RouteJs - Part.3

如果開發過程中有遇到以下的狀況,

image

依據錯誤訊息裡的解決方法,可以在 Web.Config 裡增加以下的內容,

<validation validateIntegratedModeConfiguration="false"/>

image

 

截至目前為止已經完成了最基本的資料匯出功能,接著下來就是在檢視頁面增加一個輸入介面讓使用者可以自行輸入匯出檔案的名稱,而這個輸入介面會與之後我所想要加入的一些功能整合在一起,所以我是希望在按下頁面上的「匯出資料」Button 後會以 modal 的方式來顯示輸入框,而這個 modal 介面是選用「Bootstrap Modal」。

https://github.com/jschr/bootstrap-modal

http://jschr.github.io/bootstrap-modal/bs3.html

專案加入 Bootstrap Modal 套件之後,在 ~/Views/Customer/index.cshtml 檢視頁面裡增加 modal 與輸入框的內容,

<!-- ExportData Dialog -->
<div id="ExportDataDialog" class="modal fade" tabindex="-1" data-width="600px" data-height="150px" style="display: none;">
    <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
        <h4 id="ExportDataDialog">匯出會員資料</h4>
    </div>
    <div class="modal-body" style="margin-left: 5px">
        <div class="row">
            <div class="form-group">
                <div class="col-lg-12">
                    <label for="ExportFileName">匯出資料檔名(可不填)</label>
                    <input type="text" id="ExportFileName" name="ExportFileName" class="form-control" placeholder="可輸入中文, 不含副檔名." />
                </div>
            </div>
        </div>
    </div>
    <div class="modal-footer">
        <button id="ButtonExecuteExport" class="btn btn-primary">匯出資料</button>
        <button id="ButtonCancel" class="btn" data-dismiss="modal" aria-hidden="true">取消</button>
    </div>
</div>
<!-- ExportData Dialog -->

再來就是修改後端 CustomerController.cs 與 Customer-Export.js 的程式內容,

CustomerController - Export Action

public ActionResult Export(string fileName)
{
    var exportSpource = this.GetExportDataWithAllColumns();
    var dt = JsonConvert.DeserializeObject<DataTable>(exportSpource.ToString());
 
    var exportFileName = string.IsNullOrWhiteSpace(fileName)
        ? string.Concat("CustomerData_", DateTime.Now.ToString("yyyyMMddHHmmss"), ".xlsx")
        : string.Concat(fileName, ".xlsx");
 
    return new ExportExcelResult
    {
        SheetName = "客戶資料",
        FileName = exportFileName,
        ExportData = dt
    };
}

~/Scripts/Customer-Export.js

;
(function (windows) {
    if (typeof (jQuery) === 'undefined') { alert('jQuery Library NotFound.'); return; }
 
    var HasData = 'False';
 
    $(function () {
 
        //顯示匯出選項視窗
        $('#ButtonExport').click(function () {
            $('#ExportDataDialog').modal('show');
        });
 
        //匯出資料
        $('#ButtonExecuteExport').click(function () {
            //匯出 Excel 檔名
            var exportFileName = $.trim($('#ExportFileName').val());
            ExportData(exportFileName);
        });
 
    });
 
    function AlertErrorMessage(title, message) {
        /// <summary>
        /// 使用 Bootbox.js 的錯誤訊息顯示
        /// </summary>
        /// <param name="title"></param>
        /// <param name="message"></param>
 
        bootbox.dialog({
            title: title,
            message: message,
            buttons: {
                danger: {
                    label: "確認",
                    className: "btn-danger"
                }
            }
        });
    }
 
    function ExportData(exportFileName) {
        /// <summary>
        /// 資料匯出
        /// </summary>
 
        $.ajax({
            type: 'post',
            url: Router.action('Customer', 'HasData'),
            dataType: 'json',
            cache: false,
            async: false,
            success: function (data) {
                if (data.Msg) {
                    HasData = data.Msg;
                    if (HasData == 'False') {
                        AlertErrorMessage("錯誤", "尚未建立任何資料, 無法匯出資料.");
                    }
                    else {
                        window.location = exportFileName.length == 0
                            ? Router.action('Customer', 'Export')
                            : Router.action('Customer', 'Export', { fileName: exportFileName });
 
                        $('#ExportFileName').val('');
                        $('#ExportDataDialog').modal('hide');
                    }
                }
            },
            error: function (xhr, textStatus, errorThrown) {
                AlertErrorMessage("錯誤", "資料匯出錯誤");
            }
        });
    }
})
(window);

 

執行結果

image

image

image

 


這一次所做的功能是相當小的一個項目,不過是為了要帶出接著我所要做的功能,這個功能是讓使用者除了自己輸入要匯出的 Excel 檔案名稱之外,也希望可以讓使用者可以自己挑選要匯出資料的欄位,因為有時候使用者並不想要把全部欄位的資料給匯出來,使用者也希望可以自己決定那些欄位的資料是他們所需要的,而挑選資料欄位的介面也會一起做在匯出資料的 modal 之中。

 

以上

沒有留言:

張貼留言

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。