capta1n commited on
Commit
78955e5
·
verified ·
1 Parent(s): bfbc565

请你设计一个安全评审页,这是一个用户提交需求方案和填写评审问卷的提交页面,需求细节:安全评审页面 需求方案提交: ● 需求名称:输入框 ● 需求内容:输入框,输入框中的内容提示:请输入aone需求地址/语雀/钉钉文档链接 评审类型:展示配置的问卷名称,例大模型评审、数据合规评审等,用户只能单选一个 信息收集内容:展示问卷内容的内容,这是一个类似用户调研问卷的内容 问卷示例 ● 应用模型的业务场景描述 ● 服务开放范围 ● 淘天集团内部对外开放 ● 服务使用方 ● C端用户B端商家集团内部员工/集团业务其他 ● 调用AIGC服务的应用名 ● 相关文档链接 ● 业务场景上线时间 需求提交:点击后提交需求, - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +667 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Pingsheng
3
- emoji: 🐢
4
- colorFrom: yellow
5
- colorTo: indigo
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: pingsheng
3
+ emoji: 🐳
4
+ colorFrom: purple
5
+ colorTo: yellow
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,667 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>安全评审提交</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ .fade-in {
11
+ animation: fadeIn 0.3s ease-in-out;
12
+ }
13
+ @keyframes fadeIn {
14
+ from { opacity: 0; transform: translateY(10px); }
15
+ to { opacity: 1; transform: translateY(0); }
16
+ }
17
+ .custom-radio:checked + label {
18
+ background-color: #3b82f6;
19
+ color: white;
20
+ border-color: #3b82f6;
21
+ }
22
+ .question-required:after {
23
+ content: " *";
24
+ color: #ef4444;
25
+ }
26
+ </style>
27
+ </head>
28
+ <body class="bg-gray-50 min-h-screen">
29
+ <div class="container mx-auto px-4 py-8 max-w-4xl">
30
+ <!-- Header -->
31
+ <header class="mb-8">
32
+ <h1 class="text-3xl font-bold text-gray-800 flex items-center">
33
+ <i class="fas fa-shield-alt text-blue-500 mr-3"></i> 安全评审提交
34
+ </h1>
35
+ <p class="text-gray-600 mt-2">请填写以下信息以提交安全评审需求</p>
36
+ </header>
37
+
38
+ <!-- Main Form -->
39
+ <div class="bg-white rounded-lg shadow-md p-6 mb-8 fade-in">
40
+ <!-- Progress Steps -->
41
+ <div class="flex justify-between mb-8 relative">
42
+ <div class="flex-1 flex items-center">
43
+ <div class="w-8 h-8 rounded-full bg-blue-500 text-white flex items-center justify-center font-bold">1</div>
44
+ <div class="ml-2 text-sm font-medium text-blue-500">基本信息</div>
45
+ </div>
46
+ <div class="flex-1 flex items-center justify-center">
47
+ <div class="h-1 w-full bg-gray-200"></div>
48
+ </div>
49
+ <div class="flex-1 flex items-center">
50
+ <div class="w-8 h-8 rounded-full bg-gray-200 text-gray-600 flex items-center justify-center font-bold">2</div>
51
+ <div class="ml-2 text-sm font-medium text-gray-500">评审问卷</div>
52
+ </div>
53
+ <div class="flex-1 flex items-center justify-center">
54
+ <div class="h-1 w-full bg-gray-200"></div>
55
+ </div>
56
+ <div class="flex-1 flex items-center">
57
+ <div class="w-8 h-8 rounded-full bg-gray-200 text-gray-600 flex items-center justify-center font-bold">3</div>
58
+ <div class="ml-2 text-sm font-medium text-gray-500">提交确认</div>
59
+ </div>
60
+ </div>
61
+
62
+ <!-- Step 1: Basic Information -->
63
+ <div id="step1" class="fade-in">
64
+ <h2 class="text-xl font-semibold text-gray-800 mb-6 flex items-center">
65
+ <i class="fas fa-info-circle text-blue-500 mr-2"></i> 需求基本信息
66
+ </h2>
67
+
68
+ <!-- Requirement Name -->
69
+ <div class="mb-6">
70
+ <label for="requirementName" class="block text-sm font-medium text-gray-700 mb-1 question-required">需求名称</label>
71
+ <input type="text" id="requirementName" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500" placeholder="请输入需求名称" required>
72
+ </div>
73
+
74
+ <!-- Requirement Content -->
75
+ <div class="mb-6">
76
+ <label for="requirementContent" class="block text-sm font-medium text-gray-700 mb-1 question-required">需求内容</label>
77
+ <textarea id="requirementContent" rows="3" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500" placeholder="请输入aone需求地址/语雀/钉钉文档链接" required></textarea>
78
+ </div>
79
+
80
+ <!-- Review Type -->
81
+ <div class="mb-6">
82
+ <label class="block text-sm font-medium text-gray-700 mb-2 question-required">评审类型</label>
83
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-3">
84
+ <div>
85
+ <input type="radio" id="reviewType1" name="reviewType" value="大模型评审" class="hidden custom-radio" required>
86
+ <label for="reviewType1" class="block w-full p-4 border border-gray-300 rounded-md cursor-pointer text-center hover:bg-blue-50 transition">
87
+ <i class="fas fa-brain text-blue-500 text-xl mb-2"></i>
88
+ <div class="font-medium">大模型评审</div>
89
+ </label>
90
+ </div>
91
+ <div>
92
+ <input type="radio" id="reviewType2" name="reviewType" value="数据合规评审" class="hidden custom-radio">
93
+ <label for="reviewType2" class="block w-full p-4 border border-gray-300 rounded-md cursor-pointer text-center hover:bg-blue-50 transition">
94
+ <i class="fas fa-database text-blue-500 text-xl mb-2"></i>
95
+ <div class="font-medium">数据合规评审</div>
96
+ </label>
97
+ </div>
98
+ <div>
99
+ <input type="radio" id="reviewType3" name="reviewType" value="API安全评审" class="hidden custom-radio">
100
+ <label for="reviewType3" class="block w-full p-4 border border-gray-300 rounded-md cursor-pointer text-center hover:bg-blue-50 transition">
101
+ <i class="fas fa-code text-blue-500 text-xl mb-2"></i>
102
+ <div class="font-medium">API安全评审</div>
103
+ </label>
104
+ </div>
105
+ <div>
106
+ <input type="radio" id="reviewType4" name="reviewType" value="隐私合规评审" class="hidden custom-radio">
107
+ <label for="reviewType4" class="block w-full p-4 border border-gray-300 rounded-md cursor-pointer text-center hover:bg-blue-50 transition">
108
+ <i class="fas fa-user-shield text-blue-500 text-xl mb-2"></i>
109
+ <div class="font-medium">隐私合规评审</div>
110
+ </label>
111
+ </div>
112
+ </div>
113
+ </div>
114
+
115
+ <div class="flex justify-end mt-8">
116
+ <button id="nextStep1" class="px-6 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition flex items-center">
117
+ 下一步 <i class="fas fa-arrow-right ml-2"></i>
118
+ </button>
119
+ </div>
120
+ </div>
121
+
122
+ <!-- Step 2: Questionnaire -->
123
+ <div id="step2" class="hidden fade-in">
124
+ <h2 class="text-xl font-semibold text-gray-800 mb-6 flex items-center">
125
+ <i class="fas fa-clipboard-list text-blue-500 mr-2"></i> 评审问卷
126
+ </h2>
127
+
128
+ <div class="mb-8">
129
+ <div class="bg-blue-50 border-l-4 border-blue-500 p-4 mb-6">
130
+ <div class="flex">
131
+ <div class="flex-shrink-0">
132
+ <i class="fas fa-info-circle text-blue-500"></i>
133
+ </div>
134
+ <div class="ml-3">
135
+ <p class="text-sm text-blue-700">
136
+ 请根据您选择的评审类型填写以下问卷,所有带 <span class="text-red-500">*</span> 的问题为必填项
137
+ </p>
138
+ </div>
139
+ </div>
140
+ </div>
141
+
142
+ <!-- Dynamic Questionnaire Content -->
143
+ <div id="questionnaireContent">
144
+ <!-- This will be filled by JavaScript based on selected review type -->
145
+ </div>
146
+ </div>
147
+
148
+ <div class="flex justify-between mt-8">
149
+ <button id="prevStep2" class="px-6 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300 transition flex items-center">
150
+ <i class="fas fa-arrow-left mr-2"></i> 上一步
151
+ </button>
152
+ <button id="nextStep2" class="px-6 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition flex items-center">
153
+ 下一步 <i class="fas fa-arrow-right ml-2"></i>
154
+ </button>
155
+ </div>
156
+ </div>
157
+
158
+ <!-- Step 3: Confirmation -->
159
+ <div id="step3" class="hidden fade-in">
160
+ <h2 class="text-xl font-semibold text-gray-800 mb-6 flex items-center">
161
+ <i class="fas fa-check-circle text-blue-500 mr-2"></i> 提交确认
162
+ </h2>
163
+
164
+ <div class="bg-gray-50 p-6 rounded-lg mb-8">
165
+ <h3 class="text-lg font-medium text-gray-800 mb-4">请确认您的提交信息</h3>
166
+
167
+ <div class="space-y-4">
168
+ <div>
169
+ <h4 class="text-sm font-medium text-gray-500">需求名称</h4>
170
+ <p id="confirmName" class="text-gray-800"></p>
171
+ </div>
172
+ <div>
173
+ <h4 class="text-sm font-medium text-gray-500">需求内容</h4>
174
+ <p id="confirmContent" class="text-gray-800"></p>
175
+ </div>
176
+ <div>
177
+ <h4 class="text-sm font-medium text-gray-500">评审类型</h4>
178
+ <p id="confirmType" class="text-gray-800"></p>
179
+ </div>
180
+ </div>
181
+
182
+ <div id="confirmQuestions" class="mt-6 space-y-4">
183
+ <!-- Questionnaire answers will be inserted here -->
184
+ </div>
185
+ </div>
186
+
187
+ <div class="flex justify-between mt-8">
188
+ <button id="prevStep3" class="px-6 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300 transition flex items-center">
189
+ <i class="fas fa-arrow-left mr-2"></i> 上一步
190
+ </button>
191
+ <button id="submitForm" class="px-6 py-2 bg-green-500 text-white rounded-md hover:bg-green-600 transition flex items-center">
192
+ <i class="fas fa-paper-plane mr-2"></i> 提交需求
193
+ </button>
194
+ </div>
195
+ </div>
196
+ </div>
197
+
198
+ <!-- Success Modal -->
199
+ <div id="successModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50">
200
+ <div class="bg-white rounded-lg p-8 max-w-md mx-4 fade-in">
201
+ <div class="text-center">
202
+ <div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-green-100">
203
+ <i class="fas fa-check text-green-500 text-xl"></i>
204
+ </div>
205
+ <h3 class="text-lg font-medium text-gray-900 mt-3">提交成功!</h3>
206
+ <div class="mt-2">
207
+ <p class="text-sm text-gray-500">您的安全评审需求已成功提交,我们会尽快处理。</p>
208
+ </div>
209
+ <div class="mt-6">
210
+ <button id="closeModal" class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition">
211
+ 关闭
212
+ </button>
213
+ </div>
214
+ </div>
215
+ </div>
216
+ </div>
217
+ </div>
218
+
219
+ <script>
220
+ document.addEventListener('DOMContentLoaded', function() {
221
+ // Step navigation
222
+ const step1 = document.getElementById('step1');
223
+ const step2 = document.getElementById('step2');
224
+ const step3 = document.getElementById('step3');
225
+ const nextStep1 = document.getElementById('nextStep1');
226
+ const prevStep2 = document.getElementById('prevStep2');
227
+ const nextStep2 = document.getElementById('nextStep2');
228
+ const prevStep3 = document.getElementById('prevStep3');
229
+ const submitForm = document.getElementById('submitForm');
230
+ const successModal = document.getElementById('successModal');
231
+ const closeModal = document.getElementById('closeModal');
232
+
233
+ // Questionnaire templates
234
+ const questionnaireTemplates = {
235
+ '大模型评审': [
236
+ {
237
+ question: '应用模型的业务场景描述',
238
+ required: true,
239
+ type: 'textarea'
240
+ },
241
+ {
242
+ question: '服务开放范围',
243
+ required: true,
244
+ type: 'radio',
245
+ options: ['淘天集团内部', '对外开放']
246
+ },
247
+ {
248
+ question: '服务使用方',
249
+ required: true,
250
+ type: 'checkbox',
251
+ options: ['C端用户', 'B端商家', '集团内部员工', '集团业务其他']
252
+ },
253
+ {
254
+ question: '调用AIGC服务的应用名',
255
+ required: true,
256
+ type: 'text'
257
+ },
258
+ {
259
+ question: '相关文档链接',
260
+ required: false,
261
+ type: 'text'
262
+ },
263
+ {
264
+ question: '业务场景上线时间',
265
+ required: true,
266
+ type: 'date'
267
+ }
268
+ ],
269
+ '数据合规评审': [
270
+ {
271
+ question: '数据处理类型',
272
+ required: true,
273
+ type: 'checkbox',
274
+ options: ['个人数据', '敏感数据', '业务数据', '日志数据']
275
+ },
276
+ {
277
+ question: '数据存储位置',
278
+ required: true,
279
+ type: 'text',
280
+ placeholder: '请输入数据存储的具体位置'
281
+ },
282
+ {
283
+ question: '数据访问权限控制方式',
284
+ required: true,
285
+ type: 'textarea'
286
+ },
287
+ {
288
+ question: '数据加密方式',
289
+ required: true,
290
+ type: 'text'
291
+ },
292
+ {
293
+ question: '数据保留期限',
294
+ required: true,
295
+ type: 'text'
296
+ }
297
+ ],
298
+ 'API安全评审': [
299
+ {
300
+ question: 'API用途描述',
301
+ required: true,
302
+ type: 'textarea'
303
+ },
304
+ {
305
+ question: 'API认证方式',
306
+ required: true,
307
+ type: 'checkbox',
308
+ options: ['OAuth2.0', 'API Key', 'JWT', 'Basic Auth']
309
+ },
310
+ {
311
+ question: 'API调用频率限制',
312
+ required: true,
313
+ type: 'text'
314
+ },
315
+ {
316
+ question: '敏感数据是否通过API传输',
317
+ required: true,
318
+ type: 'radio',
319
+ options: ['是', '否']
320
+ },
321
+ {
322
+ question: 'API文档链接',
323
+ required: false,
324
+ type: 'text'
325
+ }
326
+ ],
327
+ '隐私合规评审': [
328
+ {
329
+ question: '涉及的个人数据类型',
330
+ required: true,
331
+ type: 'checkbox',
332
+ options: ['姓名', '电话', '身份证号', '地址', '生物特征']
333
+ },
334
+ {
335
+ question: '数据收集方式',
336
+ required: true,
337
+ type: 'textarea'
338
+ },
339
+ {
340
+ question: '用户同意获取方式',
341
+ required: true,
342
+ type: 'radio',
343
+ options: ['明示同意', '默示同意', '其他']
344
+ },
345
+ {
346
+ question: '隐私政策链接',
347
+ required: true,
348
+ type: 'text'
349
+ },
350
+ {
351
+ question: '数据主体权利实现方式',
352
+ required: true,
353
+ type: 'textarea'
354
+ }
355
+ ]
356
+ };
357
+
358
+ // Event listeners for step navigation
359
+ nextStep1.addEventListener('click', function() {
360
+ if (validateStep1()) {
361
+ step1.classList.add('hidden');
362
+ step2.classList.remove('hidden');
363
+ loadQuestionnaire();
364
+ updateProgress(2);
365
+ }
366
+ });
367
+
368
+ prevStep2.addEventListener('click', function() {
369
+ step2.classList.add('hidden');
370
+ step1.classList.remove('hidden');
371
+ updateProgress(1);
372
+ });
373
+
374
+ nextStep2.addEventListener('click', function() {
375
+ if (validateStep2()) {
376
+ step2.classList.add('hidden');
377
+ step3.classList.remove('hidden');
378
+ populateConfirmation();
379
+ updateProgress(3);
380
+ }
381
+ });
382
+
383
+ prevStep3.addEventListener('click', function() {
384
+ step3.classList.add('hidden');
385
+ step2.classList.remove('hidden');
386
+ updateProgress(2);
387
+ });
388
+
389
+ submitForm.addEventListener('click', function() {
390
+ // In a real app, you would submit the form data to a server here
391
+ successModal.classList.remove('hidden');
392
+ });
393
+
394
+ closeModal.addEventListener('click', function() {
395
+ successModal.classList.add('hidden');
396
+ // Reset form
397
+ document.getElementById('requirementName').value = '';
398
+ document.getElementById('requirementContent').value = '';
399
+ document.querySelectorAll('input[name="reviewType"]').forEach(radio => {
400
+ radio.checked = false;
401
+ });
402
+ document.getElementById('questionnaireContent').innerHTML = '';
403
+ document.getElementById('confirmQuestions').innerHTML = '';
404
+
405
+ // Go back to step 1
406
+ step3.classList.add('hidden');
407
+ step1.classList.remove('hidden');
408
+ updateProgress(1);
409
+ });
410
+
411
+ // Helper functions
412
+ function validateStep1() {
413
+ let isValid = true;
414
+
415
+ // Validate requirement name
416
+ const nameInput = document.getElementById('requirementName');
417
+ if (!nameInput.value.trim()) {
418
+ showError(nameInput, '请输入需求名称');
419
+ isValid = false;
420
+ } else {
421
+ clearError(nameInput);
422
+ }
423
+
424
+ // Validate requirement content
425
+ const contentInput = document.getElementById('requirementContent');
426
+ if (!contentInput.value.trim()) {
427
+ showError(contentInput, '请输入需求内容');
428
+ isValid = false;
429
+ } else {
430
+ clearError(contentInput);
431
+ }
432
+
433
+ // Validate review type
434
+ const reviewTypeSelected = document.querySelector('input[name="reviewType"]:checked');
435
+ if (!reviewTypeSelected) {
436
+ showError(document.querySelector('.grid'), '请选择评审类型');
437
+ isValid = false;
438
+ } else {
439
+ clearError(document.querySelector('.grid'));
440
+ }
441
+
442
+ return isValid;
443
+ }
444
+
445
+ function validateStep2() {
446
+ let isValid = true;
447
+ const questions = document.querySelectorAll('.question-item');
448
+
449
+ questions.forEach(question => {
450
+ const required = question.dataset.required === 'true';
451
+ const type = question.dataset.type;
452
+ const questionId = question.id;
453
+
454
+ if (required) {
455
+ if (type === 'text' || type === 'textarea') {
456
+ const input = question.querySelector('input, textarea');
457
+ if (!input.value.trim()) {
458
+ showError(input, '此问题为必填项');
459
+ isValid = false;
460
+ } else {
461
+ clearError(input);
462
+ }
463
+ } else if (type === 'radio') {
464
+ const selected = question.querySelector('input[type="radio"]:checked');
465
+ if (!selected) {
466
+ showError(question.querySelector('.options-container'), '请选择至少一个选项');
467
+ isValid = false;
468
+ } else {
469
+ clearError(question.querySelector('.options-container'));
470
+ }
471
+ } else if (type === 'checkbox') {
472
+ const selected = question.querySelectorAll('input[type="checkbox"]:checked');
473
+ if (selected.length === 0) {
474
+ showError(question.querySelector('.options-container'), '请选择至少一个选项');
475
+ isValid = false;
476
+ } else {
477
+ clearError(question.querySelector('.options-container'));
478
+ }
479
+ }
480
+ }
481
+ });
482
+
483
+ return isValid;
484
+ }
485
+
486
+ function showError(element, message) {
487
+ // Remove any existing error messages
488
+ const existingError = element.nextElementSibling;
489
+ if (existingError && existingError.classList.contains('error-message')) {
490
+ existingError.remove();
491
+ }
492
+
493
+ // Add error class to element
494
+ if (element.classList) {
495
+ element.classList.add('border-red-500');
496
+ }
497
+
498
+ // Create and append error message
499
+ const errorMessage = document.createElement('p');
500
+ errorMessage.className = 'error-message mt-1 text-sm text-red-600';
501
+ errorMessage.textContent = message;
502
+ element.parentNode.insertBefore(errorMessage, element.nextSibling);
503
+ }
504
+
505
+ function clearError(element) {
506
+ // Remove error class
507
+ if (element.classList) {
508
+ element.classList.remove('border-red-500');
509
+ }
510
+
511
+ // Remove error message
512
+ const errorMessage = element.nextElementSibling;
513
+ if (errorMessage && errorMessage.classList.contains('error-message')) {
514
+ errorMessage.remove();
515
+ }
516
+ }
517
+
518
+ function loadQuestionnaire() {
519
+ const questionnaireContent = document.getElementById('questionnaireContent');
520
+ questionnaireContent.innerHTML = '';
521
+
522
+ const selectedReviewType = document.querySelector('input[name="reviewType"]:checked').value;
523
+ const questions = questionnaireTemplates[selectedReviewType];
524
+
525
+ questions.forEach((q, index) => {
526
+ const questionItem = document.createElement('div');
527
+ questionItem.className = 'question-item mb-6';
528
+ questionItem.id = `question-${index}`;
529
+ questionItem.dataset.required = q.required;
530
+ questionItem.dataset.type = q.type;
531
+
532
+ const label = document.createElement('label');
533
+ label.className = 'block text-sm font-medium text-gray-700 mb-1';
534
+ label.textContent = q.question;
535
+ if (q.required) {
536
+ label.classList.add('question-required');
537
+ }
538
+
539
+ questionItem.appendChild(label);
540
+
541
+ // Create input based on type
542
+ if (q.type === 'text') {
543
+ const input = document.createElement('input');
544
+ input.type = 'text';
545
+ input.className = 'w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500';
546
+ if (q.placeholder) {
547
+ input.placeholder = q.placeholder;
548
+ }
549
+ questionItem.appendChild(input);
550
+ } else if (q.type === 'textarea') {
551
+ const textarea = document.createElement('textarea');
552
+ textarea.className = 'w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500';
553
+ textarea.rows = 3;
554
+ questionItem.appendChild(textarea);
555
+ } else if (q.type === 'radio' || q.type === 'checkbox') {
556
+ const optionsContainer = document.createElement('div');
557
+ optionsContainer.className = 'options-container mt-2 space-y-2';
558
+
559
+ q.options.forEach((option, optIndex) => {
560
+ const optionDiv = document.createElement('div');
561
+ optionDiv.className = 'flex items-center';
562
+
563
+ const input = document.createElement('input');
564
+ input.type = q.type;
565
+ input.id = `${q.type}-${index}-${optIndex}`;
566
+ input.name = q.type === 'radio' ? `question-${index}` : `question-${index}-${optIndex}`;
567
+ input.value = option;
568
+ input.className = 'h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300';
569
+
570
+ const optionLabel = document.createElement('label');
571
+ optionLabel.htmlFor = `${q.type}-${index}-${optIndex}`;
572
+ optionLabel.className = 'ml-2 text-sm text-gray-700';
573
+ optionLabel.textContent = option;
574
+
575
+ optionDiv.appendChild(input);
576
+ optionDiv.appendChild(optionLabel);
577
+ optionsContainer.appendChild(optionDiv);
578
+ });
579
+
580
+ questionItem.appendChild(optionsContainer);
581
+ } else if (q.type === 'date') {
582
+ const input = document.createElement('input');
583
+ input.type = 'date';
584
+ input.className = 'w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500';
585
+ questionItem.appendChild(input);
586
+ }
587
+
588
+ questionnaireContent.appendChild(questionItem);
589
+ });
590
+ }
591
+
592
+ function populateConfirmation() {
593
+ // Basic info
594
+ document.getElementById('confirmName').textContent = document.getElementById('requirementName').value;
595
+ document.getElementById('confirmContent').textContent = document.getElementById('requirementContent').value;
596
+ document.getElementById('confirmType').textContent = document.querySelector('input[name="reviewType"]:checked').value;
597
+
598
+ // Questionnaire answers
599
+ const confirmQuestions = document.getElementById('confirmQuestions');
600
+ confirmQuestions.innerHTML = '';
601
+
602
+ const questions = document.querySelectorAll('.question-item');
603
+ questions.forEach(question => {
604
+ const questionDiv = document.createElement('div');
605
+ questionDiv.className = 'border-b border-gray-200 pb-4';
606
+
607
+ const questionTitle = document.createElement('h4');
608
+ questionTitle.className = 'text-sm font-medium text-gray-700';
609
+ questionTitle.textContent = question.querySelector('label').textContent.replace(' *', '');
610
+ questionDiv.appendChild(questionTitle);
611
+
612
+ const answerDiv = document.createElement('div');
613
+ answerDiv.className = 'mt-1 text-gray-800';
614
+
615
+ const type = question.dataset.type;
616
+ if (type === 'text' || type === 'textarea' || type === 'date') {
617
+ const input = question.querySelector('input, textarea');
618
+ answerDiv.textContent = input.value || '未填写';
619
+ } else if (type === 'radio') {
620
+ const selected = question.querySelector('input[type="radio"]:checked');
621
+ answerDiv.textContent = selected ? selected.value : '未选择';
622
+ } else if (type === 'checkbox') {
623
+ const selected = question.querySelectorAll('input[type="checkbox"]:checked');
624
+ if (selected.length > 0) {
625
+ const selectedValues = Array.from(selected).map(checkbox => checkbox.value).join(', ');
626
+ answerDiv.textContent = selectedValues;
627
+ } else {
628
+ answerDiv.textContent = '未选择';
629
+ }
630
+ }
631
+
632
+ questionDiv.appendChild(answerDiv);
633
+ confirmQuestions.appendChild(questionDiv);
634
+ });
635
+ }
636
+
637
+ function updateProgress(step) {
638
+ const progressSteps = document.querySelectorAll('.flex-1.flex.items-center');
639
+ progressSteps.forEach((stepElement, index) => {
640
+ const number = stepElement.querySelector('div');
641
+ const text = stepElement.querySelector('div + div');
642
+
643
+ if (index < step) {
644
+ // Completed steps
645
+ number.classList.remove('bg-gray-200', 'text-gray-600');
646
+ number.classList.add('bg-blue-500', 'text-white');
647
+ text.classList.remove('text-gray-500');
648
+ text.classList.add('text-blue-500');
649
+ } else if (index === step) {
650
+ // Current step
651
+ number.classList.remove('bg-blue-500', 'text-white');
652
+ number.classList.add('bg-blue-200', 'text-blue-700');
653
+ text.classList.remove('text-gray-500');
654
+ text.classList.add('text-blue-700');
655
+ } else {
656
+ // Future steps
657
+ number.classList.remove('bg-blue-500', 'text-white', 'bg-blue-200', 'text-blue-700');
658
+ number.classList.add('bg-gray-200', 'text-gray-600');
659
+ text.classList.remove('text-blue-500', 'text-blue-700');
660
+ text.classList.add('text-gray-500');
661
+ }
662
+ });
663
+ }
664
+ });
665
+ </script>
666
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=capta1n/pingsheng" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
667
+ </html>