Trae Assistant commited on
Commit
7d20ae4
·
1 Parent(s): 9cc3a59

Fix: wrap Vue template with Jinja raw; add Logo overlay; adjust gunicorn workers

Browse files
Files changed (3) hide show
  1. Dockerfile +1 -1
  2. app.py +13 -0
  3. templates/index.html +52 -1
Dockerfile CHANGED
@@ -21,4 +21,4 @@ ENV HOME=/home/user \
21
 
22
  EXPOSE 7860
23
 
24
- CMD ["gunicorn", "-b", "0.0.0.0:7860", "--timeout", "120", "--threads", "4", "app:app"]
 
21
 
22
  EXPOSE 7860
23
 
24
+ CMD ["gunicorn", "-b", "0.0.0.0:7860", "--workers", "2", "--threads", "4", "--timeout", "120", "app:app"]
app.py CHANGED
@@ -71,6 +71,7 @@ def generate_certificates():
71
  bg_file = request.files["background"]
72
  font_file = request.files.get("font")
73
  data_file = request.files.get("data_file")
 
74
 
75
  config_str = request.form.get("config", "[]")
76
  data_str = request.form.get("data", "[]")
@@ -78,6 +79,9 @@ def generate_certificates():
78
  export_scale = float(request.form.get("export_scale", "1"))
79
  if export_scale <= 0:
80
  export_scale = 1
 
 
 
81
 
82
  try:
83
  config = json.loads(config_str)
@@ -140,6 +144,15 @@ def generate_certificates():
140
  draw.text((x, y), text_wrapped, fill=color, font=font, anchor=anchor,
141
  stroke_width=stroke_width if stroke_color else 0, stroke_fill=stroke_color or None)
142
 
 
 
 
 
 
 
 
 
 
143
  out = io.BytesIO()
144
  img.save(out, format="PNG")
145
 
 
71
  bg_file = request.files["background"]
72
  font_file = request.files.get("font")
73
  data_file = request.files.get("data_file")
74
+ logo_file = request.files.get("logo")
75
 
76
  config_str = request.form.get("config", "[]")
77
  data_str = request.form.get("data", "[]")
 
79
  export_scale = float(request.form.get("export_scale", "1"))
80
  if export_scale <= 0:
81
  export_scale = 1
82
+ logo_x = int(float(request.form.get("logo_x", "0")))
83
+ logo_y = int(float(request.form.get("logo_y", "0")))
84
+ logo_scale = float(request.form.get("logo_scale", "0"))
85
 
86
  try:
87
  config = json.loads(config_str)
 
144
  draw.text((x, y), text_wrapped, fill=color, font=font, anchor=anchor,
145
  stroke_width=stroke_width if stroke_color else 0, stroke_fill=stroke_color or None)
146
 
147
+ if logo_file and logo_scale > 0:
148
+ logo_file.stream.seek(0)
149
+ logo = Image.open(logo_file).convert("RGBA")
150
+ lw, lh = logo.size
151
+ target_w = max(1, int(lw * logo_scale))
152
+ target_h = max(1, int(lh * logo_scale))
153
+ logo_resized = logo.resize((target_w, target_h), Image.LANCZOS)
154
+ img.paste(logo_resized, (int(logo_x * export_scale), int(logo_y * export_scale)), logo_resized)
155
+
156
  out = io.BytesIO()
157
  img.save(out, format="PNG")
158
 
templates/index.html CHANGED
@@ -1,3 +1,4 @@
 
1
  <!DOCTYPE html>
2
  <html lang="zh-CN">
3
  <head>
@@ -120,6 +121,37 @@
120
  </div>
121
  </div>
122
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  </div>
124
 
125
  <!-- 2. Data Input -->
@@ -292,6 +324,11 @@
292
  const fontFile = ref(null);
293
  const customFontFamily = ref('Inter'); // Default
294
 
 
 
 
 
 
295
  const fields = ref([]);
296
  const activeFieldId = ref(null);
297
 
@@ -376,6 +413,12 @@
376
  }
377
  };
378
 
 
 
 
 
 
 
379
  const addField = () => {
380
  if (!bgImage.value) {
381
  alert("请先上传背景图片");
@@ -529,6 +572,12 @@
529
  if (csvFile.value) {
530
  formData.append('data_file', csvFile.value);
531
  }
 
 
 
 
 
 
532
 
533
  try {
534
  const res = await fetch('/api/generate', {
@@ -572,10 +621,12 @@
572
  getPreviewText, getFieldStyle,
573
  startDrag,
574
  zoomIn, zoomOut,
575
- generateCertificates, loadDemoData, handleCsvUpload
 
576
  };
577
  }
578
  }).mount('#app');
579
  </script>
580
  </body>
581
  </html>
 
 
1
+ {% raw %}
2
  <!DOCTYPE html>
3
  <html lang="zh-CN">
4
  <head>
 
121
  </div>
122
  </div>
123
  </div>
124
+
125
+ <!-- Logo Upload -->
126
+ <div class="mt-4">
127
+ <label class="block text-sm font-medium text-gray-700 mb-1">徽章/Logo (可选)</label>
128
+ <div class="relative border border-gray-300 rounded-lg p-3 flex items-center gap-3 hover:bg-gray-50 cursor-pointer" @click="$refs.logoInput.click()">
129
+ <input type="file" ref="logoInput" class="hidden" accept="image/*" @change="handleLogoUpload">
130
+ <div class="bg-gray-100 p-2 rounded text-gray-500">
131
+ <i class="fas fa-stamp"></i>
132
+ </div>
133
+ <div class="flex-1 min-w-0">
134
+ <div class="text-sm font-medium text-gray-900 truncate">
135
+ {{ logoFile ? logoFile.name : '未选择' }}
136
+ </div>
137
+ <div class="text-xs text-gray-500">支持 PNG,带透明背景最佳</div>
138
+ </div>
139
+ </div>
140
+ <div v-if="logoFile" class="grid grid-cols-3 gap-2 mt-2">
141
+ <div>
142
+ <label class="block text-xs text-gray-500 mb-1">Logo X</label>
143
+ <input type="number" v-model.number="logoX" class="w-full p-2 border border-gray-300 rounded text-sm">
144
+ </div>
145
+ <div>
146
+ <label class="block text-xs text-gray-500 mb-1">Logo Y</label>
147
+ <input type="number" v-model.number="logoY" class="w-full p-2 border border-gray-300 rounded text-sm">
148
+ </div>
149
+ <div>
150
+ <label class="block text-xs text-gray-500 mb-1">Logo 缩放</label>
151
+ <input type="number" v-model.number="logoScale" step="0.1" min="0" class="w-full p-2 border border-gray-300 rounded text-sm">
152
+ </div>
153
+ </div>
154
+ </div>
155
  </div>
156
 
157
  <!-- 2. Data Input -->
 
324
  const fontFile = ref(null);
325
  const customFontFamily = ref('Inter'); // Default
326
 
327
+ const logoFile = ref(null);
328
+ const logoX = ref(0);
329
+ const logoY = ref(0);
330
+ const logoScale = ref(0);
331
+
332
  const fields = ref([]);
333
  const activeFieldId = ref(null);
334
 
 
413
  }
414
  };
415
 
416
+ const handleLogoUpload = async (event) => {
417
+ const file = event.target.files[0];
418
+ if (!file) return;
419
+ logoFile.value = file;
420
+ };
421
+
422
  const addField = () => {
423
  if (!bgImage.value) {
424
  alert("请先上传背景图片");
 
572
  if (csvFile.value) {
573
  formData.append('data_file', csvFile.value);
574
  }
575
+ if (logoFile.value) {
576
+ formData.append('logo', logoFile.value);
577
+ formData.append('logo_x', String(logoX.value || 0));
578
+ formData.append('logo_y', String(logoY.value || 0));
579
+ formData.append('logo_scale', String(logoScale.value || 0));
580
+ }
581
 
582
  try {
583
  const res = await fetch('/api/generate', {
 
621
  getPreviewText, getFieldStyle,
622
  startDrag,
623
  zoomIn, zoomOut,
624
+ generateCertificates, loadDemoData, handleCsvUpload,
625
+ logoFile, logoX, logoY, logoScale, handleLogoUpload
626
  };
627
  }
628
  }).mount('#app');
629
  </script>
630
  </body>
631
  </html>
632
+ {% endraw %}