iT邦幫忙

2026 iThome 鐵人賽

DAY 28
0
Build on Google AI

今天學什麼?30 天用 Google AI 打造智慧學習卡系列 第 28 篇

Day 28|從 Prompt 到畫面:整理智慧學習卡完整資料流程

  • 分享至 

  • xImage
  •  

昨天完成 LocalStorage 後,AI 產生的學習卡已經可以保存在瀏覽器中。即使重新整理網頁,先前產生與人工編輯過的主題也不會消失。

不過,目前的學習卡仍然只能在網站上使用。

如果家長想把教材帶到沒有網路的地方,或老師想在課堂上發給學生練習,就需要一個更適合實際使用的輸出方式。

因此,今天除了整理「從 Prompt 到畫面」的完整資料流程,也要替 Learning Card App 加入:

將目前選取的學習卡主題列印,或另存為 PDF。

這次不使用額外的 PDF 套件,而是運用瀏覽器原生的列印功能,搭配專用的列印元件與 CSS,將畫面中的學習卡轉換成適合 A4 紙張的版面。


一、目前的完整資料流程

經過前幾天的調整後,App 的資料流程已經逐漸完整。

使用者先在產生表單中設定:

  • 適合年齡
  • 學習主題
  • 卡片張數

接著將這些資料送給 Gemini API。

Gemini 回傳結構化的 Learning Card 資料後,App 會把卡片整理成一個 Generated Theme,加入統一的 AppTheme[] 狀態中。

使用者輸入產生條件
        ↓
建立 Gemini Prompt
        ↓
Gemini Structured Output
        ↓
轉換成 LearningCard[]
        ↓
建立 Generated AppTheme
        ↓
加入 AppTheme[]
        ↓
ThemeSelector 顯示新主題
        ↓
React 畫面呈現學習卡
        ↓
LocalStorage 自動保存
        ↓
列印或另存為 PDF

目前不論是內建主題或 AI 產生主題,都使用相同的 Theme 與 Card 結構。

因此,新增列印功能時,不需要重新整理 Gemini 回傳資料,也不需要建立另一套卡片格式。只要把目前選取的 Theme 傳給列印元件,就能產生紙本版面。


二、今天要完成的功能

今天的列印功能需要符合以下條件:

  1. 在學習卡操作區加入「列印/PDF」按鈕。
  2. 列印目前選取的主題。
  3. 一次輸出該主題的全部卡片。
  4. 顯示主題名稱、Emoji、來源、適合年齡與卡片張數。
  5. 每張卡片顯示完整內容、問題與答案。
  6. 如果卡片具有親子提示,也要一起輸出。
  7. 使用 A4 直向版面。
  8. 每頁使用雙欄排列。
  9. 避免單張卡片被切在兩頁之間。
  10. 列印時隱藏導覽列、按鈕及互動畫面。
  11. 可以透過瀏覽器選擇「另存為 PDF」。
  12. 不影響原本的瀏覽、編輯、產生、重新產生及 LocalStorage 功能。

三、交給 Gemini 的修改 Prompt

這次仍然請 Gemini 採用最小必要修改,不更動現有資料結構,也不另外安裝 PDF 套件。

請檢查目前 React + TypeScript Learning Card App,
新增「列印/另存為 PDF」功能。

請沿用現有的 AppTheme[]、selectedThemeId 與 LearningCard 資料結構,
以最小必要修改完成需求。

【功能目標】

讓家長或老師可以將目前選取的學習卡主題列印,
或使用瀏覽器列印功能另存為 PDF,
方便在離線環境或實體課堂中使用。

【功能需求】

1. 在目前卡片上方的操作區加入「列印/PDF」按鈕。

2. 點擊按鈕時使用瀏覽器原生 window.print()。

3. 列印內容只包含目前選取的 Theme:
   - Theme emoji
   - Theme name
   - source
   - generationParams.ageGroup
   - generationParams.topic
   - 卡片總數
   - 該 Theme 的全部 Learning Cards

4. 每張列印卡片需顯示:
   - emoji
   - title
   - pinyin(如果存在)
   - content
   - question
   - answer
   - parentTip(如果存在)
   - soundName(如果存在)

5. 請新增獨立元件:
   src/components/PrintableLearningCards.tsx

6. PrintableLearningCards 只負責接收目前 Theme,
   並產生列印用版面,
   不直接修改 themes、selectedThemeId 或其他 App State。

7. 使用 CSS @media print:
   - A4 直向
   - 適當頁面邊界
   - 每頁雙欄排列
   - 隱藏原本 App 操作介面
   - 只顯示列印內容
   - 避免單張卡片跨頁切斷
   - 保持中文字體與內容可讀性

8. 題目與答案在紙本中必須直接顯示,
   不使用互動式「查看答案」按鈕。

9. 若 Theme 有 5 張以上卡片,
   應允許瀏覽器自然分頁,
   不可因固定高度導致內容被裁切。

10. 列印前停止語音播放與自動朗讀,
    避免列印期間仍然切換卡片。

【限制】

- 不安裝 jsPDF 或其他 PDF 套件
- 不修改 Gemini API
- 不修改 Structured Output Schema
- 不修改 LearningCard 與 AppTheme 資料結構
- 不影響 Human Edit
- 不影響 Generate 與 Regenerate
- 不影響 ThemeSelector
- 不影響 Explore 與 Challenge Mode
- 不修改 LocalStorage Key
- 保留現有畫面與樣式
- 以最小必要修改完成

【完成後請回報】

1. 修改及新增的檔案
2. 列印資料如何取得
3. 如何只列印目前選取的 Theme
4. 如何隱藏原本 App UI
5. 如何避免卡片跨頁切斷
6. TypeScript 與 Build 結果

四、Gemini 完成的程式調整

Gemini 完成後,主要修改了三個檔案。

Day28 Gemini updated 3 files

1. 新增 PrintableLearningCards.tsx

新增:

src/components/PrintableLearningCards.tsx

這個元件專門負責產生紙本版面。

它接收目前選取的 Theme,並將主題資訊與所有卡片排列成適合列印的格式。

概念上可以整理為:

<PrintableLearningCards theme={currentTheme} />

這個元件不會修改 themes,也不處理主題切換或卡片編輯。

它只負責:

接收 Theme
    ↓
讀取 Theme 資訊
    ↓
讀取 Theme.cards
    ↓
產生列印版卡片

將列印內容抽成獨立元件,可以避免把紙本排版全部塞進 LearningCardApp.tsx,也讓互動畫面與列印畫面保持各自清楚的責任。


2. 修改 LearningCardApp.tsx

在原本的卡片操作區加入「列印/PDF」按鈕。

【圖 1:App 加入「列印/PDF」按鈕】
Day28 add print ui

使用者點擊按鈕後,App 會:

  1. 停止正在播放的語音。
  2. 關閉自動朗讀。
  3. 呼叫瀏覽器原生的 window.print()。
  4. 讓瀏覽器套用列印專用 CSS。
  5. 顯示 PrintableLearningCards 產生的內容。

列印資料直接來自目前選取的 Theme,不需要再次呼叫 Gemini。

selectedThemeId
      ↓
從 AppTheme[] 找到 currentTheme
      ↓
傳入 PrintableLearningCards
      ↓
輸出該主題全部卡片

因此,畫面上目前選擇「太陽系」,列印內容就會是太陽系的全部卡片;切換到其他主題後再次列印,也會自動取得新主題的內容。


3. 修改 index.css

這次列印功能的關鍵之一,是使用 @media print 建立列印專用樣式。

一般瀏覽畫面中,列印內容會先被隱藏:

.print-only {
  display: none;
}

進入列印模式後,再隱藏原本 App 畫面並顯示紙本版面:

@media print {
  .no-print {
    display: none !important;
  }

  .print-only {
    display: block !important;
  }
}

同時設定 A4 直向與紙張邊界:

@page {
  size: A4 portrait;
  margin: 12mm;
}

每張卡片則加入避免跨頁切斷的設定:

.print-card-item {
  break-inside: avoid;
  page-break-inside: avoid;
}

如此一來,瀏覽器在需要換頁時,會盡量把一張完整卡片移到下一個位置,而不是將卡片上半部留在前一頁、下半部放到下一頁。


五、互動畫面與列印畫面分開

這次沒有直接列印目前畫面上的大卡片。

原因是網站中的學習卡屬於互動介面,包含:

  • 編輯按鈕
  • 語音按鈕
  • 查看答案按鈕
  • 上一張與下一張
  • 自動朗讀
  • 重新產生
  • 模式切換
  • 主題選擇器

如果直接把整個 App 畫面送去列印,不只會出現許多紙本不需要的按鈕,也只會看到目前正在瀏覽的單張卡片。

因此,這次另外建立紙本專用元件:

互動版 Learning Card
適合在螢幕上操作
一次瀏覽一張卡片
保留按鈕與互動功能

            ↓ 使用同一份 Theme 資料

列印版 Learning Card
適合 A4 紙張與 PDF
一次排列全部卡片
直接顯示題目與答案

雖然兩種畫面的用途不同,但資料來源相同,所以不需要複製或重新維護卡片內容。


六、列印與另存 PDF 的測試方式

在 Google AI Studio 中,App 是顯示在嵌入式預覽環境裡。這類環境可能會限制網站直接開啟瀏覽器列印視窗。

因此,正式測試時,我先用新分頁開啟 App 的網站連結,再點擊「列印/PDF」。

獨立分頁中的列印功能可以正常執行。

如果要輸出 PDF,只要在列印視窗的「目的地」選擇:

另存為 PDF

接著按下儲存即可,不需要安裝額外套件。


七、實際列印結果

這次使用 AI 產生的「太陽系」主題進行測試。

測試資料包含:

  • 主題:太陽系
  • 適合年齡:7~8 歲兒童
  • 卡片張數:5 張
  • 來源:AI 智能生成主題

列印預覽結果中,可以看到:

  • 主題名稱與 Emoji
  • AI 生成來源
  • 適合年齡
  • 主題設定
  • 卡片總數
  • 五張完整學習卡
  • 卡片內容
  • 小問題
  • 答案

【圖 2:太陽系主題的 A4 列印預覽】
Day28 Print Preview card

實際結果符合預期:

測試項目 結果
列印按鈕正常顯示 成功
獨立分頁可開啟列印視窗 成功
只顯示學習卡列印內容 成功
沒有列印 App 操作按鈕 成功
正確取得目前選取的主題 成功
五張卡片全部輸出 成功
題目與答案直接顯示 成功
A4 直向版面 成功
雙欄卡片排列 成功
卡片沒有跨頁切斷 成功
中文內容正常顯示 成功
可選擇另存為 PDF 成功

八、為什麼不直接使用 PDF 套件?

前端產生 PDF 常見的方式包括:

  • jsPDF
  • html2canvas
  • React PDF
  • 由後端產生 PDF

不過,這些方法可能增加:

  • 額外套件與檔案大小
  • 中文字型處理
  • 畫面轉圖片後文字不清楚
  • 跨頁排版問題
  • 後續維護成本

目前的需求只是將網站上的學習卡輸出成紙本或 PDF,因此瀏覽器原生列印功能已經足夠。

它的優點是:

  • 不需要額外安裝套件
  • 支援瀏覽器列印
  • 可以直接選擇印表機
  • 可以另存為 PDF
  • 中文仍然是可閱讀的文字
  • 可以透過 CSS 控制 A4 排版
  • 實作範圍較小

對目前這個專案而言,原生列印是較符合需求的方案。


九、今天整理出的完整架構

完成 Day 28 後,Learning Card App 已經形成一條更完整的資料使用流程:

Generate Form
      ↓
Gemini Prompt
      ↓
Structured Output
      ↓
LearningCard[]
      ↓
AppTheme[]
      ├── ThemeSelector
      ├── Explore Mode
      ├── Challenge Mode
      ├── Human Edit
      ├── Regenerate
      ├── LocalStorage
      └── PrintableLearningCards

同一份 AppTheme[] 資料現在可以同時支援:

  • 螢幕瀏覽
  • 語音朗讀
  • 問答互動
  • 人工編輯
  • AI 重新產生
  • 瀏覽器保存
  • 紙本列印
  • PDF 輸出

這也是統一資料結構帶來的好處。

如果每個功能都使用不同的資料格式,加入列印功能時就必須再次轉換資料;現在只要把目前 Theme 傳給列印元件,就能完成輸出。


十、今天遇到的環境差異

程式本身完成後,在 Google AI Studio 的預覽區測試時,列印按鈕沒有直接開啟系統列印視窗。

後來將網站連結改用獨立分頁開啟,再測試同一個按鈕,列印功能就能正常運作。

這表示問題不是卡片資料或列印 CSS,而是開發平台的嵌入式預覽環境對瀏覽器功能有所限制。

這次也讓我更清楚區分:

功能實作是否正確

以及:

功能是否受到測試環境限制

開發工具中的 Preview 適合快速確認畫面,但遇到列印、下載、彈窗或權限相關功能時,仍然應該在獨立分頁或實際部署環境中再次驗證。


十一、今天學到的事

今天最大的收穫不只是增加一個列印按鈕,而是把前幾天完成的資料流程真正串接到新的使用情境。

1. 畫面與資料應該分開思考

同一份卡片資料可以同時產生互動畫面與紙本畫面,不需要把目前看到的 UI 原封不動拿去列印。

2. 列印版面值得獨立成元件

PrintableLearningCards.tsx 只負責紙本輸出,讓主要 App 不需要承擔所有版面邏輯。

3. CSS 不只是控制螢幕畫面

透過 @media print,同一個網站也能針對 A4 紙張建立不同的呈現方式。

4. 原生功能有時已經足夠

不是每個功能都需要安裝新的套件。只要需求清楚,window.print() 搭配列印 CSS 就能完成實用的 PDF 輸出。

5. 測試環境會影響結果

嵌入式預覽與獨立分頁的瀏覽器權限不同。遇到功能沒有反應時,除了檢查程式,也要確認是否受到執行環境限制。


十二、Day 28 完成成果

今天完成了:

  • 整理 Prompt 到畫面的完整資料流程
  • 新增獨立的列印元件
  • 加入「列印/PDF」按鈕
  • 建立 A4 列印專用 CSS
  • 一次輸出目前主題的所有卡片
  • 在紙本中直接顯示題目與答案
  • 避免單張卡片跨頁切斷
  • 支援瀏覽器另存為 PDF
  • 確認內建功能不受影響
  • 在獨立分頁完成列印驗證

現在,AI 產生的內容已經不只停留在網站畫面中。

家長可以將卡片保存成 PDF,老師也能把教材列印後帶進實體課堂,讓同一份 AI 學習內容同時支援線上與離線使用。

明天將進入整合測試:

Day 29|不只會動,還要穩定:完成智慧學習卡整合測試

接下來會把 Generate、Regenerate、Human Edit、ThemeSelector、LocalStorage、列印與各種操作流程串在一起測試,確認這些功能不只各自成功,也能在完整使用流程中穩定合作。


上一篇
Day 27|重新整理也不怕:使用 LocalStorage 保存 AI 學習卡
系列文
今天學什麼?30 天用 Google AI 打造智慧學習卡 共 28 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言