Lashtw commited on
Commit
02d82c7
·
verified ·
1 Parent(s): 6adf160

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +62 -96
index.html CHANGED
@@ -19,9 +19,6 @@
19
  <link href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.css" rel="stylesheet">
20
  <script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.js"></script>
21
 
22
- <!-- FontAwesome 6 (For Consistent Icons) -->
23
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
24
-
25
  <!-- Google Fonts -->
26
  <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@400;700&display=swap" rel="stylesheet">
27
 
@@ -100,12 +97,6 @@
100
  width: 100%;
101
  height: 100%;
102
  }
103
-
104
- /* FontAwesome Adjustments */
105
- .fa-icon {
106
- display: inline-block;
107
- line-height: 1;
108
- }
109
  </style>
110
  </head>
111
  <body class="h-screen overflow-hidden text-slate-800">
@@ -121,7 +112,10 @@
121
  <div class="p-6 border-b border-slate-100 bg-slate-50">
122
  <div class="flex justify-between items-center mb-2">
123
  <h1 class="text-2xl font-bold text-indigo-600 flex items-center gap-2">
124
- <i class="fa-solid fa-wand-magic-sparkles"></i>
 
 
 
125
  魔法摺紙
126
  </h1>
127
  </div>
@@ -169,7 +163,10 @@
169
  @click="clearAllContent"
170
  class="w-full py-2 text-sm text-red-600 border border-red-200 bg-red-50 hover:bg-red-100 rounded-lg transition-all flex items-center justify-center gap-1"
171
  >
172
- <i class="fa-solid fa-trash-can mr-1"></i>
 
 
 
173
  一鍵清空所有內容
174
  </button>
175
  </div>
@@ -181,7 +178,10 @@
181
  @click="applyTemplate('lucky')"
182
  class="w-full py-3 bg-gradient-to-r from-emerald-500 to-teal-600 hover:from-emerald-600 hover:to-teal-700 text-white font-bold rounded-lg shadow-md hover:shadow-lg active:scale-95 transition-all flex items-center justify-center gap-2"
183
  >
184
- <i class="fa-solid fa-clover text-xl"></i>
 
 
 
185
  <span class="text-sm">套用:幸運遇見你<br><span class="text-xs opacity-90">(如皓老師分享)</span></span>
186
  </button>
187
  <p class="text-xs text-slate-400 mt-2">提示:套用後會自動切換為 6x8 網格並填入內容。</p>
@@ -195,14 +195,16 @@
195
  @click="exportProject"
196
  class="py-2 bg-slate-600 hover:bg-slate-700 text-white font-bold rounded-lg shadow-sm active:scale-95 transition-all flex items-center justify-center gap-1 text-sm"
197
  >
198
- <i class="fa-solid fa-download mr-1"></i>
 
199
  匯出存檔
200
  </button>
201
  <button
202
  @click="triggerImport"
203
  class="py-2 bg-slate-600 hover:bg-slate-700 text-white font-bold rounded-lg shadow-sm active:scale-95 transition-all flex items-center justify-center gap-1 text-sm"
204
  >
205
- <i class="fa-solid fa-upload mr-1"></i>
 
206
  匯入舊檔
207
  </button>
208
  <input
@@ -267,7 +269,7 @@
267
  <label class="block text-sm font-bold text-slate-700 mb-3">3. 選擇圖示</label>
268
  <div class="grid grid-cols-4 gap-2 max-h-60 overflow-y-auto p-1 custom-scrollbar">
269
  <button
270
- v-for="(iconClass, name) in icons"
271
  :key="name"
272
  @click="applyIconToCell(name)"
273
  :disabled="selectedCellIndex === null"
@@ -275,7 +277,9 @@
275
  :class="selectedCellIndex === null ? 'border-slate-200 text-slate-300 cursor-not-allowed' : 'border-slate-300 text-slate-600 hover:border-indigo-400 hover:text-indigo-600 cursor-pointer'"
276
  :title="name"
277
  >
278
- <i :class="[iconClass, 'text-3xl']" :style="{ color: selectedCellIndex !== null ? selectedColor : '' }"></i>
 
 
279
  </button>
280
  </div>
281
  </div>
@@ -288,7 +292,7 @@
288
  @click="triggerImageUpload"
289
  class="text-xs bg-indigo-100 text-indigo-700 px-2 py-1 rounded hover:bg-indigo-200 font-bold flex items-center gap-1"
290
  >
291
- <i class="fa-solid fa-image"></i>
292
  上傳圖片
293
  </button>
294
  <input type="file" ref="imageUploadInput" class="hidden" accept="image/*" @change="handleImageUpload">
@@ -349,11 +353,16 @@
349
  class="w-full py-4 bg-blue-600 hover:bg-blue-700 text-white font-bold rounded-xl shadow-lg hover:shadow-xl active:scale-95 transition-all flex items-center justify-center gap-2 disabled:opacity-70 disabled:cursor-wait"
350
  >
351
  <span v-if="!isGenerating">
352
- <i class="fa-solid fa-file-pdf text-xl"></i>
 
353
  下載雙頁 PDF
354
  </span>
355
  <span v-else>
356
- <i class="fa-solid fa-circle-notch fa-spin"></i>
 
 
 
 
357
  處理中...
358
  </span>
359
  </button>
@@ -404,8 +413,10 @@
404
  <div class="rotation-wrapper" :style="{ transform: `rotate(${cell.rotation}deg)` }">
405
  <span v-if="cell.type === 'text'" class="text-2xl font-bold cell-text" :style="{ color: cell.color }">{{ cell.content }}</span>
406
 
407
- <!-- Render Icon: FontAwesome -->
408
- <i v-if="cell.type === 'icon'" :class="[icons[cell.content], 'text-3xl fa-icon']" :style="{ color: cell.color }"></i>
 
 
409
 
410
  <!-- Image Renderer -->
411
  <img v-if="cell.type === 'image'" :src="cell.content" class="w-4/5 h-4/5 object-contain">
@@ -419,7 +430,9 @@
419
  <!-- EDIT MODE -->
420
  <div v-else class="flex flex-col gap-2 animate-fade-in">
421
  <button @click="viewMode = 'overview'" class="self-start text-slate-500 hover:text-indigo-600 font-bold text-sm flex items-center gap-1">
422
- <i class="fa-solid fa-arrow-left"></i> 回到全覽
 
 
423
  </button>
424
 
425
  <div
@@ -451,8 +464,10 @@
451
  {{ cell.content }}
452
  </span>
453
 
454
- <!-- Render Icon: FontAwesome -->
455
- <i v-if="cell.type === 'icon'" :class="[icons[cell.content], 'text-4xl md:text-5xl fa-icon']" :style="{ color: cell.color }"></i>
 
 
456
 
457
  <!-- Image Renderer -->
458
  <img v-if="cell.type === 'image'" :src="cell.content" class="w-4/5 h-4/5 object-contain">
@@ -498,25 +513,26 @@
498
  // Slate-800 is default
499
  const colors = ['#1e293b', '#ef4444', '#f97316', '#f59e0b', '#22c55e', '#14b8a6', '#3b82f6', '#6366f1', '#a855f7', '#ec4899'];
500
 
501
- // --- Icons (Unified to FontAwesome) ---
 
502
  const icons = {
503
- '幸運草': 'fa-solid fa-clover',
504
- '愛心': 'fa-solid fa-heart',
505
- '星星': 'fa-solid fa-star',
506
- '勝利': 'fa-solid fa-hand-peace',
507
- '獎盃': 'fa-solid fa-trophy',
508
- '笑臉': 'fa-solid fa-face-smile',
509
- '皇冠': 'fa-solid fa-crown',
510
- '鑽石': 'fa-solid fa-gem',
511
- '燈泡': 'fa-solid fa-lightbulb',
512
- '太陽': 'fa-solid fa-sun',
513
- '月亮': 'fa-solid fa-moon',
514
- '雲朵': 'fa-solid fa-cloud',
515
- '音符': 'fa-solid fa-music',
516
- '飛機': 'fa-solid fa-plane',
517
- '花朵': 'fa-solid fa-fan',
518
- '樹木': 'fa-solid fa-tree',
519
- '禮物': 'fa-solid fa-gift'
520
  };
521
 
522
  // --- State Initialization ---
@@ -893,32 +909,6 @@
893
  }, { deep: true });
894
  });
895
 
896
- // Helper to extract unicode from FontAwesome Class
897
- const getIconUnicode = (className) => {
898
- const el = document.createElement('i');
899
- el.className = className;
900
- document.body.appendChild(el);
901
- const content = window.getComputedStyle(el, ':before').getPropertyValue('content');
902
- document.body.removeChild(el);
903
- return content.replace(/['"]/g, '');
904
- };
905
-
906
- // Helper to convert text (or icon) to image data URL
907
- const textToImage = (text, color, font) => {
908
- const canvas = document.createElement('canvas');
909
- // Increase resolution for print quality
910
- canvas.width = 150;
911
- canvas.height = 150;
912
- const ctx = canvas.getContext('2d');
913
- ctx.font = font;
914
- ctx.fillStyle = color;
915
- ctx.textAlign = 'center';
916
- ctx.textBaseline = 'middle';
917
- // Draw
918
- ctx.fillText(text, 75, 75);
919
- return canvas.toDataURL('image/png');
920
- };
921
-
922
  // Helper to replace text element with SVG for perfect centering
923
  const replaceWithSvgText = (el, content, color, fontFamily, fontWeight) => {
924
  const ns = "http://www.w3.org/2000/svg";
@@ -972,12 +962,9 @@
972
  // Text uses the SVG text replacement trick for perfect centering
973
  contentHtml = `<span class="export-text" data-color="${cell.color}" style="font-size: 40px; font-weight: bold; color: ${cell.color}; font-family: 'Noto Sans TC', sans-serif;">${cell.content}</span>`;
974
  } else if (cell.type === 'icon') {
975
- // FontAwesome Icons -> Convert to Unicode Char
976
- // This ensures the icon behaves exactly like text (perfectly centered, rotates well)
977
- const iconClass = icons[cell.content];
978
- const unicode = getIconUnicode(iconClass);
979
- // Using 'Font Awesome 6 Free' with font-weight 900 for solid icons
980
- contentHtml = `<span class="export-icon" data-color="${cell.color}" style="font-size: 50px; line-height: 1; color: ${cell.color};">${unicode}</span>`;
981
  } else if (cell.type === 'image') {
982
  contentHtml = `<img src="${cell.content}" style="width: 80%; height: 80%; object-fit: contain;">`;
983
  }
@@ -1004,7 +991,7 @@
1004
  useCORS: true,
1005
  backgroundColor: '#ffffff',
1006
  onclone: (clonedDoc) => {
1007
- // Handle Text Centering
1008
  const textElements = clonedDoc.querySelectorAll('.export-text');
1009
  textElements.forEach(el => {
1010
  const textContent = el.innerText;
@@ -1012,27 +999,6 @@
1012
  if (!textContent) return;
1013
  replaceWithSvgText(el, textContent, color, "'Noto Sans TC', sans-serif", "bold");
1014
  });
1015
-
1016
- // Handle Icon Centering via Image conversion (Fixes PDF box issue)
1017
- const iconElements = clonedDoc.querySelectorAll('.export-icon');
1018
- iconElements.forEach(el => {
1019
- const textContent = el.innerText;
1020
- const color = el.getAttribute('data-color');
1021
- if (!textContent) return;
1022
-
1023
- // Convert to image using canvas in main thread context
1024
- // 'Font Awesome 6 Free' must be loaded in the page
1025
- const imgData = textToImage(textContent, color, "900 100px 'Font Awesome 6 Free'");
1026
-
1027
- const img = document.createElement('img');
1028
- img.src = imgData;
1029
- img.style.width = '100%';
1030
- img.style.height = '100%';
1031
- img.style.objectFit = 'contain';
1032
-
1033
- el.parentNode.innerHTML = ''; // Clear parent
1034
- el.parentNode.appendChild(img);
1035
- });
1036
  }
1037
  });
1038
 
 
19
  <link href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.css" rel="stylesheet">
20
  <script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.js"></script>
21
 
 
 
 
22
  <!-- Google Fonts -->
23
  <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@400;700&display=swap" rel="stylesheet">
24
 
 
97
  width: 100%;
98
  height: 100%;
99
  }
 
 
 
 
 
 
100
  </style>
101
  </head>
102
  <body class="h-screen overflow-hidden text-slate-800">
 
112
  <div class="p-6 border-b border-slate-100 bg-slate-50">
113
  <div class="flex justify-between items-center mb-2">
114
  <h1 class="text-2xl font-bold text-indigo-600 flex items-center gap-2">
115
+ <!-- Magic Wand Icon -->
116
+ <svg viewBox="0 0 512 512" class="w-8 h-8 fill-current">
117
+ <path d="M447.4 9.4c-9.4-9.4-24.6-9.4-33.9 0l-80 80c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l80-80c9.4-9.4 9.4-24.6 0-33.9zM368 224c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.3-32-32-32zm64-96c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.3-32-32-32zm-64 96c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.3-32-32-32zM96 192c-17.7 0-32 14.3-32 32s14.3 32 32 32 32-14.3 32-32-14.3-32-32-32zM0 224c0-17.7 14.3-32 32-32s32 14.3 32 32-14.3 32-32 32-32-14.3-32-32zM64 64c0-17.7 14.3-32 32-32s32 14.3 32 32-14.3 32-32 32-32-14.3-32-32zm0 384c0-17.7 14.3-32 32-32s32 14.3 32 32-14.3 32-32 32-32-14.3-32-32zm256 32c0-17.7 14.3-32 32-32s32 14.3 32 32-14.3 32-32 32-32-14.3-32-32zm-64-96c0-17.7 14.3-32 32-32s32 14.3 32 32-14.3 32-32 32-32-14.3-32-32zm-96-32c0-17.7 14.3-32 32-32s32 14.3 32 32-14.3 32-32 32-32-14.3-32-32zm336-32c0-17.7 14.3-32 32-32s32 14.3 32 32-14.3 32-32 32-32-14.3-32-32zm-136.6 20.7c-9.4-9.4-24.6-9.4-33.9 0L19.4 430.6c-9.4 9.4-9.4 24.6 0 33.9l62.1 62.1c9.4 9.4 24.6 9.4 33.9 0l229.3-229.3c9.4-9.4 9.4-24.6 0-33.9l-62.1-62.1z"/>
118
+ </svg>
119
  魔法摺紙
120
  </h1>
121
  </div>
 
163
  @click="clearAllContent"
164
  class="w-full py-2 text-sm text-red-600 border border-red-200 bg-red-50 hover:bg-red-100 rounded-lg transition-all flex items-center justify-center gap-1"
165
  >
166
+ <!-- Trash Icon -->
167
+ <svg viewBox="0 0 448 512" class="w-4 h-4 fill-current mr-1">
168
+ <path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z"/>
169
+ </svg>
170
  一鍵清空所有內容
171
  </button>
172
  </div>
 
178
  @click="applyTemplate('lucky')"
179
  class="w-full py-3 bg-gradient-to-r from-emerald-500 to-teal-600 hover:from-emerald-600 hover:to-teal-700 text-white font-bold rounded-lg shadow-md hover:shadow-lg active:scale-95 transition-all flex items-center justify-center gap-2"
180
  >
181
+ <!-- Clover Icon (FA6) -->
182
+ <svg viewBox="0 0 512 512" class="w-5 h-5 fill-current">
183
+ <path d="M224 96a64 64 0 1 1 128 0l0 32-32 0c-17.7 0-32 14.3-32 32l0 128c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-160L96 128c-35.3 0-64 28.7-64 64s28.7 64 64 64l32 0 0 32c0 17.7 14.3 32 32 32l128 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-160 0c0-35.3-28.7-64-64-64s-64 28.7-64 64s28.7 64 64 64l32 0 0 32c0 17.7 14.3 32 32 32l112 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-48 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l48 0c44.2 0 80-35.8 80-80l0-16c35.3 0 64-28.7 64-64s-28.7-64-64-64l-32 0 0-32c0-17.7-14.3-32-32-32l-128 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l160 0c0 35.3 28.7 64 64 64s64-28.7 64-64s-28.7-64-64-64l-32 0 0-32c0-17.7-14.3-32-32-32s-32 14.3-32 32z"/>
184
+ </svg>
185
  <span class="text-sm">套用:幸運遇見你<br><span class="text-xs opacity-90">(如皓老師分享)</span></span>
186
  </button>
187
  <p class="text-xs text-slate-400 mt-2">提示:套用後會自動切換為 6x8 網格並填入內容。</p>
 
195
  @click="exportProject"
196
  class="py-2 bg-slate-600 hover:bg-slate-700 text-white font-bold rounded-lg shadow-sm active:scale-95 transition-all flex items-center justify-center gap-1 text-sm"
197
  >
198
+ <!-- Download Icon -->
199
+ <svg viewBox="0 0 512 512" class="w-4 h-4 fill-current mr-1"><path d="M288 32c0-17.7-14.3-32-32-32s-32 14.3-32 32V274.7l-73.4-73.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l128 128c12.5 12.5 32.8 12.5 45.3 0l128-128c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L288 274.7V32zM64 352c-35.3 0-64 28.7-64 64v32c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V416c0-35.3-28.7-64-64-64H346.5l-45.3 45.3c-25 25-65.5 25-90.5 0L165.5 352H64zM432 456c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24z"/></svg>
200
  匯出存檔
201
  </button>
202
  <button
203
  @click="triggerImport"
204
  class="py-2 bg-slate-600 hover:bg-slate-700 text-white font-bold rounded-lg shadow-sm active:scale-95 transition-all flex items-center justify-center gap-1 text-sm"
205
  >
206
+ <!-- Upload Icon -->
207
+ <svg viewBox="0 0 512 512" class="w-4 h-4 fill-current mr-1"><path d="M288 109.3V352c0 17.7-14.3 32-32 32s-32-14.3-32-32V109.3l-73.4 73.4c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l128-128c12.5-12.5 32.8-12.5 45.3 0l128 128c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L288 109.3zM64 352H192c0 35.3 28.7 64 64 64s64-28.7 64-64H448c35.3 0 64 28.7 64 64v32c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V416c0-35.3 28.7-64 64-64zM432 456c13.3 0 24-10.7 24-24s-10.7-24-24-24s-24 10.7-24 24s10.7 24 24 24z"/></svg>
208
  匯入舊檔
209
  </button>
210
  <input
 
269
  <label class="block text-sm font-bold text-slate-700 mb-3">3. 選擇圖示</label>
270
  <div class="grid grid-cols-4 gap-2 max-h-60 overflow-y-auto p-1 custom-scrollbar">
271
  <button
272
+ v-for="(path, name) in icons"
273
  :key="name"
274
  @click="applyIconToCell(name)"
275
  :disabled="selectedCellIndex === null"
 
277
  :class="selectedCellIndex === null ? 'border-slate-200 text-slate-300 cursor-not-allowed' : 'border-slate-300 text-slate-600 hover:border-indigo-400 hover:text-indigo-600 cursor-pointer'"
278
  :title="name"
279
  >
280
+ <svg viewBox="0 0 512 512" class="w-8 h-8" :style="{ fill: selectedCellIndex !== null ? selectedColor : '#1e293b' }">
281
+ <path :d="path"/>
282
+ </svg>
283
  </button>
284
  </div>
285
  </div>
 
292
  @click="triggerImageUpload"
293
  class="text-xs bg-indigo-100 text-indigo-700 px-2 py-1 rounded hover:bg-indigo-200 font-bold flex items-center gap-1"
294
  >
295
+ <svg viewBox="0 0 512 512" class="w-3 h-3 fill-current mr-1"><path d="M0 96C0 60.7 28.7 32 64 32H448c35.3 0 64 28.7 64 64V416c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6h96 32H424c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48z"/></svg>
296
  上傳圖片
297
  </button>
298
  <input type="file" ref="imageUploadInput" class="hidden" accept="image/*" @change="handleImageUpload">
 
353
  class="w-full py-4 bg-blue-600 hover:bg-blue-700 text-white font-bold rounded-xl shadow-lg hover:shadow-xl active:scale-95 transition-all flex items-center justify-center gap-2 disabled:opacity-70 disabled:cursor-wait"
354
  >
355
  <span v-if="!isGenerating">
356
+ <!-- PDF Icon -->
357
+ <svg viewBox="0 0 384 512" class="w-5 h-5 fill-current inline-block"><path d="M181.9 256.1c-5-16-4.9-46.9-2-46.9 8.4 0 7.6 36.9 2 46.9zm-1.7 47.2c-7.7 20.2-17.3 43.3-28.4 62.7 18.3-7 39-17.2 62.9-21.9-12.7-9.6-24.9-23.4-34.5-40.8zM86.1 428.1c0 .8 13.2-5.4 34.9-40.2-6.7 6.3-29.1 24.5-34.9 40.2zM248 160h136v328c0 13.3-10.7 24-24 24H24c-13.3 0-24-10.7-24-24V24C0 10.7 10.7 0 24 0h200v136c0 13.2 10.8 24 24 24zm-8 171.8c-20-12.2-33.3-29-42.7-53.8 4.5-18.5 11.6-46.6 6.2-64.2-4.7-29.4-42.4-26.5-47.8-6.8-5 18.3-.4 44.1 8.1 77-11.6 27.6-28.7 64.6-40.8 85.8-.1 0-.1.1-.2.1-27.1 13.9-73.6 44.5-54.5 68 5.6 6.9 16 10 21.5 10 17.9 0 35.7-18 61.1-61.8 25.8-8.5 54.1-19.1 79-23.2 21.7 11.8 47.1 19.5 64 19.5 29.2 0 31.2-32 19.7-43.4-13.9-13.6-54.3-9.7-73.6-7.2zM377 105L279 7c-4.5-4.5-10.6-7-17-7h-6v128h128v-6.1c0-6.3-2.5-12.4-7-16.9zm-74.1 255.3c4.1-2.7-2.5-11.9-4.2-17.9-2.9-4.1-32.9-14.9-49.6-19.7 1.4 4.9 3 9.8 4.2 14.6 3.1 17.9 6.9 51.1 12.8 62.1 9.5 1 34.1-33 36.8-39.1z"/></svg>
358
  下載雙頁 PDF
359
  </span>
360
  <span v-else>
361
+ <!-- Spinner -->
362
+ <svg class="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
363
+ <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
364
+ <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
365
+ </svg>
366
  處理中...
367
  </span>
368
  </button>
 
413
  <div class="rotation-wrapper" :style="{ transform: `rotate(${cell.rotation}deg)` }">
414
  <span v-if="cell.type === 'text'" class="text-2xl font-bold cell-text" :style="{ color: cell.color }">{{ cell.content }}</span>
415
 
416
+ <!-- Render Icon: SVG Path -->
417
+ <svg v-if="cell.type === 'icon'" viewBox="0 0 512 512" class="w-3/5 h-3/5" :style="{ fill: cell.color }">
418
+ <path :d="icons[cell.content]"/>
419
+ </svg>
420
 
421
  <!-- Image Renderer -->
422
  <img v-if="cell.type === 'image'" :src="cell.content" class="w-4/5 h-4/5 object-contain">
 
430
  <!-- EDIT MODE -->
431
  <div v-else class="flex flex-col gap-2 animate-fade-in">
432
  <button @click="viewMode = 'overview'" class="self-start text-slate-500 hover:text-indigo-600 font-bold text-sm flex items-center gap-1">
433
+ <!-- Arrow Left -->
434
+ <svg viewBox="0 0 448 512" class="w-4 h-4 fill-current mr-1 inline-block"><path d="M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.2 288 416 288c17.7 0 32-14.3 32-32s-14.3-32-32-32l-306.7 0L214.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z"/></svg>
435
+ 回到全覽
436
  </button>
437
 
438
  <div
 
464
  {{ cell.content }}
465
  </span>
466
 
467
+ <!-- Render Icon: SVG Path -->
468
+ <svg v-if="cell.type === 'icon'" viewBox="0 0 512 512" class="w-3/5 h-3/5" :style="{ fill: cell.color }">
469
+ <path :d="icons[cell.content]"/>
470
+ </svg>
471
 
472
  <!-- Image Renderer -->
473
  <img v-if="cell.type === 'image'" :src="cell.content" class="w-4/5 h-4/5 object-contain">
 
513
  // Slate-800 is default
514
  const colors = ['#1e293b', '#ef4444', '#f97316', '#f59e0b', '#22c55e', '#14b8a6', '#3b82f6', '#6366f1', '#a855f7', '#ec4899'];
515
 
516
+ // --- Icons (FontAwesome 6 SVG Paths) ---
517
+ // This ensures perfect PDF export without needing webfonts
518
  const icons = {
519
+ '幸運草': 'M224 96a64 64 0 1 1 128 0l0 32-32 0c-17.7 0-32 14.3-32 32l0 128c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-160L96 128c-35.3 0-64 28.7-64 64s28.7 64 64 64l32 0 0 32c0 17.7 14.3 32 32 32l128 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-160 0c0-35.3-28.7-64-64-64s-64 28.7-64 64s28.7 64 64 64l32 0 0 32c0 17.7 14.3 32 32 32l112 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-48 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l48 0c44.2 0 80-35.8 80-80l0-16c35.3 0 64-28.7 64-64s-28.7-64-64-64l-32 0 0-32c0-17.7-14.3-32-32-32l-128 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l160 0c0 35.3 28.7 64 64 64s64-28.7 64-64s-28.7-64-64-64l-32 0 0-32c0-17.7-14.3-32-32-32s-32 14.3-32 32z',
520
+ '愛心': 'M47.6 300.4L228.3 469.1c7.5 7 17.4 10.9 27.7 10.9s20.2-3.9 27.7-10.9L464.4 300.4c30.4-28.3 47.6-68 47.6-109.5v-5.8c0-69.9-50.5-129.5-119.4-141C347 36.5 300.6 51.4 268 84L256 96 244 84c-32.6-32.6-79-47.5-124.6-39.9C50.5 55.6 0 115.2 0 185.1v5.8c0 41.5 17.2 81.2 47.6 109.5z',
521
+ '星星': 'M316.9 18C311.6 7 300.4 0 288.1 0s-23.4 7-28.8 18L195 150.3 51.4 171.5c-12 1.8-22 10.2-25.7 21.7s-.7 24.2 7.9 32.7L137.8 329 113.2 474.7c-2 12 3 24.2 12.9 31.3s23 8 33.8 2.3l128.3-68.5 128.3 68.5c10.8 5.7 23.9 4.9 33.8-2.3s14.9-19.3 12.9-31.3L438.5 329 542.7 225.9c8.6-8.5 11.7-21.2 7.9-32.7s-13.7-19.9-25.7-21.7L381.2 150.3 316.9 18z',
522
+ '勝利': 'M128 0c17.7 0 32 14.3 32 32l0 263.7L235 220.1c16.3-8.8 36.6-2.9 45.4 13.3s2.9 36.6-13.3 45.4l-117.6 63.6c-19.4 10.5-32.3 29.8-34.9 51.8l-4.1 34.7c-3.7 31.6 18.5 60.5 49.8 65.6l96.6 15.7c33.9 5.5 67.2-16.1 75.6-49.1L355.7 350.2c2.5-10 11.6-16.9 21.9-16.6l9.5 .2c23 0 42.8-16.4 47.1-39.1l2.5-13.2c4.3-22.7-10.6-44.5-33.3-48.8l-15.1-2.9c-24-4.5-39.7-28-35.1-52.1l19.1-100.9C382.9 26.6 427.1-3.6 477.3 6.9c25.3 5.3 45.7 23.1 54.3 47.7s4.8 51.7-10.1 72l-58.4 79.7c-30.7 41.9-40.4 96.1-26.6 146.7l12.7 46.2c17.3 63.3 10.6 131.2-18.6 189.4c-9.2 18.3-25.9 32.1-45.7 37.9L288 456c-38.3 11.2-80-6-102.6-39.2L128 332.8 128 32c0-17.7 14.3-32 32-32zM32 64C14.3 64 0 78.3 0 96L0 448c0 17.7 14.3 32 32 32s32-14.3 32-32l0-352c0-17.7-14.3-32-32-32z',
523
+ '獎盃': 'M400 96l48 0c26.5 0 48 21.5 48 48l0 48c0 62.1-44.5 113.8-103.2 126.3c-13.2 38.3-43.1 68.6-80.8 81.3L336 448l16 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-192 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l16 0 24-48.4c-37.7-12.7-67.6-43-80.8-81.3C60.5 305.8 16 254.1 16 192L16 144c0-26.5 21.5-48 48-48l48 0 0-64c0-17.7 14.3-32 32-32l224 0c17.7 0 32 14.3 32 32l0 64zM80 144l0 48c0 23.4 16.8 42.9 38.9 47.1c4.4-7.3 9.4-14.2 14.8-20.6L128 144l-48 0zm352 48l0-48-48 0 0 74.4c5.4 6.4 10.4 13.3 14.8 20.6C383.2 234.9 400 215.4 400 192z',
524
+ '笑臉': 'M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM164.1 325.5C182 346.2 212.6 368 256 368s74-21.8 91.9-42.5c5.8-6.7 15.9-7.4 22.6-1.6s7.4 15.9 1.6 22.6C349.8 372.1 311.1 400 256 400s-93.8-27.9-116.1-53.5c-5.8-6.7-5.1-16.8 1.6-22.6s16.8-5.1 22.6 1.6zM144.4 208a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zm192-32a32 32 0 1 1 0 64 32 32 0 1 1 0-64z',
525
+ '皇冠': 'M4.1 38.2C1.4 34.2 0 29.4 0 24.6C0 11 11 0 24.6 0C33.2 0 41.1 4.3 45.9 11.5L98.6 90.7l93.1-78.1C197 7.3 204.6 4.3 212.6 4.3C223 4.3 233 8.6 240 16.6l0 0 0 0c7-8 17-12.3 27.4-12.3c8 0 15.6 3 20.9 8.3l93.1 78.1 52.7-79.2C438.9 4.3 446.8 0 455.4 0C469 0 480 11 480 24.6c0 4.8-1.4 9.6-4.1 13.6L420.7 165.6c-5.5 8.2-13.8 14.1-23.4 16.5l-93.7 23.4L252.1 442.2c-2.8 11.3-13.1 19.3-24.8 19.2s-21.7-8.3-24.2-19.7l-49.4-235.6L58.7 182.2c-9.6-2.4-17.9-8.3-23.4-16.5L4.1 38.2z',
526
+ '鑽石': 'M242.4 292.5C247.8 287.1 257.1 287.1 262.5 292.5L339.5 369.5C353.5 383.5 372.6 391.3 392.4 391.3H416C416 417.7 394.7 439 368.4 439H143.6C117.3 439 96 417.7 96 391.3H119.6C139.4 391.3 158.5 383.5 172.5 369.5L242.4 292.5zM262.5 219.5C257.1 224.9 247.8 224.9 242.4 219.5L172.5 142.5C158.5 128.5 139.4 120.7 119.6 120.7H96C96 94.3 117.3 73 143.6 73H368.4C394.7 73 416 94.3 416 120.7H392.4C372.6 120.7 353.5 128.5 339.5 142.5L262.5 219.5z',
527
+ '燈泡': 'M272 64c-8.8 0-16 7.2-16 16s7.2 16 16 16c17.7 0 32 14.3 32 32s7.2 16 16 16 16-7.2 16-16c0-35.3-28.7-64-64-64zM256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-448c-11.8 0-23.2 .9-34.2 2.6c-4.3 .7-8.8-1.7-10.6-5.8s.3-8.9 4.1-11.6C235 35.5 256 24.3 278.4 16.9c4.2-1.4 8.7 .5 10.9 4.5s.9 8.9-3.1 11.7c-19.1 13.3-30.2 35.2-30.2 58.9z',
528
+ '太陽': 'M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 14.8L432.2 240l76.9 74.8c3.1 4.1 3.7 9.8 1.6 14.8s-6.6 8.6-11.9 9.6L391 359 371.1 466.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-14.8-1.6L272 400.2l-74.8 76.9c-4.1 3.1-9.8 3.7-14.8 1.6s-8.6-6.6-9.6-11.9L153 359 45.1 339.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-14.8L111.8 240 34.9 165.2c-3.1-4.1-3.7-9.8-1.6-14.8s6.6-8.6 11.9-9.6L153 121 172.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 14.8 1.6L272 79.8 346.8 2.9c4.1-3.1 9.8-3.7 14.8-1.6zM272 168c0-44.2-35.8-80-80-80s-80 35.8-80 80 35.8 80 80 80 80-35.8 80-80z',
529
+ '月亮': 'M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4 5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3 6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z',
530
+ '雲朵': 'M0 336c0 79.5 64.5 144 144 144H368c79.5 0 144-64.5 144-144c0-47.8-23.7-89.8-59.5-115.9c-2.3-67-56.7-120.1-124.5-120.1c-60.8 0-111.4 43.1-122.9 100.9C91.6 211.3 0 263.9 0 336z',
531
+ '音符': 'M496 64a16 16 0 0 0 -16 16v224c0 35.3-28.7 64-64 64s-64-28.7-64-64 28.7-64 64-64c12.5 0 24.1 3.6 34.1 9.8l13.9 8.7V64C464 28.7 435.3 0 400 0H112C76.7 0 48 28.7 48 64v288c0 35.3-28.7 64-64 64s-64-28.7-64-64 28.7-64 64-64c12.5 0 24.1 3.6 34.1 9.8l13.9 8.7V128h288v64H160v224c0 35.3-28.7 64-64 64s-64-28.7-64-64 28.7-64 64-64c12.5 0 24.1 3.6 34.1 9.8l13.9 8.7V112c0-8.8 7.2-16 16-16h320c8.8 0 16 7.2 16 16V80c0-8.8 7.2-16 16-16z',
532
+ '飛機': 'M384 160c-17.7 0-32-14.3-32-32s14.3-32 32-32H512c17.7 0 32 14.3 32 32V256c0 17.7-14.3 32-32 32s-32-14.3-32-32V160H384zM64 480c-17.7 0-32-14.3-32-32s14.3-32 32-32H192c17.7 0 32 14.3 32 32s-14.3 32-32 32H64zM224 96c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 64c0 17.7 14.3 32 32 32s32-14.3 32-32l0-64zM480 320c-17.7 0-32 14.3-32 32v64c0 17.7 14.3 32 32 32s32-14.3 32-32V352c0-17.7-14.3-32-32-32zM32 64C14.3 64 0 78.3 0 96v64c0 17.7 14.3 32 32 32s32-14.3 32-32V96H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H32zM276.9 221.7c-4.8 16.6-21.8 26.2-38.3 21.4s-26.2-21.8-21.4-38.3L237 135c4.8-16.6 21.8-26.2 38.3-21.4s26.2 21.8 21.4 38.3l-19.8 69.8z',
533
+ '花朵': 'M261.8 229.6l-20.2 20.2c-7.1 7.1-18.5 7.1-25.5 0l-20.2-20.2c-1.4-1.4-2.1-3.2-2.2-5.1c-14-159.2 122.9-281.3 266-223c6.3 2.5 10.3 8.7 10.3 15.4c0 82.6-67 149.6-149.6 149.6c-4.8 0-9.5-2.2-12.7-6.2c-3.1 4-7.9 6.2-12.7 6.2c-82.6 0-149.6 67-149.6 149.6c0 6.8 4 12.9 10.3 15.4c143.2 58.3 280-63.8 266-223c0-1.9-.8-3.7-2.2-5.1L491.4 88.9c7.1-7.1 7.1-18.5 0-25.5L471.2 43.1c-7.1-7.1-18.5-7.1-25.5 0l-20.2 20.2c-1.4 1.4-3.2 2.1-5.1 2.2c-159.2 14-281.3-122.9-223-266C200 193.3 206.1 189.3 212.9 189.3c82.6 0 149.6 67 149.6 149.6c0 4.8-2.2 9.5-6.2 12.7c4 3.1 6.2 7.9 6.2 12.7c0 82.6 67 149.6 149.6 149.6c6.8 0 12.9-4 15.4-10.3c58.3-143.2-63.8-280-223-266c-1.9 0-3.7 .8-5.1 2.2L261.8 229.6z',
534
+ '樹木': 'M448 32c-35.3 0-64 28.7-64 64V256c0 35.3 28.7 64 64 64H512c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H448zM96 32C60.7 32 32 60.7 32 96v256c0 35.3 28.7 64 64 64H224c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H96zM48 208v96c0 8.8-7.2 16-16 16s-16-7.2-16-16V208c0-8.8 7.2-16 16-16s16 7.2 16 16zm256-48c0 8.8-7.2 16-16 16s-16-7.2-16-16V144c0-8.8 7.2-16 16-16s16 7.2 16 16v16zm-96 96v96c0 8.8-7.2 16-16 16s-16-7.2-16-16V256c0-8.8 7.2-16 16-16s16 7.2 16 16z',
535
+ '禮物': 'M96 0C78.3 0 64 14.3 64 32v96h64V32c0-17.7-14.3-32-32-32H96zM288 128V32c0-17.7-14.3-32-32-32H224c-17.7 0-32 14.3-32 32v96h96zM64 160c-35.3 0-64 28.7-64 64V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V224c0-35.3-28.7-64-64-64H64z'
536
  };
537
 
538
  // --- State Initialization ---
 
909
  }, { deep: true });
910
  });
911
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
912
  // Helper to replace text element with SVG for perfect centering
913
  const replaceWithSvgText = (el, content, color, fontFamily, fontWeight) => {
914
  const ns = "http://www.w3.org/2000/svg";
 
962
  // Text uses the SVG text replacement trick for perfect centering
963
  contentHtml = `<span class="export-text" data-color="${cell.color}" style="font-size: 40px; font-weight: bold; color: ${cell.color}; font-family: 'Noto Sans TC', sans-serif;">${cell.content}</span>`;
964
  } else if (cell.type === 'icon') {
965
+ // Render SVG directly for PDF
966
+ const path = icons[cell.content];
967
+ contentHtml = `<svg viewBox="0 0 512 512" style="width: 60%; height: 60%; fill: ${cell.color};"><path d="${path}"/></svg>`;
 
 
 
968
  } else if (cell.type === 'image') {
969
  contentHtml = `<img src="${cell.content}" style="width: 80%; height: 80%; object-fit: contain;">`;
970
  }
 
991
  useCORS: true,
992
  backgroundColor: '#ffffff',
993
  onclone: (clonedDoc) => {
994
+ // Handle Text Centering (Only for Text now)
995
  const textElements = clonedDoc.querySelectorAll('.export-text');
996
  textElements.forEach(el => {
997
  const textContent = el.innerText;
 
999
  if (!textContent) return;
1000
  replaceWithSvgText(el, textContent, color, "'Noto Sans TC', sans-serif", "bold");
1001
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1002
  }
1003
  });
1004