leonsimon23 commited on
Commit
ac24a6b
·
verified ·
1 Parent(s): ffeb1ef

Create static/index.html

Browse files
Files changed (1) hide show
  1. static/index.html +434 -0
static/index.html ADDED
@@ -0,0 +1,434 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>皮镜 (SkinLens) - 智能皮肤诊疗助手</title>
7
+ <style>
8
+ /* --- 全局样式 --- */
9
+ @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap');
10
+
11
+ body {
12
+ font-family: 'Noto Sans SC', sans-serif;
13
+ background-color: #f4f7f9;
14
+ margin: 0;
15
+ padding: 20px;
16
+ display: flex;
17
+ justify-content: center;
18
+ align-items: flex-start;
19
+ min-height: 100vh;
20
+ color: #333;
21
+ }
22
+
23
+ /* --- 主容器 --- */
24
+ .container {
25
+ width: 100%;
26
+ max-width: 900px;
27
+ background-color: #ffffff;
28
+ border-radius: 12px;
29
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
30
+ padding: 40px;
31
+ box-sizing: border-box;
32
+ }
33
+
34
+ /* --- 头部 --- */
35
+ .header {
36
+ text-align: center;
37
+ margin-bottom: 30px;
38
+ border-bottom: 1px solid #e0e0e0;
39
+ padding-bottom: 20px;
40
+ }
41
+
42
+ .header h1 {
43
+ color: #1a73e8;
44
+ margin: 0;
45
+ font-weight: 500;
46
+ }
47
+
48
+ .header p {
49
+ color: #5f6368;
50
+ margin-top: 5px;
51
+ font-size: 1rem;
52
+ }
53
+
54
+ /* --- 主内容区 --- */
55
+ .main-content {
56
+ display: grid;
57
+ grid-template-columns: 1fr 1fr;
58
+ gap: 30px;
59
+ }
60
+
61
+ @media (max-width: 768px) {
62
+ .main-content {
63
+ grid-template-columns: 1fr;
64
+ }
65
+ }
66
+
67
+ /* --- 输入卡片样式 --- */
68
+ .card {
69
+ background: #fff;
70
+ padding: 25px;
71
+ border-radius: 8px;
72
+ border: 1px solid #dfe1e5;
73
+ }
74
+
75
+ .card h2 {
76
+ font-size: 1.2rem;
77
+ margin-top: 0;
78
+ margin-bottom: 20px;
79
+ color: #1a73e8;
80
+ border-bottom: 2px solid #e8f0fe;
81
+ padding-bottom: 10px;
82
+ }
83
+
84
+ /* --- 图片上传区域 --- */
85
+ .image-uploader {
86
+ border: 2px dashed #d0d0d0;
87
+ border-radius: 8px;
88
+ padding: 20px;
89
+ text-align: center;
90
+ cursor: pointer;
91
+ transition: border-color 0.3s, background-color 0.3s;
92
+ position: relative;
93
+ }
94
+
95
+ .image-uploader:hover {
96
+ border-color: #1a73e8;
97
+ background-color: #f8f9fa;
98
+ }
99
+
100
+ .image-uploader .upload-text {
101
+ color: #5f6368;
102
+ }
103
+
104
+ #image-preview {
105
+ max-width: 100%;
106
+ max-height: 200px;
107
+ margin-top: 15px;
108
+ border-radius: 4px;
109
+ display: none;
110
+ }
111
+
112
+ #image-input {
113
+ display: none;
114
+ }
115
+
116
+ /* --- 文本输入区域 --- */
117
+ textarea {
118
+ width: 100%;
119
+ height: 150px;
120
+ border-radius: 4px;
121
+ border: 1px solid #d0d0d0;
122
+ padding: 10px;
123
+ font-family: inherit;
124
+ font-size: 0.95rem;
125
+ resize: vertical;
126
+ box-sizing: border-box;
127
+ }
128
+
129
+ textarea:focus {
130
+ outline: none;
131
+ border-color: #1a73e8;
132
+ box-shadow: 0 0 0 2px rgba(26, 115, 232, 0.2);
133
+ }
134
+
135
+ /* --- 操作按钮 --- */
136
+ .actions {
137
+ grid-column: 1 / -1; /* 跨越所有列 */
138
+ text-align: center;
139
+ margin-top: 20px;
140
+ }
141
+
142
+ #analyze-btn {
143
+ background-color: #1a73e8;
144
+ color: white;
145
+ border: none;
146
+ padding: 12px 30px;
147
+ border-radius: 25px;
148
+ font-size: 1rem;
149
+ font-weight: 500;
150
+ cursor: pointer;
151
+ transition: background-color 0.3s, box-shadow 0.3s;
152
+ display: inline-flex;
153
+ align-items: center;
154
+ gap: 8px;
155
+ }
156
+
157
+ #analyze-btn:hover {
158
+ background-color: #185abc;
159
+ box-shadow: 0 4px 12px rgba(26, 115, 232, 0.2);
160
+ }
161
+
162
+ #analyze-btn:disabled {
163
+ background-color: #a0c3ff;
164
+ cursor: not-allowed;
165
+ }
166
+
167
+ /* --- 加载动画 --- */
168
+ .loader {
169
+ width: 18px;
170
+ height: 18px;
171
+ border: 2px solid #fff;
172
+ border-bottom-color: transparent;
173
+ border-radius: 50%;
174
+ display: none; /* 初始隐藏 */
175
+ box-sizing: border-box;
176
+ animation: rotation 1s linear infinite;
177
+ }
178
+
179
+ @keyframes rotation {
180
+ 0% { transform: rotate(0deg); }
181
+ 100% { transform: rotate(360deg); }
182
+ }
183
+
184
+ /* --- 结果展示区域 --- */
185
+ #results-container {
186
+ margin-top: 40px;
187
+ display: none; /* 初始隐藏 */
188
+ border-top: 1px solid #e0e0e0;
189
+ padding-top: 30px;
190
+ }
191
+
192
+ .results-grid {
193
+ display: grid;
194
+ gap: 20px;
195
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
196
+ }
197
+
198
+ .result-card {
199
+ background-color: #f8f9fa;
200
+ border-radius: 8px;
201
+ padding: 20px;
202
+ border: 1px solid #e0e0e0;
203
+ }
204
+
205
+ .result-card h3 {
206
+ margin-top: 0;
207
+ color: #1a73e8;
208
+ font-size: 1.1rem;
209
+ margin-bottom: 15px;
210
+ }
211
+
212
+ .result-card ul {
213
+ padding-left: 20px;
214
+ margin: 0;
215
+ color: #3c4043;
216
+ }
217
+
218
+ .result-card li {
219
+ margin-bottom: 8px;
220
+ }
221
+
222
+ .diagnosis-item {
223
+ border-bottom: 1px solid #e0e0e0;
224
+ padding-bottom: 10px;
225
+ margin-bottom: 10px;
226
+ }
227
+ .diagnosis-item:last-child {
228
+ border-bottom: none;
229
+ }
230
+ .diagnosis-item strong {
231
+ color: #202124;
232
+ }
233
+ .diagnosis-item .likelihood {
234
+ font-weight: 500;
235
+ padding: 2px 8px;
236
+ border-radius: 12px;
237
+ font-size: 0.8rem;
238
+ margin-left: 8px;
239
+ }
240
+ .likelihood-高 { background-color: #e57373; color: white; }
241
+ .likelihood-中 { background-color: #ffb74d; color: white; }
242
+ .likelihood-低 { background-color: #81c784; color: white; }
243
+
244
+ .error-message {
245
+ color: #d93025;
246
+ text-align: center;
247
+ margin-top: 20px;
248
+ display: none; /* 初始隐藏 */
249
+ }
250
+ </style>
251
+ </head>
252
+ <body>
253
+
254
+ <div class="container">
255
+ <div class="header">
256
+ <h1>皮镜 (SkinLens)</h1>
257
+ <p>您的智能皮肤科临床诊疗辅助伙伴</p>
258
+ </div>
259
+
260
+ <div class="main-content">
261
+ <!-- 图片上传卡片 -->
262
+ <div class="card">
263
+ <h2>1. 上传皮肤图像</h2>
264
+ <div class="image-uploader" id="image-uploader-box" onclick="document.getElementById('image-input').click();">
265
+ <p class="upload-text">点击或拖拽图片到此区域</p>
266
+ <img id="image-preview" src="#" alt="Image Preview"/>
267
+ </div>
268
+ <input type="file" id="image-input" accept="image/jpeg, image/png">
269
+ </div>
270
+
271
+ <!-- 病历信息卡片 -->
272
+ <div class="card">
273
+ <h2>2. 输入病历信息</h2>
274
+ <textarea id="prompt-input" placeholder="请输入患者主诉、现病史、查体等信息..."></textarea>
275
+ </div>
276
+ </div>
277
+
278
+ <div class="actions">
279
+ <button id="analyze-btn">
280
+ <span id="btn-text">开始智能分析</span>
281
+ <div class="loader" id="loader"></div>
282
+ </button>
283
+ </div>
284
+
285
+ <p id="error-message" class="error-message"></p>
286
+
287
+ <!-- 结果展示容器 -->
288
+ <div id="results-container">
289
+ <div class="results-grid">
290
+ <div class="result-card" id="diagnosis-card">
291
+ <h3>鉴别诊断列表</h3>
292
+ <div id="differential-diagnosis-content"></div>
293
+ </div>
294
+ <div class="result-card" id="features-card">
295
+ <h3>图像特征分析</h3>
296
+ <ul id="image-features-content"></ul>
297
+ </div>
298
+ <div class="result-card" id="analysis-card" style="grid-column: 1 / -1;">
299
+ <h3>综合分析</h3>
300
+ <p id="comprehensive-analysis-content"></p>
301
+ </div>
302
+ <div class="result-card" id="recommendations-card" style="grid-column: 1 / -1;">
303
+ <h3>下一步建议</h3>
304
+ <div class="results-grid">
305
+ <div>
306
+ <h4>辅助检查</h4>
307
+ <ul id="further-examinations-content"></ul>
308
+ </div>
309
+ <div>
310
+ <h4>治疗方向</h4>
311
+ <ul id="treatment-suggestions-content"></ul>
312
+ </div>
313
+ </div>
314
+ </div>
315
+ </div>
316
+ </div>
317
+ </div>
318
+
319
+ <script>
320
+ const imageInput = document.getElementById('image-input');
321
+ const imagePreview = document.getElementById('image-preview');
322
+ const imageUploaderBox = document.getElementById('image-uploader-box');
323
+ const uploadText = document.querySelector('.upload-text');
324
+ const analyzeBtn = document.getElementById('analyze-btn');
325
+ const btnText = document.getElementById('btn-text');
326
+ const loader = document.getElementById('loader');
327
+ const promptInput = document.getElementById('prompt-input');
328
+ const resultsContainer = document.getElementById('results-container');
329
+ const errorMessage = document.getElementById('error-message');
330
+
331
+ let imageBase64 = null;
332
+
333
+ // --- 事件监听��图片选择 ---
334
+ imageInput.addEventListener('change', (event) => {
335
+ const file = event.target.files[0];
336
+ if (file) {
337
+ const reader = new FileReader();
338
+ reader.onload = (e) => {
339
+ imagePreview.src = e.target.result;
340
+ imagePreview.style.display = 'block';
341
+ uploadText.style.display = 'none';
342
+ // 转换为Base64
343
+ imageBase64 = e.target.result.split(',')[1];
344
+ };
345
+ reader.readAsDataURL(file);
346
+ }
347
+ });
348
+
349
+ // --- 事件监听:分析按钮点击 ---
350
+ analyzeBtn.addEventListener('click', async () => {
351
+ if (!imageBase64 || !promptInput.value.trim()) {
352
+ showError("请务必上传一张图片并填写病历信息。");
353
+ return;
354
+ }
355
+
356
+ // --- UI状态:开始分析 ---
357
+ toggleLoading(true);
358
+ resultsContainer.style.display = 'none';
359
+ errorMessage.style.display = 'none';
360
+
361
+ try {
362
+ const response = await fetch('/api/analyze', {
363
+ method: 'POST',
364
+ headers: { 'Content-Type': 'application/json' },
365
+ body: JSON.stringify({
366
+ image: imageBase64,
367
+ text: promptInput.value
368
+ })
369
+ });
370
+
371
+ if (!response.ok) {
372
+ const errData = await response.json();
373
+ throw new Error(errData.error || `HTTP 错误! 状态: ${response.status}`);
374
+ }
375
+
376
+ const data = await response.json();
377
+ displayResults(data);
378
+
379
+ } catch (error) {
380
+ showError(`分析失败: ${error.message}`);
381
+ } finally {
382
+ // --- UI状态:分析结束 ---
383
+ toggleLoading(false);
384
+ }
385
+ });
386
+
387
+ // --- 功能函数:切换加载状态 ---
388
+ function toggleLoading(isLoading) {
389
+ analyzeBtn.disabled = isLoading;
390
+ if (isLoading) {
391
+ btnText.textContent = "分析中...";
392
+ loader.style.display = 'block';
393
+ } else {
394
+ btnText.textContent = "开始智能分析";
395
+ loader.style.display = 'none';
396
+ }
397
+ }
398
+
399
+ // --- 功能函数:显示错误信息 ---
400
+ function showError(message) {
401
+ errorMessage.textContent = message;
402
+ errorMessage.style.display = 'block';
403
+ }
404
+
405
+ // --- 功能函数:渲染结果到页面 ---
406
+ function displayResults(data) {
407
+ // 鉴别诊断
408
+ const diagnosisContent = document.getElementById('differential-diagnosis-content');
409
+ diagnosisContent.innerHTML = data.differential_diagnosis.map(item => `
410
+ <div class="diagnosis-item">
411
+ <strong>${item.diagnosis}</strong>
412
+ <span class="likelihood likelihood-${item.likelihood}">${item.likelihood}</span>
413
+ <p style="font-size:0.9rem; color:#5f6368; margin-top:5px;">依据: ${item.evidence}</p>
414
+ </div>
415
+ `).join('');
416
+
417
+ // 图像特征
418
+ document.getElementById('image-features-content').innerHTML = data.image_features.map(item => `<li>${item}</li>`).join('');
419
+
420
+ // 综合分析
421
+ document.getElementById('comprehensive-analysis-content').textContent = data.comprehensive_analysis;
422
+
423
+ // 建议
424
+ document.getElementById('further-examinations-content').innerHTML = data.recommendations.further_examinations.map(item => `<li>${item}</li>`).join('');
425
+ document.getElementById('treatment-suggestions-content').innerHTML = data.recommendations.treatment_suggestions.map(item => `<li>${item}</li>`).join('');
426
+
427
+ resultsContainer.style.display = 'block';
428
+ // 平滑滚动到结果区域
429
+ resultsContainer.scrollIntoView({ behavior: 'smooth' });
430
+ }
431
+ </script>
432
+
433
+ </body>
434
+ </html>