2025年12月14日 星期日

Github Copilot 2 : 透過Copilot將畫面直接產生出 Blazor 頁面

Github Copilot 2 : 透過Copilot將畫面直接產生出 Blazor 頁面

在這裡將會沿用上一篇文章所建立起來的Blazor專案,並且嘗試看看,是否能夠直接在 Visual Studio 2026 編輯器中,直接貼上這個圖片,並且請 Github Copilot 協助我來產生出 Blazor 頁面的程式碼。

建立一個空白頁面

  • 首先,在 [Components] > [Pages] 目錄下,建立一個 [Sample2.razor] & [Sample2.razor.css] 檔案

  • 切換到 VS 2026 IDE 畫面內的 Github Copilot 視窗
  • 將下列的圖片,直接貼到 Github Copilot 視窗

  • 接著,在圖片的下方,輸入下列的提示語句 (Prompt):
    • "#Sample2.razor' 將這個畫面轉成 html & css"
  • 此時,Github Copilot 便會開始產生出 Blazor 頁面的程式碼 
  • 現在可以看到可以將產生出來程式碼,複製到剪貼簿中,或者選擇套用
  • 這裡選擇另外一種做法,那就是 [套用] 的按鈕
  • 從左邊的視窗中,可以看到所產生的程式碼已經複製到 [Sample2.razor] 頁面中
  • 在 [Sample2.razor] 視窗中,按下 [Tab] 按鍵,套用這次的變更
  • 在 Copilot 視窗內,往下捲動,可以看到 [Sample2.razor.css] 的程式碼
  • 在左邊的 [Sample2.razor.css] 視窗中,按下 [Tab] 按鍵,套用這次的變更
  • 最後,執行該 Blazor 專案
  • 開啟瀏覽器,檢視該頁面 https://localhost:7299/Sample2
  • 得到底下的畫面 

這樣的處理過程,似乎能夠讓一個圖片畫面快速的轉換成為 Blazor 的頁面,並且整個過程都在 Visual Studio 2026 編輯器內完成,省去了來回切換 ChatGPT 與 VS 2026 的繁瑣過程,不知道大家喜歡哪種做法,但對於我自己而言,我一開始並不信任與接受 Github Copilot 的用法,經過幾次試用與體驗之後,我慢慢地喜歡與接受這個工具了。

接下來,我將會嘗試看看,利用 Github Copilot 來協助我做出專案開發的改善與修正的幫助。

Sample2.razor

@page "/Sample2"

<div class="clinical-page">
    <h1 class="page-title">
        Basic clinical presentation <span class="title-zh">臨床資訊</span>
    </h1>

    <div class="clinical-layout">
        <!-- 左邊:病人基本資料 -->
        <section class="panel panel-patient">
            <header class="panel-header">
                <span>病人編號 Subject No</span>
                <span class="subject-no">NCKUH-E001</span>
                <span class="subject-note">(E 代表內膜癌,O 代表卵巢癌)</span>
            </header>

            <table class="info-table">
                <tbody>
                    <tr>
                        <th>臨床資訊, 癌別<br />EC or OC(自填)</th>
                        <td class="value-text">數值型</td>
                    </tr>
                    <tr>
                        <th>年齡 (Age)</th>
                        <td class="value-text">
                            數值型<br />
                            <span class="hint">20歲~80歲</span>
                        </td>
                    </tr>
                    <tr>
                        <th>月經狀態</th>
                        <td class="value-text">
                            數值型<br />
                            <span class="hint">0 停經,1 未停經</span>
                        </td>
                    </tr>
                    <tr>
                        <th>身高 (Height)<br />cm</th>
                        <td class="value-text">
                            數值型<br />
                            <span class="hint">140cm~180cm</span>
                        </td>
                    </tr>
                    <tr>
                        <th>體重 (BW) Kg</th>
                        <td class="value-text">
                            數值型<br />
                            <span class="hint">30kg~120kg</span>
                        </td>
                    </tr>
                    <tr>
                        <th>BMI (Kg/m²)</th>
                        <td class="value-text">
                            計算公式說明如下:<br />
                            體重(公斤) ÷ 身高(公尺) ÷ 身高(公尺)
                        </td>
                    </tr>
                    <tr>
                        <th>體表面積 (BSA) m²</th>
                        <td class="value-text">
                            計算公式說明如下:<br />
                            體重(公斤) × 身高(公分) ÷ 3600,開根號
                        </td>
                    </tr>
                    <tr>
                        <th>腰圍 (AC) cm</th>
                        <td class="value-text">自填</td>
                    </tr>
                    <tr>
                        <th>日常機能狀態</th>
                        <td class="value-text">數值型</td>
                    </tr>
                </tbody>
            </table>
        </section>

        <!-- 中間:癌症狀態 -->
        <section class="panel panel-cancer">
            <header class="panel-header panel-header-highlight">
                癌症狀態 癌別 卵巢/子宮
            </header>

            <table class="info-table">
                <tbody>
                    <tr>
                        <th>癌症分期 (2023 FIGO)</th>
                        <td class="link-like">拉選式</td>
                    </tr>
                    <tr>
                        <th>AJCC c stage</th>
                        <td class="link-like">拉選式</td>
                    </tr>
                    <tr>
                        <th>AJCC p stage</th>
                        <td class="link-like">拉選式</td>
                    </tr>
                    <tr>
                        <th>組織型態</th>
                        <td class="link-like">拉選式</td>
                    </tr>
                    <tr>
                        <th>MMR protein</th>
                        <td class="link-like">拉選式</td>
                    </tr>
                    <tr>
                        <th>p53</th>
                        <td class="link-like">拉選式</td>
                    </tr>
                    <tr>
                        <th>Hormon status</th>
                        <td class="link-like">拉選式</td>
                    </tr>
                </tbody>
            </table>
        </section>

        <!-- 右邊:CT Image -->
        <section class="panel panel-ct">
            <header class="ct-header">
                CT Image
            </header>

            <div class="ct-content">
                <div class="ct-image-frame">
                    <div class="ct-image-placeholder">
                        <!-- 這裡之後可以換成 <img src="..." /> -->
                        <span class="ct-level-label">L3</span>
                    </div>
                </div>
            </div>

            <button class="report-button">
                Report
            </button>
        </section>
    </div>
</div>

Sample2.razor.css

.clinical-page {
    padding: 32px;
    background-color: #f5f5f7;
    font-family: "Segoe UI", system-ui, -apple-system, BlinkMacSystemFont, "Microsoft JhengHei", sans-serif;
    color: #333;
}

.page-title {
    font-size: 32px;
    font-weight: 600;
    margin: 0 0 24px;
}

.page-title .title-zh {
    margin-left: 8px;
    font-size: 28px;
}

.clinical-layout {
    display: grid;
    grid-template-columns: 2.2fr 1.6fr 1.4fr;
    gap: 16px;
}

.panel {
    background-color: #ffffff;
    border-radius: 10px;
    box-shadow: 0 0 0 1px #ddd, 0 3px 8px rgba(0, 0, 0, 0.05);
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

/* 左邊病人資訊 */
.panel-patient .panel-header {
    padding: 12px 16px;
    border-bottom: 1px solid #d0d0d0;
    background-color: #f8f8fb;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 8px;
    font-size: 14px;
}

.subject-no {
    font-weight: 800;
    color: #d20040;
    margin-left: 6px;
}

.subject-note {
    font-size: 12px;
    color: #666;
}

/* 中間癌症狀態 */
.panel-cancer .panel-header-highlight {
    padding: 10px 16px;
    background-color: #ffeccc;
    border-bottom: 1px solid #d0d0d0;
    font-weight: 600;
    font-size: 14px;
}

/* CT Panel */
.panel-ct {
    background-color: #ffffff;
    border-radius: 18px;
    box-shadow: 0 0 0 2px #e200c8;
    padding-bottom: 16px;
}

.ct-header {
    background: linear-gradient(90deg, #e200c8, #b000ff);
    color: #ffffff;
    font-weight: 700;
    text-align: center;
    padding: 16px 8px;
    font-size: 22px;
}

.ct-content {
    padding: 20px 24px 12px;
}

.ct-image-frame {
    border-radius: 12px;
    background-color: #111;
    padding: 10px;
}

.ct-image-placeholder {
    position: relative;
    width: 100%;
    height: 190px;
    background-color: #000;
    border-radius: 8px;
    overflow: hidden;
}

.ct-level-label {
    position: absolute;
    left: 50%;
    bottom: 24px;
    transform: translateX(-50%);
    font-size: 40px;
    font-weight: 700;
    color: #ffe6ff;
    text-shadow: 0 0 6px #ff66ff, 0 0 16px #ff00ff;
}

/* Report 按鈕 */
.report-button {
    margin: 16px 24px 0;
    width: calc(100% - 48px);
    padding: 10px;
    border-radius: 8px;
    border: 2px solid #e200c8;
    background-color: #ffffff;
    color: #333;
    font-size: 18px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.15s ease-in-out;
}

.report-button:hover {
    background-color: #ffe6ff;
}

/* 通用表格樣式 */
.info-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}

.info-table th,
.info-table td {
    border: 1px solid #d0d0d0;
    padding: 6px 8px;
    vertical-align: top;
}

.info-table th {
    width: 45%;
    background-color: #f5f5f5;
    font-weight: 600;
}

.info-table td {
    background-color: #ffffff;
}

.value-text {
    color: #c4005b;
}

.value-text .hint {
    color: #666;
    font-size: 11px;
}

.link-like {
    color: #c4005b;
    text-align: right;
}

/* RWD 簡單處理 */
@media (max-width: 992px) {
    .clinical-layout {
        grid-template-columns: 1fr;
    }

    .panel-ct {
        order: -1;
    } 

} 



2025年4月23日 星期三

ASP.NET Core 9 Web API 專案範本

ASP.NET Core 9 Web API 專案範本

建立測試專案

請依照底下的操作,建立起這篇文章需要用到的練習專案

  • 打開 Visual Studio 2022 IDE 應用程式
  • 從 [Visual Studio 2022] 對話窗中,點選右下方的 [建立新的專案] 按鈕
  • 在 [建立新專案] 對話窗右半部
    • 切換 [所有語言 (L)] 下拉選單控制項為 [C#]
    • 切換 [所有專案類型 (T)] 下拉選單控制項為 [Web API]
  • 在中間的專案範本清單中,找到並且點選 [ASP.NET Core Web API] 專案範本選項

    此專案範本可用於 ASP.NET Core 控制器或最小 API 建立 RESTful Web API,並可選擇性支援 OpenAPI 和驗證

  • 點選右下角的 [下一步] 按鈕
  • 在 [設定新的專案] 對話窗
  • 找到 [解決方案名稱] 欄位,輸入 csNET9WebApiStandard 作為專案名稱
  • 在剛剛輸入的 [解決方案名稱] 欄位下方,確認沒有勾選 [將解決方案與專案至於相同目錄中] 這個檢查盒控制項
  • 點選右下角的 [下一步] 按鈕
  • 現在將會看到 [其他資訊] 對話窗
  • 在 [架構] 欄位中,請選擇最新的開發框架,這裡選擇的 [架構] 是 : .NET 9.0 (標準字詞支援)
  • 在 [驗證類型] 選擇無
  • 勾選 [啟用 OpenAPI 支援] 這個檢查盒控制項
  • 在這個練習中,需要去勾選 [不要使用最上層陳述式(T)] 這個檢查盒控制項

    這裡的這個操作,可以由讀者自行決定是否要勾選這個檢查盒控制項

  • 勾選 [使用控制器] 這個檢查盒控制項
  • 請點選右下角的 [建立] 按鈕

稍微等候一下,這個專案將會建立完成

專案結構

建立好的 ASP.NET Core Web API 專案,其結構如下圖

專案結構總覽

  • Connected Services 這個節點包含專案連接的外部服務,如 Azure 服務、第三方 API 等。

  • Properties 包含專案的屬性設定,如啟動設定、部署設定等。

  • 相依性 列出專案依賴的所有套件和專案參考。

  • Controllers 資料夾 包含 API 控制器類別,處理 HTTP 請求和回應。 WeatherForecastController.cs 這是預設生成的控制器範例。

  • appsettings.json 應用程式的主要配置檔案,包含連線字串、日誌設定和其他配置值。

  • csNET9WebApiStandard.http 這可能是一個 HTTP 請求檔案,用於測試 API 端點。

  • Program.cs 這個 ASP.NET Core Web API 的 入口點檔案,包含應用程式的配置、服務註冊和中間件設定。

  • WeatherForecast.cs 一個模型類別,定義了 WeatherForecastController 返回的資料結構。 這是示範專案自動生成的資料模型。

Program.cs 類別內容

  • 找到並且打開 [Program.cs] 這個檔案
namespace csNET9WebApiStandard
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.

            builder.Services.AddControllers();
            // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
            builder.Services.AddOpenApi();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.MapOpenApi();
            }

            app.UseHttpsRedirection();

            app.UseAuthorization();


            app.MapControllers();

            app.Run();
        }
    }
}

這個檔案是 ASP.NET Core Web API 專案的進入點,包含了應用程式的配置和啟動邏輯

這裡的 [Main] 方法是應用程式的進入點,使用 [WebApplication.CreateBuilder] 方法來建立一個 Web 應用程式的建構器,並且在這裡註冊服務中介軟體

對於 AddControllers() 這個方法,這是用來註冊控制器服務的,這樣 ASP.NET Core 就知道要使用控制器來處理 HTTP 請求。

這裡的 AddOpenApi() 是用來註冊 OpenAPI 支援的,這樣可以自動生成 API 文件。

而 .Build() 方法會建立 Web 應用程式的實例,然後可以使用這個實例來配置中介軟體和路由

透過 .Build() 方法的呼叫,可以得到一個型別為 WebApplication 的物件,這個物件是 ASP.NET Core Web 應用程式的主要入口點。

使用 [app.Environment.IsDevelopment()] 來檢查應用程式是否在開發環境中,如果是,就會使用 app.MapOpenApi() 來啟用 OpenAPI 支援,這樣可以在開發環境中生成 API 文件。

接下來會看到 [app.UseHttpsRedirection()],這個方法會啟用 HTTPS 重導向,這樣所有的 HTTP 請求都會被重導向到 HTTPS。

在這個預設專案模板中,會看到 app.UseAuthorization(),這個方法會啟用授權中介軟體,這樣可以在應用程式中使用授權功能。

最後,使用 app.MapControllers() 來映射控制器路由,這樣 ASP.NET Core 就知道如何將 HTTP 請求路由到對應的控制器和動作方法。

到了這個階段,使用 app.Run() 來啟動應用程式,這樣就可以開始接收 HTTP 請求了

啟動並執行這個專案

  • 在 Visual Studio 2022 IDE 中,按下 F5 鍵,或者是在功能表中選擇 [除錯] -> [開始偵錯],來執行這個程式

當專案啟動之後,並沒有看到任何瀏覽器出現

這裡是專案一啟動之後,Console顯示的 Log 內容

開啟瀏覽器輸入 https://localhost:7004/WeatherForecast 這個網址,這時,將會看到瀏覽器顯示 天氣預報資訊 的訊息

這裡使用的 Port 編號,可以在 [Properties] 資料夾中的 [launchSettings.json] 檔案中找到

另外,也可以透過底下方式來進行 API 端點的測試

從功能表的 [檢視] -> [其他視窗] -> [端點總管],來開啟 HTTP 要求測試器

在 [端點總管] 視窗中,使用滑鼠右擊選擇 [/weatherforecast] 這個 API 端點,然後在彈出功能表清單內,按下 [產生要求] 按鈕

在出線的 [csNET9WebApiStandard.http] 視窗中,找到並且按下 [傳送要求] 按鈕

此時,在該視窗的右方,就可以看到這個 API 端點的執行結果了