|
|
@@ -96,8 +96,6 @@ const questions = ref<Question[]>([
|
|
96
|
96
|
},
|
|
97
|
97
|
]);
|
|
98
|
98
|
|
|
99
|
|
-
|
|
100
|
|
-
|
|
101
|
99
|
// 填空题替换方法
|
|
102
|
100
|
const replaceBlanks = (content: string) => {
|
|
103
|
101
|
return content.replaceAll(
|
|
|
@@ -106,13 +104,11 @@ const replaceBlanks = (content: string) => {
|
|
106
|
104
|
);
|
|
107
|
105
|
};
|
|
108
|
106
|
|
|
109
|
|
-
|
|
110
|
|
-
|
|
111
|
107
|
// 弹窗相关
|
|
112
|
108
|
const isModalVisible = ref(false);
|
|
113
|
109
|
const modalTitle = ref('');
|
|
114
|
110
|
const currentQuestion = ref<null | Question>(null);
|
|
115
|
|
-const currentCategory = ref<null | Category>(null);
|
|
|
111
|
+const currentCategory = ref<Category | null>(null);
|
|
116
|
112
|
const newCategoryName = ref('');
|
|
117
|
113
|
const inputError = ref('');
|
|
118
|
114
|
|
|
|
@@ -161,7 +157,6 @@ const selectCategory = (id: number) => {
|
|
161
|
157
|
selectedCategory.value = id;
|
|
162
|
158
|
};
|
|
163
|
159
|
|
|
164
|
|
-
|
|
165
|
160
|
// 打开新增题目弹窗
|
|
166
|
161
|
const openAddModal = () => {
|
|
167
|
162
|
modalTitle.value = '添加题目';
|
|
|
@@ -177,8 +172,12 @@ const createNewQuestion = (type: QuestionType): Question => {
|
|
177
|
172
|
id: 0, // 临时ID,保存时会生成
|
|
178
|
173
|
type,
|
|
179
|
174
|
content: '',
|
|
180
|
|
- options: type === 'true_false' ? ['正确', '错误'] :
|
|
181
|
|
- type === 'single_choice' || type === 'multiple_choice' ? [''] : undefined,
|
|
|
175
|
+ options:
|
|
|
176
|
+ type === 'true_false'
|
|
|
177
|
+ ? ['正确', '错误']
|
|
|
178
|
+ : (type === 'single_choice' || type === 'multiple_choice'
|
|
|
179
|
+ ? ['']
|
|
|
180
|
+ : undefined),
|
|
182
|
181
|
answer: '',
|
|
183
|
182
|
};
|
|
184
|
183
|
};
|
|
|
@@ -200,25 +199,29 @@ const validateQuestion = (question: Question): boolean => {
|
|
200
|
199
|
alert('请输入题目内容');
|
|
201
|
200
|
return false;
|
|
202
|
201
|
}
|
|
203
|
|
-
|
|
204
|
|
- if (question.type === 'single_choice' || question.type === 'multiple_choice' || question.type === 'true_false') {
|
|
|
202
|
+
|
|
|
203
|
+ if (
|
|
|
204
|
+ question.type === 'single_choice' ||
|
|
|
205
|
+ question.type === 'multiple_choice' ||
|
|
|
206
|
+ question.type === 'true_false'
|
|
|
207
|
+ ) {
|
|
205
|
208
|
if (!question.options || question.options.length < 2) {
|
|
206
|
209
|
alert('请至少添加两个选项');
|
|
207
|
210
|
return false;
|
|
208
|
211
|
}
|
|
209
|
|
-
|
|
210
|
|
- const hasEmptyOption = question.options.some(option => !option.trim());
|
|
|
212
|
+
|
|
|
213
|
+ const hasEmptyOption = question.options.some((option) => !option.trim());
|
|
211
|
214
|
if (hasEmptyOption) {
|
|
212
|
215
|
alert('选项内容不能为空');
|
|
213
|
216
|
return false;
|
|
214
|
217
|
}
|
|
215
|
218
|
}
|
|
216
|
|
-
|
|
|
219
|
+
|
|
217
|
220
|
if (!question.answer.trim()) {
|
|
218
|
221
|
alert('请输入答案');
|
|
219
|
222
|
return false;
|
|
220
|
223
|
}
|
|
221
|
|
-
|
|
|
224
|
+
|
|
222
|
225
|
return true;
|
|
223
|
226
|
};
|
|
224
|
227
|
|
|
|
@@ -230,9 +233,9 @@ const saveAllQuestions = () => {
|
|
230
|
233
|
return;
|
|
231
|
234
|
}
|
|
232
|
235
|
}
|
|
233
|
|
-
|
|
|
236
|
+
|
|
234
|
237
|
// 为每个新题目生成唯一ID
|
|
235
|
|
- const maxId = Math.max(...questions.value.map(q => q.id), 0);
|
|
|
238
|
+ const maxId = Math.max(...questions.value.map((q) => q.id), 0);
|
|
236
|
239
|
newQuestions.value.forEach((q, index) => {
|
|
237
|
240
|
q.id = maxId + index + 1;
|
|
238
|
241
|
questions.value.push(q);
|
|
|
@@ -242,14 +245,14 @@ const saveAllQuestions = () => {
|
|
242
|
245
|
|
|
243
|
246
|
// 保存单个编辑的题目
|
|
244
|
247
|
const saveSingleQuestion = () => {
|
|
245
|
|
- if (currentQuestion.value) {
|
|
246
|
|
- if (validateQuestion(currentQuestion.value)) {
|
|
247
|
|
- const index = questions.value.findIndex(q => q.id === currentQuestion.value!.id);
|
|
248
|
|
- if (index !== -1) {
|
|
249
|
|
- questions.value[index] = { ...currentQuestion.value };
|
|
250
|
|
- }
|
|
251
|
|
- closeModal();
|
|
|
248
|
+ if (currentQuestion.value && validateQuestion(currentQuestion.value)) {
|
|
|
249
|
+ const index = questions.value.findIndex(
|
|
|
250
|
+ (q) => q.id === currentQuestion.value!.id,
|
|
|
251
|
+ );
|
|
|
252
|
+ if (index !== -1) {
|
|
|
253
|
+ questions.value[index] = { ...currentQuestion.value };
|
|
252
|
254
|
}
|
|
|
255
|
+ closeModal();
|
|
253
|
256
|
}
|
|
254
|
257
|
};
|
|
255
|
258
|
|
|
|
@@ -274,7 +277,9 @@ const openEditCategoryModal = (category: Category) => {
|
|
274
|
277
|
// 删除题目类型
|
|
275
|
278
|
const deleteCategory = (id: number) => {
|
|
276
|
279
|
if (confirm('确定要删除这个题目类型吗?')) {
|
|
277
|
|
- categories.value = categories.value.filter(category => category.id !== id);
|
|
|
280
|
+ categories.value = categories.value.filter(
|
|
|
281
|
+ (category) => category.id !== id,
|
|
|
282
|
+ );
|
|
278
|
283
|
// 如果删除的是当前选中的分类,切换到第一个分类
|
|
279
|
284
|
if (selectedCategory.value === id && categories.value.length > 0) {
|
|
280
|
285
|
selectedCategory.value = categories.value[0].id;
|
|
|
@@ -287,28 +292,30 @@ const saveCategory = () => {
|
|
287
|
292
|
if (!validateInput(newCategoryName.value)) {
|
|
288
|
293
|
return;
|
|
289
|
294
|
}
|
|
290
|
|
-
|
|
|
295
|
+
|
|
291
|
296
|
if (currentCategory.value) {
|
|
292
|
297
|
// 修改现有分类
|
|
293
|
|
- const index = categories.value.findIndex(cat => cat.id === currentCategory.value!.id);
|
|
|
298
|
+ const index = categories.value.findIndex(
|
|
|
299
|
+ (cat) => cat.id === currentCategory.value!.id,
|
|
|
300
|
+ );
|
|
294
|
301
|
if (index !== -1) {
|
|
295
|
302
|
categories.value[index] = {
|
|
296
|
303
|
...categories.value[index],
|
|
297
|
304
|
name: newCategoryName.value,
|
|
298
|
|
- updateTime: new Date().toISOString().split('T')[0]
|
|
|
305
|
+ updateTime: new Date().toISOString().split('T')[0],
|
|
299
|
306
|
};
|
|
300
|
307
|
}
|
|
301
|
308
|
} else {
|
|
302
|
309
|
// 添加新分类
|
|
303
|
|
- const newId = Math.max(...categories.value.map(cat => cat.id), 0) + 1;
|
|
|
310
|
+ const newId = Math.max(...categories.value.map((cat) => cat.id), 0) + 1;
|
|
304
|
311
|
categories.value.push({
|
|
305
|
312
|
id: newId,
|
|
306
|
313
|
name: newCategoryName.value,
|
|
307
|
314
|
updateTime: new Date().toISOString().split('T')[0],
|
|
308
|
|
- count: 0
|
|
|
315
|
+ count: 0,
|
|
309
|
316
|
});
|
|
310
|
317
|
}
|
|
311
|
|
-
|
|
|
318
|
+
|
|
312
|
319
|
closeModal();
|
|
313
|
320
|
};
|
|
314
|
321
|
|
|
|
@@ -404,18 +411,19 @@ onUnmounted(() => {
|
|
404
|
411
|
if (categoryListRef.value) {
|
|
405
|
412
|
categoryListRef.value.removeEventListener('scroll', handleScroll);
|
|
406
|
413
|
}
|
|
407
|
|
-});
|
|
|
414
|
+});
|
|
408
|
415
|
</script>
|
|
409
|
416
|
|
|
410
|
417
|
<template>
|
|
411
|
418
|
<Page>
|
|
412
|
|
-
|
|
413
|
419
|
<div class="question-bank-container">
|
|
414
|
420
|
<!-- 左侧题库分类 -->
|
|
415
|
421
|
<div class="left-panel">
|
|
416
|
422
|
<div class="panel-header">
|
|
417
|
423
|
<h2>题库分类</h2>
|
|
418
|
|
- <button class="add-btn" @click="openAddCategoryModal">添加题目类型</button>
|
|
|
424
|
+ <button class="add-btn" @click="openAddCategoryModal">
|
|
|
425
|
+ 添加题目类型
|
|
|
426
|
+ </button>
|
|
419
|
427
|
</div>
|
|
420
|
428
|
<div class="category-list" ref="categoryListRef">
|
|
421
|
429
|
<!-- @ts-ignore -->
|
|
|
@@ -432,8 +440,18 @@ onUnmounted(() => {
|
|
432
|
440
|
<span>题库数量: {{ category.count }}</span>
|
|
433
|
441
|
</div>
|
|
434
|
442
|
<div class="category-actions">
|
|
435
|
|
- <button class="edit-category-btn" @click.stop="openEditCategoryModal(category)">修改</button>
|
|
436
|
|
- <button class="delete-category-btn" @click.stop="deleteCategory(category.id)">删除</button>
|
|
|
443
|
+ <button
|
|
|
444
|
+ class="edit-category-btn"
|
|
|
445
|
+ @click.stop="openEditCategoryModal(category)"
|
|
|
446
|
+ >
|
|
|
447
|
+ 修改
|
|
|
448
|
+ </button>
|
|
|
449
|
+ <button
|
|
|
450
|
+ class="delete-category-btn"
|
|
|
451
|
+ @click.stop="deleteCategory(category.id)"
|
|
|
452
|
+ >
|
|
|
453
|
+ 删除
|
|
|
454
|
+ </button>
|
|
437
|
455
|
</div>
|
|
438
|
456
|
</div>
|
|
439
|
457
|
|
|
|
@@ -539,8 +557,6 @@ onUnmounted(() => {
|
|
539
|
557
|
</div>
|
|
540
|
558
|
</div>
|
|
541
|
559
|
</div>
|
|
542
|
|
-
|
|
543
|
|
-
|
|
544
|
560
|
</div>
|
|
545
|
561
|
</div>
|
|
546
|
562
|
</div>
|
|
|
@@ -553,205 +569,220 @@ onUnmounted(() => {
|
|
553
|
569
|
<button class="close-btn" @click="closeModal">×</button>
|
|
554
|
570
|
</div>
|
|
555
|
571
|
<div class="drawer-body">
|
|
556
|
|
- <!-- 题目类型表单 -->
|
|
557
|
|
- <div v-if="modalTitle.includes('题目类型')" class="category-form">
|
|
558
|
|
- <div class="form-item">
|
|
559
|
|
- <label class="form-label">题目类型名称</label>
|
|
560
|
|
- <input
|
|
561
|
|
- type="text"
|
|
562
|
|
- class="form-input"
|
|
563
|
|
- v-model="newCategoryName"
|
|
564
|
|
- placeholder="请输入题目类型名称(最多50个字)"
|
|
565
|
|
- maxlength="50"
|
|
566
|
|
- @input="validateInput(newCategoryName)"
|
|
567
|
|
- />
|
|
568
|
|
- <div v-if="inputError" class="error-message">{{ inputError }}</div>
|
|
569
|
|
- <div class="char-count">{{ newCategoryName.length }}/50</div>
|
|
570
|
|
- </div>
|
|
|
572
|
+ <!-- 题目类型表单 -->
|
|
|
573
|
+ <div v-if="modalTitle.includes('题目类型')" class="category-form">
|
|
|
574
|
+ <div class="form-item">
|
|
|
575
|
+ <label class="form-label">题目类型名称</label>
|
|
|
576
|
+ <input
|
|
|
577
|
+ type="text"
|
|
|
578
|
+ class="form-input"
|
|
|
579
|
+ v-model="newCategoryName"
|
|
|
580
|
+ placeholder="请输入题目类型名称(最多50个字)"
|
|
|
581
|
+ maxlength="50"
|
|
|
582
|
+ @input="validateInput(newCategoryName)"
|
|
|
583
|
+ />
|
|
|
584
|
+ <div v-if="inputError" class="error-message">{{ inputError }}</div>
|
|
|
585
|
+ <div class="char-count">{{ newCategoryName.length }}/50</div>
|
|
571
|
586
|
</div>
|
|
572
|
|
- <!-- 题目编辑表单 -->
|
|
573
|
|
- <div v-else-if="modalTitle === '编辑题目' && currentQuestion" class="question-form">
|
|
574
|
|
- <!-- 题目类型 -->
|
|
575
|
|
- <div class="form-item">
|
|
576
|
|
- <label class="form-label">题目类型</label>
|
|
577
|
|
- <div class="type-tabs">
|
|
578
|
|
- <button
|
|
579
|
|
- v-for="type in questionTypes"
|
|
580
|
|
- :key="type.value"
|
|
581
|
|
- class="type-tab"
|
|
582
|
|
- :class="{ active: currentQuestion.type === type.value }"
|
|
583
|
|
- @click="currentQuestion.type = type.value as QuestionType;
|
|
584
|
|
- if ((type.value === 'single_choice' || type.value === 'multiple_choice' || type.value === 'true_false') && !currentQuestion.options) {
|
|
585
|
|
- currentQuestion.options = type.value === 'true_false' ? ['正确', '错误'] : [''];
|
|
586
|
|
- } else if (type.value !== 'single_choice' && type.value !== 'multiple_choice' && type.value !== 'true_false') {
|
|
587
|
|
- currentQuestion.options = undefined;
|
|
588
|
|
- }"
|
|
589
|
|
- >
|
|
590
|
|
- {{ type.label }}
|
|
591
|
|
- </button>
|
|
592
|
|
- </div>
|
|
593
|
|
- </div>
|
|
594
|
|
-
|
|
595
|
|
- <!-- 题目内容 -->
|
|
596
|
|
- <div class="form-item">
|
|
597
|
|
- <label class="form-label">题目内容</label>
|
|
598
|
|
- <textarea
|
|
599
|
|
- class="form-textarea"
|
|
600
|
|
- v-model="currentQuestion.content"
|
|
601
|
|
- placeholder="请输入题目内容"
|
|
602
|
|
- ></textarea>
|
|
|
587
|
+ </div>
|
|
|
588
|
+ <!-- 题目编辑表单 -->
|
|
|
589
|
+ <div
|
|
|
590
|
+ v-else-if="modalTitle === '编辑题目' && currentQuestion"
|
|
|
591
|
+ class="question-form"
|
|
|
592
|
+ >
|
|
|
593
|
+ <!-- 题目类型 -->
|
|
|
594
|
+ <div class="form-item">
|
|
|
595
|
+ <label class="form-label">题目类型</label>
|
|
|
596
|
+ <div class="type-tabs">
|
|
|
597
|
+ <button
|
|
|
598
|
+ v-for="type in questionTypes"
|
|
|
599
|
+ :key="type.value"
|
|
|
600
|
+ class="type-tab"
|
|
|
601
|
+ :class="{ active: currentQuestion.type === type.value }"
|
|
|
602
|
+ @click="
|
|
|
603
|
+ currentQuestion.type = type.value as QuestionType;
|
|
|
604
|
+ if (
|
|
|
605
|
+ (type.value === 'single_choice' ||
|
|
|
606
|
+ type.value === 'multiple_choice' ||
|
|
|
607
|
+ type.value === 'true_false') &&
|
|
|
608
|
+ !currentQuestion.options
|
|
|
609
|
+ ) {
|
|
|
610
|
+ currentQuestion.options =
|
|
|
611
|
+ type.value === 'true_false' ? ['正确', '错误'] : [''];
|
|
|
612
|
+ } else if (
|
|
|
613
|
+ type.value !== 'single_choice' &&
|
|
|
614
|
+ type.value !== 'multiple_choice' &&
|
|
|
615
|
+ type.value !== 'true_false'
|
|
|
616
|
+ ) {
|
|
|
617
|
+ currentQuestion.options = undefined;
|
|
|
618
|
+ }
|
|
|
619
|
+ "
|
|
|
620
|
+ >
|
|
|
621
|
+ {{ type.label }}
|
|
|
622
|
+ </button>
|
|
603
|
623
|
</div>
|
|
604
|
|
-
|
|
605
|
|
- <!-- 选项 -->
|
|
606
|
|
- <div v-if="currentQuestion.options" class="form-item">
|
|
607
|
|
- <label class="form-label">选项</label>
|
|
608
|
|
- <div class="options-list">
|
|
609
|
|
- <div
|
|
610
|
|
- v-for="(option, index) in currentQuestion.options"
|
|
611
|
|
- :key="index"
|
|
612
|
|
- class="option-item"
|
|
613
|
|
- >
|
|
614
|
|
- <input
|
|
615
|
|
- type="text"
|
|
616
|
|
- class="form-input"
|
|
617
|
|
- v-model="currentQuestion.options![index]"
|
|
618
|
|
- placeholder="请输入选项内容"
|
|
619
|
|
- />
|
|
620
|
|
- <button
|
|
621
|
|
- class="remove-option-btn"
|
|
622
|
|
- @click="removeOption(currentQuestion, index)"
|
|
623
|
|
- :disabled="currentQuestion.options.length <= 1"
|
|
624
|
|
- >
|
|
625
|
|
- 删除
|
|
626
|
|
- </button>
|
|
627
|
|
- </div>
|
|
628
|
|
- <button
|
|
629
|
|
- class="add-option-btn"
|
|
630
|
|
- @click="addOption(currentQuestion)"
|
|
|
624
|
+ </div>
|
|
|
625
|
+
|
|
|
626
|
+ <!-- 题目内容 -->
|
|
|
627
|
+ <div class="form-item">
|
|
|
628
|
+ <label class="form-label">题目内容</label>
|
|
|
629
|
+ <textarea
|
|
|
630
|
+ class="form-textarea"
|
|
|
631
|
+ v-model="currentQuestion.content"
|
|
|
632
|
+ placeholder="请输入题目内容"
|
|
|
633
|
+ ></textarea>
|
|
|
634
|
+ </div>
|
|
|
635
|
+
|
|
|
636
|
+ <!-- 选项 -->
|
|
|
637
|
+ <div v-if="currentQuestion.options" class="form-item">
|
|
|
638
|
+ <label class="form-label">选项</label>
|
|
|
639
|
+ <div class="options-list">
|
|
|
640
|
+ <div
|
|
|
641
|
+ v-for="(option, index) in currentQuestion.options"
|
|
|
642
|
+ :key="index"
|
|
|
643
|
+ class="option-item"
|
|
|
644
|
+ >
|
|
|
645
|
+ <input
|
|
|
646
|
+ type="text"
|
|
|
647
|
+ class="form-input"
|
|
|
648
|
+ v-model="currentQuestion.options![index]"
|
|
|
649
|
+ placeholder="请输入选项内容"
|
|
|
650
|
+ />
|
|
|
651
|
+ <button
|
|
|
652
|
+ class="remove-option-btn"
|
|
|
653
|
+ @click="removeOption(currentQuestion, index)"
|
|
|
654
|
+ :disabled="currentQuestion.options.length <= 1"
|
|
631
|
655
|
>
|
|
632
|
|
- + 添加选项
|
|
|
656
|
+ 删除
|
|
633
|
657
|
</button>
|
|
634
|
658
|
</div>
|
|
|
659
|
+ <button
|
|
|
660
|
+ class="add-option-btn"
|
|
|
661
|
+ @click="addOption(currentQuestion)"
|
|
|
662
|
+ >
|
|
|
663
|
+ + 添加选项
|
|
|
664
|
+ </button>
|
|
635
|
665
|
</div>
|
|
636
|
|
-
|
|
637
|
|
- <!-- 答案 -->
|
|
638
|
|
- <div class="form-item">
|
|
639
|
|
- <label class="form-label">答案</label>
|
|
640
|
|
- <textarea
|
|
641
|
|
- class="form-textarea"
|
|
642
|
|
- v-model="currentQuestion.answer"
|
|
643
|
|
- placeholder="请输入答案"
|
|
644
|
|
- ></textarea>
|
|
|
666
|
+ </div>
|
|
|
667
|
+
|
|
|
668
|
+ <!-- 答案 -->
|
|
|
669
|
+ <div class="form-item">
|
|
|
670
|
+ <label class="form-label">答案</label>
|
|
|
671
|
+ <textarea
|
|
|
672
|
+ class="form-textarea"
|
|
|
673
|
+ v-model="currentQuestion.answer"
|
|
|
674
|
+ placeholder="请输入答案"
|
|
|
675
|
+ ></textarea>
|
|
|
676
|
+ </div>
|
|
|
677
|
+ </div>
|
|
|
678
|
+
|
|
|
679
|
+ <!-- 添加题目表单 -->
|
|
|
680
|
+ <div v-else-if="modalTitle === '添加题目'" class="question-form">
|
|
|
681
|
+ <!-- 题目类型选择 -->
|
|
|
682
|
+ <div class="form-item">
|
|
|
683
|
+ <label class="form-label">选择题目类型</label>
|
|
|
684
|
+ <div class="type-tabs">
|
|
|
685
|
+ <button
|
|
|
686
|
+ v-for="type in questionTypes"
|
|
|
687
|
+ :key="type.value"
|
|
|
688
|
+ class="type-tab"
|
|
|
689
|
+ @click="addNewQuestion(type.value as QuestionType)"
|
|
|
690
|
+ >
|
|
|
691
|
+ {{ type.label }}
|
|
|
692
|
+ </button>
|
|
645
|
693
|
</div>
|
|
646
|
694
|
</div>
|
|
647
|
|
-
|
|
648
|
|
- <!-- 添加题目表单 -->
|
|
649
|
|
- <div v-else-if="modalTitle === '添加题目'" class="question-form">
|
|
650
|
|
- <!-- 题目类型选择 -->
|
|
651
|
|
- <div class="form-item">
|
|
652
|
|
- <label class="form-label">选择题目类型</label>
|
|
653
|
|
- <div class="type-tabs">
|
|
654
|
|
- <button
|
|
655
|
|
- v-for="type in questionTypes"
|
|
656
|
|
- :key="type.value"
|
|
657
|
|
- class="type-tab"
|
|
658
|
|
- @click="addNewQuestion(type.value as QuestionType)"
|
|
|
695
|
+
|
|
|
696
|
+ <!-- 所有题目编辑区域 -->
|
|
|
697
|
+ <div v-if="newQuestions.length > 0" class="all-questions">
|
|
|
698
|
+ <div
|
|
|
699
|
+ v-for="(question, qIndex) in newQuestions"
|
|
|
700
|
+ :key="qIndex"
|
|
|
701
|
+ class="question-item-container"
|
|
|
702
|
+ >
|
|
|
703
|
+ <div class="question-header">
|
|
|
704
|
+ <h4>
|
|
|
705
|
+ 题目{{ qIndex + 1 }} {{ getQuestionTypeLabel(question.type) }}
|
|
|
706
|
+ </h4>
|
|
|
707
|
+ <button
|
|
|
708
|
+ class="delete-question-btn"
|
|
|
709
|
+ @click="newQuestions.splice(qIndex, 1)"
|
|
659
|
710
|
>
|
|
660
|
|
- {{ type.label }}
|
|
|
711
|
+ 删除题目
|
|
661
|
712
|
</button>
|
|
662
|
713
|
</div>
|
|
663
|
|
- </div>
|
|
664
|
|
-
|
|
665
|
|
- <!-- 所有题目编辑区域 -->
|
|
666
|
|
- <div v-if="newQuestions.length > 0" class="all-questions">
|
|
667
|
|
- <div
|
|
668
|
|
- v-for="(question, qIndex) in newQuestions"
|
|
669
|
|
- :key="qIndex"
|
|
670
|
|
- class="question-item-container"
|
|
671
|
|
- >
|
|
672
|
|
- <div class="question-header">
|
|
673
|
|
- <h4>题目{{ qIndex + 1 }} {{ getQuestionTypeLabel(question.type) }}</h4>
|
|
674
|
|
- <button
|
|
675
|
|
- class="delete-question-btn"
|
|
676
|
|
- @click="newQuestions.splice(qIndex, 1)"
|
|
|
714
|
+
|
|
|
715
|
+ <div class="form-item">
|
|
|
716
|
+ <label class="form-label">题目内容</label>
|
|
|
717
|
+ <textarea
|
|
|
718
|
+ class="form-textarea"
|
|
|
719
|
+ v-model="question.content"
|
|
|
720
|
+ placeholder="请输入题目内容"
|
|
|
721
|
+ ></textarea>
|
|
|
722
|
+ </div>
|
|
|
723
|
+
|
|
|
724
|
+ <!-- 选项 -->
|
|
|
725
|
+ <div v-if="question.options" class="form-item">
|
|
|
726
|
+ <label class="form-label">选项</label>
|
|
|
727
|
+ <div class="options-list">
|
|
|
728
|
+ <div
|
|
|
729
|
+ v-for="(option, optIndex) in question.options"
|
|
|
730
|
+ :key="optIndex"
|
|
|
731
|
+ class="option-item"
|
|
677
|
732
|
>
|
|
678
|
|
- 删除题目
|
|
679
|
|
- </button>
|
|
680
|
|
- </div>
|
|
681
|
|
-
|
|
682
|
|
- <div class="form-item">
|
|
683
|
|
- <label class="form-label">题目内容</label>
|
|
684
|
|
- <textarea
|
|
685
|
|
- class="form-textarea"
|
|
686
|
|
- v-model="question.content"
|
|
687
|
|
- placeholder="请输入题目内容"
|
|
688
|
|
- ></textarea>
|
|
689
|
|
- </div>
|
|
690
|
|
-
|
|
691
|
|
- <!-- 选项 -->
|
|
692
|
|
- <div v-if="question.options" class="form-item">
|
|
693
|
|
- <label class="form-label">选项</label>
|
|
694
|
|
- <div class="options-list">
|
|
695
|
|
- <div
|
|
696
|
|
- v-for="(option, optIndex) in question.options"
|
|
697
|
|
- :key="optIndex"
|
|
698
|
|
- class="option-item"
|
|
699
|
|
- >
|
|
700
|
|
- <input
|
|
701
|
|
- type="text"
|
|
702
|
|
- class="form-input"
|
|
703
|
|
- v-model="question.options![optIndex]"
|
|
704
|
|
- placeholder="请输入选项内容"
|
|
705
|
|
- />
|
|
706
|
|
- <button
|
|
707
|
|
- class="remove-option-btn"
|
|
708
|
|
- @click="removeOption(question, optIndex)"
|
|
709
|
|
- :disabled="question.options.length <= 1"
|
|
710
|
|
- >
|
|
711
|
|
- 删除
|
|
712
|
|
- </button>
|
|
713
|
|
- </div>
|
|
714
|
|
- <button
|
|
715
|
|
- class="add-option-btn"
|
|
716
|
|
- @click="addOption(question)"
|
|
|
733
|
+ <input
|
|
|
734
|
+ type="text"
|
|
|
735
|
+ class="form-input"
|
|
|
736
|
+ v-model="question.options![optIndex]"
|
|
|
737
|
+ placeholder="请输入选项内容"
|
|
|
738
|
+ />
|
|
|
739
|
+ <button
|
|
|
740
|
+ class="remove-option-btn"
|
|
|
741
|
+ @click="removeOption(question, optIndex)"
|
|
|
742
|
+ :disabled="question.options.length <= 1"
|
|
717
|
743
|
>
|
|
718
|
|
- + 添加选项
|
|
|
744
|
+ 删除
|
|
719
|
745
|
</button>
|
|
720
|
746
|
</div>
|
|
721
|
|
- </div>
|
|
722
|
|
-
|
|
723
|
|
- <!-- 答案 -->
|
|
724
|
|
- <div class="form-item">
|
|
725
|
|
- <label class="form-label">答案</label>
|
|
726
|
|
- <textarea
|
|
727
|
|
- class="form-textarea"
|
|
728
|
|
- v-model="question.answer"
|
|
729
|
|
- placeholder="请输入答案"
|
|
730
|
|
- ></textarea>
|
|
|
747
|
+ <button class="add-option-btn" @click="addOption(question)">
|
|
|
748
|
+ + 添加选项
|
|
|
749
|
+ </button>
|
|
731
|
750
|
</div>
|
|
732
|
751
|
</div>
|
|
|
752
|
+
|
|
|
753
|
+ <!-- 答案 -->
|
|
|
754
|
+ <div class="form-item">
|
|
|
755
|
+ <label class="form-label">答案</label>
|
|
|
756
|
+ <textarea
|
|
|
757
|
+ class="form-textarea"
|
|
|
758
|
+ v-model="question.answer"
|
|
|
759
|
+ placeholder="请输入答案"
|
|
|
760
|
+ ></textarea>
|
|
|
761
|
+ </div>
|
|
733
|
762
|
</div>
|
|
734
|
763
|
</div>
|
|
735
|
|
-
|
|
736
|
|
- </div>
|
|
737
|
|
- <div class="drawer-footer">
|
|
|
764
|
+ </div>
|
|
|
765
|
+ <div class="drawer-footer">
|
|
738
|
766
|
<button class="cancel-btn" @click="closeModal">取消</button>
|
|
739
|
|
- <button
|
|
740
|
|
- class="confirm-btn"
|
|
741
|
|
- @click="modalTitle.includes('题目类型') ? saveCategory() :
|
|
742
|
|
- modalTitle.includes('编辑') ? saveSingleQuestion() : saveAllQuestions()"
|
|
|
767
|
+ <button
|
|
|
768
|
+ class="confirm-btn"
|
|
|
769
|
+ @click="
|
|
|
770
|
+ modalTitle.includes('题目类型')
|
|
|
771
|
+ ? saveCategory()
|
|
|
772
|
+ : modalTitle.includes('编辑')
|
|
|
773
|
+ ? saveSingleQuestion()
|
|
|
774
|
+ : saveAllQuestions()
|
|
|
775
|
+ "
|
|
743
|
776
|
>
|
|
744
|
777
|
确定
|
|
745
|
778
|
</button>
|
|
|
779
|
+ </div>
|
|
746
|
780
|
</div>
|
|
747
|
781
|
</div>
|
|
748
|
|
-
|
|
749
|
|
-
|
|
750
|
782
|
</Page>
|
|
751
|
783
|
</template>
|
|
752
|
784
|
|
|
753
|
785
|
<style lang="scss" scoped>
|
|
754
|
|
-
|
|
755
|
786
|
@keyframes fadeIn {
|
|
756
|
787
|
from {
|
|
757
|
788
|
opacity: 0;
|
|
|
@@ -876,7 +907,8 @@ onUnmounted(() => {
|
|
876
|
907
|
opacity: 1;
|
|
877
|
908
|
}
|
|
878
|
909
|
|
|
879
|
|
-.edit-category-btn, .delete-category-btn {
|
|
|
910
|
+.edit-category-btn,
|
|
|
911
|
+.delete-category-btn {
|
|
880
|
912
|
padding: 4px 8px;
|
|
881
|
913
|
font-size: 12px;
|
|
882
|
914
|
cursor: pointer;
|
|
|
@@ -888,7 +920,7 @@ onUnmounted(() => {
|
|
888
|
920
|
.edit-category-btn {
|
|
889
|
921
|
color: #1890ff;
|
|
890
|
922
|
background-color: rgb(24 144 255 / 10%);
|
|
891
|
|
-
|
|
|
923
|
+
|
|
892
|
924
|
&:hover {
|
|
893
|
925
|
background-color: rgb(24 144 255 / 20%);
|
|
894
|
926
|
}
|
|
|
@@ -897,7 +929,7 @@ onUnmounted(() => {
|
|
897
|
929
|
.delete-category-btn {
|
|
898
|
930
|
color: #ff4d4f;
|
|
899
|
931
|
background-color: rgb(255 77 79 / 10%);
|
|
900
|
|
-
|
|
|
932
|
+
|
|
901
|
933
|
&:hover {
|
|
902
|
934
|
background-color: rgb(255 77 79 / 20%);
|
|
903
|
935
|
}
|
|
|
@@ -1072,8 +1104,6 @@ onUnmounted(() => {
|
|
1072
|
1104
|
}
|
|
1073
|
1105
|
}
|
|
1074
|
1106
|
|
|
1075
|
|
-
|
|
1076
|
|
-
|
|
1077
|
1107
|
/* 滚动条样式优化 */
|
|
1078
|
1108
|
.category-list::-webkit-scrollbar,
|
|
1079
|
1109
|
.question-list::-webkit-scrollbar,
|
|
|
@@ -1198,7 +1228,7 @@ onUnmounted(() => {
|
|
1198
|
1228
|
border: 1px solid #d9d9d9;
|
|
1199
|
1229
|
border-radius: 4px;
|
|
1200
|
1230
|
transition: all 0.3s;
|
|
1201
|
|
-
|
|
|
1231
|
+
|
|
1202
|
1232
|
&:focus {
|
|
1203
|
1233
|
border-color: #1890ff;
|
|
1204
|
1234
|
outline: none;
|
|
|
@@ -1215,7 +1245,7 @@ onUnmounted(() => {
|
|
1215
|
1245
|
border: 1px solid #d9d9d9;
|
|
1216
|
1246
|
border-radius: 4px;
|
|
1217
|
1247
|
transition: all 0.3s;
|
|
1218
|
|
-
|
|
|
1248
|
+
|
|
1219
|
1249
|
&:focus {
|
|
1220
|
1250
|
border-color: #1890ff;
|
|
1221
|
1251
|
outline: none;
|
|
|
@@ -1253,12 +1283,12 @@ onUnmounted(() => {
|
|
1253
|
1283
|
border: 1px solid #d9d9d9;
|
|
1254
|
1284
|
border-radius: 4px;
|
|
1255
|
1285
|
transition: all 0.3s;
|
|
1256
|
|
-
|
|
|
1286
|
+
|
|
1257
|
1287
|
&:hover {
|
|
1258
|
1288
|
color: #1890ff;
|
|
1259
|
1289
|
border-color: #1890ff;
|
|
1260
|
1290
|
}
|
|
1261
|
|
-
|
|
|
1291
|
+
|
|
1262
|
1292
|
&.active {
|
|
1263
|
1293
|
color: #1890ff;
|
|
1264
|
1294
|
background-color: #e6f7ff;
|
|
|
@@ -1331,11 +1361,11 @@ onUnmounted(() => {
|
|
1331
|
1361
|
border: 1px solid #ffccc7;
|
|
1332
|
1362
|
border-radius: 4px;
|
|
1333
|
1363
|
transition: all 0.3s;
|
|
1334
|
|
-
|
|
|
1364
|
+
|
|
1335
|
1365
|
&:hover:not(:disabled) {
|
|
1336
|
1366
|
background-color: #ffccc7;
|
|
1337
|
1367
|
}
|
|
1338
|
|
-
|
|
|
1368
|
+
|
|
1339
|
1369
|
&:disabled {
|
|
1340
|
1370
|
cursor: not-allowed;
|
|
1341
|
1371
|
opacity: 0.5;
|
|
|
@@ -1351,7 +1381,7 @@ onUnmounted(() => {
|
|
1351
|
1381
|
border: none;
|
|
1352
|
1382
|
border-radius: 4px;
|
|
1353
|
1383
|
transition: all 0.3s;
|
|
1354
|
|
-
|
|
|
1384
|
+
|
|
1355
|
1385
|
&:hover {
|
|
1356
|
1386
|
color: #40a9ff;
|
|
1357
|
1387
|
text-decoration: underline;
|