leonsimon23 commited on
Commit
562efa6
·
verified ·
1 Parent(s): 30a4363

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +90 -447
templates/index.html CHANGED
@@ -11,510 +11,153 @@
11
  padding: 0;
12
  box-sizing: border-box;
13
  }
14
-
15
  body {
16
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
17
- background: linear-gradient(135deg, #e0f7fa 0%, #b3e5fc 100%);
 
18
  min-height: 100vh;
19
- padding: 20px;
 
 
20
  }
21
-
22
  .container {
23
- max-width: 1200px;
24
- margin: 0 auto;
25
- background: rgba(255, 255, 255, 0.95);
26
- border-radius: 20px;
27
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
28
- overflow: hidden;
29
- backdrop-filter: blur(10px);
30
- }
31
-
32
- .header {
33
- background: linear-gradient(135deg, #1d976c 0%, #93f9b9 100%);
34
- color: white;
35
  padding: 30px;
36
- text-align: center;
37
- position: relative;
38
- overflow: hidden;
39
- }
40
-
41
- .header::before {
42
- content: '';
43
- position: absolute;
44
- top: -50%;
45
- left: -50%;
46
- width: 200%;
47
- height: 200%;
48
- background: repeating-linear-gradient(
49
- 45deg,
50
- transparent,
51
- transparent 10px,
52
- rgba(255, 255, 255, 0.1) 10px,
53
- rgba(255, 255, 255, 0.1) 20px
54
- );
55
- animation: float 20s linear infinite;
56
- }
57
-
58
- @keyframes float {
59
- 0% { transform: translate(-50%, -50%) rotate(0deg); }
60
- 100% { transform: translate(-50%, -50%) rotate(360deg); }
61
- }
62
-
63
- .header h1 {
64
- font-size: 2.5rem;
65
- margin-bottom: 10px;
66
- position: relative;
67
- z-index: 1;
68
- text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
69
- }
70
-
71
- .header p {
72
- font-size: 1.1rem;
73
- opacity: 0.9;
74
- position: relative;
75
- z-index: 1;
76
- text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
77
- }
78
-
79
- .main-content {
80
- padding: 40px;
81
- display: grid;
82
- grid-template-columns: 1fr 1fr;
83
- gap: 40px;
84
- align-items: start;
85
- }
86
-
87
- .upload-section {
88
- background: #fff;
89
  border-radius: 15px;
90
- padding: 30px;
91
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
92
- border: 2px dashed transparent;
93
- transition: all 0.3s ease;
94
- }
95
-
96
- .upload-section:hover {
97
- transform: translateY(-5px);
98
- box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15);
99
- border-color: #1d976c;
100
  }
101
-
102
- .upload-area {
103
- border: 3px dashed #ddd;
104
- border-radius: 12px;
105
- padding: 40px;
106
  text-align: center;
107
- cursor: pointer;
108
- transition: all 0.3s ease;
109
- position: relative;
110
- overflow: hidden;
111
- }
112
-
113
- .upload-area:hover {
114
- border-color: #1d976c;
115
- background: linear-gradient(45deg, rgba(29, 151, 108, 0.05), rgba(147, 249, 185, 0.05));
116
- }
117
-
118
- .upload-area.dragover {
119
- border-color: #1d976c;
120
- background: rgba(29, 151, 108, 0.1);
121
- transform: scale(1.02);
122
  }
123
-
124
- .upload-icon {
125
- font-size: 3rem;
126
- color: #1d976c;
127
  margin-bottom: 15px;
128
- transition: transform 0.3s ease;
129
  }
130
-
131
- .upload-area:hover .upload-icon {
132
- transform: scale(1.1) rotate(5deg);
133
- }
134
-
135
- .text-input {
136
  width: 100%;
137
- min-height: 120px;
138
- border: 2px solid #e1e5e9;
139
- border-radius: 12px;
140
- padding: 15px;
141
- font-size: 16px;
142
- resize: vertical;
143
- transition: all 0.3s ease;
144
  }
145
-
146
- .text-input:focus {
147
- outline: none;
148
- border-color: #1d976c;
149
- box-shadow: 0 0 0 3px rgba(29, 151, 108, 0.1);
150
  }
151
-
152
- .submit-btn {
153
- background: linear-gradient(135deg, #1d976c 0%, #93f9b9 100%);
154
- color: white;
155
  border: none;
156
- padding: 15px 40px;
157
- border-radius: 25px;
158
- font-size: 18px;
159
- font-weight: 600;
160
  cursor: pointer;
161
- transition: all 0.3s ease;
162
- position: relative;
163
- overflow: hidden;
164
- width: 100%;
165
- margin-top: 20px;
166
  }
167
-
168
- .submit-btn::before {
169
- content: '';
170
- position: absolute;
171
- top: 0;
172
- left: -100%;
173
- width: 100%;
174
- height: 100%;
175
- background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
176
- transition: left 0.5s;
177
  }
178
-
179
- .submit-btn:hover::before {
180
- left: 100%;
181
- }
182
-
183
- .submit-btn:hover {
184
- transform: translateY(-2px);
185
- box-shadow: 0 10px 25px rgba(29, 151, 108, 0.3);
186
- }
187
-
188
- .submit-btn:disabled {
189
- opacity: 0.6;
190
- cursor: not-allowed;
191
- transform: none;
192
- }
193
-
194
- .results-section {
195
- background: #fff;
196
- border-radius: 15px;
197
- padding: 30px;
198
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
199
- min-height: 500px;
200
- }
201
-
202
- .result-content {
203
- white-space: pre-wrap;
204
- line-height: 1.6;
205
- color: #333;
206
- font-size: 16px;
207
  }
208
-
209
- .loading {
210
- display: flex;
211
- align-items: center;
212
- justify-content: center;
213
- height: 200px;
214
- flex-direction: column;
215
- gap: 20px;
216
- }
217
-
218
- .spinner {
219
- width: 40px;
220
- height: 40px;
221
- border: 4px solid #f3f3f3;
222
- border-top: 4px solid #1d976c;
223
- border-radius: 50%;
224
- animation: spin 1s linear infinite;
225
- }
226
-
227
- @keyframes spin {
228
- 0% { transform: rotate(0deg); }
229
- 100% { transform: rotate(360deg); }
230
- }
231
-
232
- .preview-image {
233
- max-width: 100%;
234
- height: auto;
235
- border-radius: 8px;
236
- margin: 15px 0;
237
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
238
- transition: transform 0.3s ease;
239
- }
240
-
241
- .preview-image:hover {
242
- transform: scale(1.05);
243
- }
244
-
245
- .section-title {
246
- font-size: 1.5rem;
247
- margin-bottom: 20px;
248
- color: #333;
249
- display: flex;
250
- align-items: center;
251
- gap: 10px;
252
- }
253
-
254
- .feature-card {
255
- background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
256
- border-radius: 10px;
257
- padding: 20px;
258
- margin: 15px 0;
259
- border-left: 4px solid #1d976c;
260
  }
261
-
262
  .error-message {
263
- background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%);
264
- color: #c0392b;
265
- padding: 15px;
266
- border-radius: 8px;
267
- margin: 10px 0;
268
- border-left: 4px solid #c0392b;
269
  }
270
-
271
  .success-message {
272
- background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
273
- color: #198754;
274
- padding: 15px;
275
- border-radius: 8px;
276
- margin: 10px 0;
277
- border-left: 4px solid #198754;
278
- }
279
-
280
- @media (max-width: 768px) {
281
- .main-content {
282
- grid-template-columns: 1fr;
283
- gap: 20px;
284
- }
285
-
286
- .header h1 {
287
- font-size: 2rem;
288
- }
289
-
290
- .container {
291
- margin: 10px;
292
- }
293
  }
294
-
295
- .file-info {
296
- background: #f8f9fa;
297
- border-radius: 8px;
298
- padding: 15px;
299
- margin: 10px 0;
300
- border: 1px solid #dee2e6;
301
  }
302
  </style>
303
  </head>
304
  <body>
305
  <div class="container">
306
- <header class="header">
307
- <h1>🏥 皮肤医生临床诊疗辅助系统</h1>
308
- <p>AI驱动的智能皮肤疾病诊断与分析平台</p>
309
- </header>
310
-
311
- <main class="main-content">
312
- <section class="upload-section">
313
- <h2 class="section-title">
314
- <i data-lucide="upload"></i>
315
- 输入诊疗信息
316
- </h2>
317
-
318
- <form id="analysisForm" enctype="multipart/form-data">
319
- <div class="upload-area" id="uploadArea">
320
- <i data-lucide="camera"></i>
321
- <h3>上传皮肤图像</h3>
322
- <p>拖拽图片到此处或点击选择文件</p>
323
- <p style="font-size: 14px; color: #666; margin-top: 10px;">
324
- 支持 JPG, PNG, GIF 格式,最大10MB
325
- </p>
326
- </div>
327
- <input type="file" id="imageInput" name="image" accept="image/*" style="display: none;">
328
-
329
- <div id="imagePreview"></div>
330
-
331
- <div style="margin: 25px 0;">
332
- <label for="textInput" style="display: block; margin-bottom: 10px; font-weight: 600;">
333
- <i data-lucide="file-text"></i> 症状描述或问题咨询:
334
- </label>
335
- <textarea
336
- id="textInput"
337
- name="text"
338
- class="text-input"
339
- placeholder="请描述患者的症状、病史、用药情况或其他相关信息...&#10;例如:患者皮肤出现红疹,伴有瘙痒,持续一周..."
340
- ></textarea>
341
- </div>
342
-
343
- <button type="submit" id="submitBtn" class="submit-btn">
344
- <i data-lucide="brain"></i>
345
- 开始AI诊断分析
346
- </button>
347
- </form>
348
- </section>
349
-
350
- <section class="results-section">
351
- <h2 class="section-title">
352
- <i data-lucide="activity"></i>
353
- 诊断结果
354
- </h2>
355
-
356
- <div id="results">
357
- <div class="feature-card">
358
- <h3>🎯 系统功能特点</h3>
359
- <ul style="margin-left: 20px; margin-top: 10px;">
360
- <li>📸 支持皮肤图像上传与分析</li>
361
- <li>💬 结合文字症状描述</li>
362
- <li>🤖 基于Gemini-2.0-Pro AI模型</li>
363
- <li>⚡ 快速准确的诊断建议</li>
364
- <li>📋 详细的治疗方案推荐</li>
365
- </ul>
366
- </div>
367
-
368
- <div class="feature-card">
369
- <h3>ℹ️ 使用说明</h3>
370
- <p>1. 上传清晰的皮肤病变图像</p>
371
- <p>2. 详细描述症状和病史</p>
372
- <p>3. 点击"开始AI诊断分析"按钮</p>
373
- <p>4. 等待AI分析并查看结果</p>
374
- </div>
375
-
376
- <div style="background: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; border-radius: 8px; margin-top: 20px;">
377
- <strong>⚠️ 重要提示:</strong>
378
- <p>本系统仅为临床辅助工具,诊断结果仅供参考。最终诊断请以专业医师的临床判断为准。</p>
379
- </div>
380
- </div>
381
- </section>
382
- </main>
383
  </div>
384
 
385
  <script>
386
- lucide.createIcons();
387
-
388
- const uploadArea = document.getElementById('uploadArea');
389
- const imageInput = document.getElementById('imageInput');
390
  const textInput = document.getElementById('textInput');
 
391
  const submitBtn = document.getElementById('submitBtn');
392
  const results = document.getElementById('results');
393
- const imagePreview = document.getElementById('imagePreview');
394
- const form = document.getElementById('analysisForm');
395
-
396
- // 修复:使用 JS 监听上传区域的点击,并手动触发文件输入框
397
- uploadArea.addEventListener('click', () => {
398
- imageInput.click();
399
- });
400
-
401
- // 文件输入框改变事件,处理文件预览
402
- imageInput.addEventListener('change', (e) => {
403
- const file = e.target.files[0];
404
- if (file) {
405
- handleFile(file);
406
- }
407
- });
408
 
409
- // 拖放事件处理
410
- uploadArea.addEventListener('dragover', (e) => {
411
- e.preventDefault();
412
- uploadArea.classList.add('dragover');
413
- });
414
-
415
- uploadArea.addEventListener('dragleave', () => {
416
- uploadArea.classList.remove('dragover');
417
- });
418
-
419
- uploadArea.addEventListener('drop', (e) => {
420
- e.preventDefault();
421
- uploadArea.classList.remove('dragover');
422
- const file = e.dataTransfer.files[0];
423
- if (file && file.type.startsWith('image/')) {
424
- // 将文件传递给 handleFile 函数进行预览
425
- handleFile(file);
426
-
427
- // 关键修复:将拖放的文件赋值给隐藏的 input 元素
428
- const dataTransfer = new DataTransfer();
429
- dataTransfer.items.add(file);
430
- imageInput.files = dataTransfer.files;
431
- }
432
- });
433
-
434
- // 处理文件预览
435
- function handleFile(file) {
436
- const reader = new FileReader();
437
- reader.onload = (e) => {
438
- imagePreview.innerHTML = `
439
- <div class="file-info">
440
- <h4>📁 已选择文件:${file.name}</h4>
441
- <p>文件大小:${(file.size / 1024 / 1024).toFixed(2)} MB</p>
442
- <img src="${e.target.result}" alt="预览图片" class="preview-image">
443
- </div>
444
- `;
445
- };
446
- reader.readAsDataURL(file);
447
- }
448
-
449
- // 表单提交事件处理
450
  form.addEventListener('submit', async (e) => {
451
  e.preventDefault();
452
 
453
  const text = textInput.value.trim();
454
  const imageFile = imageInput.files[0];
455
 
456
- if (!imageFile && !text) {
457
- showError('请至少上传一张图片或输入症状描述');
458
  return;
459
  }
460
-
461
- submitBtn.disabled = true;
462
- submitBtn.innerHTML = '<i data-lucide="loader"></i> 正在分析中...';
463
- lucide.createIcons();
464
-
465
- showLoading();
466
 
467
- try {
468
- // 使用FormData自动收集表单数据
469
- const formData = new FormData(form);
 
 
 
 
 
 
 
 
 
 
470
 
 
471
  const response = await fetch('/analyze', {
472
  method: 'POST',
473
- body: formData
474
  });
475
 
476
  const result = await response.json();
477
 
478
- if (!response.ok) {
479
- throw new Error(result.error || `服务器错误: ${response.status}`);
 
 
480
  }
481
-
482
- showResults(result);
483
-
484
  } catch (error) {
485
- showError(`分析失败: ${error.message}`);
486
  } finally {
487
- submitBtn.disabled = false;
488
- submitBtn.innerHTML = '<i data-lucide="brain"></i> 开始AI诊断分析';
489
- lucide.createIcons();
490
  }
491
  });
492
-
493
- function showLoading() {
494
- results.innerHTML = `
495
- <div class="loading">
496
- <div class="spinner"></div>
497
- <p>🤖 AI正在分析中,请稍候...</p>
498
- </div>
499
- `;
500
- }
501
-
502
- function showResults(data) {
503
- results.innerHTML = `
504
- <div class="success-message">
505
- ✅ 分析完成!以下是AI诊断结果:
506
- </div>
507
- <div class="result-content">${data.result || data.error}</div>
508
- `;
509
- }
510
-
511
- function showError(message) {
512
- results.innerHTML = `
513
- <div class="error-message">
514
- ❌ ${message}
515
- </div>
516
- `;
517
- }
518
  </script>
519
  </body>
520
- </html>
 
11
  padding: 0;
12
  box-sizing: border-box;
13
  }
 
14
  body {
15
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
16
+ background: linear-gradient(135deg, #00bcd4, #8bc34a);
17
+ color: white;
18
  min-height: 100vh;
19
+ display: flex;
20
+ justify-content: center;
21
+ align-items: center;
22
  }
 
23
  .container {
24
+ background: rgba(255, 255, 255, 0.2);
 
 
 
 
 
 
 
 
 
 
 
25
  padding: 30px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  border-radius: 15px;
27
+ max-width: 600px;
28
+ width: 100%;
29
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
 
 
 
 
 
 
 
30
  }
31
+ h1 {
 
 
 
 
32
  text-align: center;
33
+ font-size: 2.5rem;
34
+ margin-bottom: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  }
36
+ .form-group {
 
 
 
37
  margin-bottom: 15px;
 
38
  }
39
+ input[type="file"], textarea {
 
 
 
 
 
40
  width: 100%;
41
+ padding: 10px;
42
+ border-radius: 5px;
43
+ border: 1px solid #ddd;
 
 
 
 
44
  }
45
+ textarea {
46
+ resize: vertical;
47
+ height: 100px;
 
 
48
  }
49
+ button {
50
+ width: 100%;
51
+ padding: 12px;
52
+ background-color: #4CAF50;
53
  border: none;
54
+ border-radius: 5px;
55
+ color: white;
56
+ font-size: 1.2rem;
 
57
  cursor: pointer;
58
+ transition: background-color 0.3s;
 
 
 
 
59
  }
60
+ button:disabled {
61
+ background-color: #9e9e9e;
 
 
 
 
 
 
 
 
62
  }
63
+ button:hover {
64
+ background-color: #45a049;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  }
66
+ .results {
67
+ margin-top: 20px;
68
+ padding: 15px;
69
+ border-radius: 5px;
70
+ background-color: rgba(255, 255, 255, 0.2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  }
 
72
  .error-message {
73
+ color: red;
 
 
 
 
 
74
  }
 
75
  .success-message {
76
+ color: green;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  }
78
+ .loading {
79
+ display: none;
80
+ text-align: center;
81
+ font-size: 1.2rem;
 
 
 
82
  }
83
  </style>
84
  </head>
85
  <body>
86
  <div class="container">
87
+ <h1>皮肤病诊断助手</h1>
88
+ <form id="uploadForm">
89
+ <div class="form-group">
90
+ <label for="textInput">症状描述</label>
91
+ <textarea id="textInput" placeholder="请输入您的症状描述(可选)"></textarea>
92
+ </div>
93
+ <div class="form-group">
94
+ <label for="imageInput">上传皮肤病变图像(可选)</label>
95
+ <input type="file" id="imageInput" accept="image/*">
96
+ </div>
97
+ <div class="form-group">
98
+ <button type="submit" id="submitBtn">提交分析</button>
99
+ </div>
100
+ </form>
101
+ <div id="loadingIndicator" class="loading">
102
+ <div class="spinner"></div>
103
+ <p>正在分析,请稍等...</p>
104
+ </div>
105
+ <div id="results" class="results"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  </div>
107
 
108
  <script>
109
+ const form = document.getElementById('uploadForm');
 
 
 
110
  const textInput = document.getElementById('textInput');
111
+ const imageInput = document.getElementById('imageInput');
112
  const submitBtn = document.getElementById('submitBtn');
113
  const results = document.getElementById('results');
114
+ const loadingIndicator = document.getElementById('loadingIndicator');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  form.addEventListener('submit', async (e) => {
117
  e.preventDefault();
118
 
119
  const text = textInput.value.trim();
120
  const imageFile = imageInput.files[0];
121
 
122
+ if (!text && !imageFile) {
123
+ alert("请至少提供症状描述或上传图像!");
124
  return;
125
  }
 
 
 
 
 
 
126
 
127
+ if (imageFile && imageFile.size > 10 * 1024 * 1024) {
128
+ alert("文件过大,请确保文件小于10MB");
129
+ return;
130
+ }
131
+
132
+ submitBtn.disabled = true; // 禁用按钮避免重复提交
133
+ loadingIndicator.style.display = 'block'; // 显示加载动画
134
+
135
+ const formData = new FormData();
136
+ formData.append('text', text);
137
+ if (imageFile) {
138
+ formData.append('image', imageFile);
139
+ }
140
 
141
+ try {
142
  const response = await fetch('/analyze', {
143
  method: 'POST',
144
+ body: formData,
145
  });
146
 
147
  const result = await response.json();
148
 
149
+ if (result.error) {
150
+ results.innerHTML = `<div class="error-message">${result.error}</div>`;
151
+ } else {
152
+ results.innerHTML = `<div class="success-message">${result.result}</div>`;
153
  }
 
 
 
154
  } catch (error) {
155
+ results.innerHTML = `<div class="error-message">网络请求失败,请稍后重试。</div>`;
156
  } finally {
157
+ submitBtn.disabled = false; // 重新启用按钮
158
+ loadingIndicator.style.display = 'none'; // 隐藏加载动画
 
159
  }
160
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  </script>
162
  </body>
163
+ </html>