ai-question-pro 0.0.20 → 0.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,543 +1,998 @@
1
1
  <template>
2
- <div class="ai_contain">
3
- <slot>
4
- <span class="ai_button" @click="showAddQuestion">AI出题</span>
5
- </slot>
6
- <el-drawer :visible.sync="addShow" :direction="direction" :wrapperClosable="false" destroy-on-close>
7
- <template slot="title">
8
- <div class="add_question_title">
9
- <img src="../static/logo.png" alt="" style="width: 32px; height: 18px;"/>
10
- <span>出题</span>
11
- </div>
12
- </template>
13
- <div class="add_question_body">
14
- <el-form ref="form" :model="form" :rules="rules" label-width="100px" label-position="left">
15
- <el-form-item label="题目类型" prop="questionType">
16
- <!-- <el-checkbox-group v-model="form.questionTypes">-->
17
- <!-- <el-checkbox v-for="item in quesTypeList" :key="item.id" :label="item.id">-->
18
- <!-- {{ item.name }}-->
19
- <!-- </el-checkbox>-->
20
- <!-- </el-checkbox-group>-->
21
- <el-radio-group v-model="form.questionType" >
22
- <!-- <el-radio-button v-for="item in quesTypeList" :key="item.id" :label="item.id">{{ item.name }}</el-radio-button>-->
23
- <el-radio v-for="item in quesTypeList"
24
- style="margin-right: 8px; margin-bottom: 8px"
25
- :key="item.id"
26
- :label="item.id">{{ item.name }}</el-radio>
27
- </el-radio-group>
28
- </el-form-item>
29
- <el-form-item label="题目难度" prop="difficulty">
30
- <!-- <el-checkbox-group v-model="form.difficultys">-->
31
- <!-- <el-checkbox :label="item.id" v-for="item in difficultyList" :key="item.id">{{ item.name-->
32
- <!-- }}</el-checkbox>-->
33
- <!-- </el-checkbox-group>-->
34
- <el-radio-group v-model="form.difficulty" size="medium">
35
- <!-- <el-radio-button v-for="item in difficultyList" :key="item.id" :label="item.id">{{ item.name }}</el-radio-button>-->
36
- <el-radio v-for="item in difficultyList"
37
- style="margin-right: 8px; margin-bottom: 8px"
38
- :key="item.id"
39
- :label="item.id">{{ item.name }}</el-radio>
40
- </el-radio-group>
41
- </el-form-item>
42
- <el-form-item label="关联知识点" prop="knowledgeIds">
43
- <!-- <el-cascader v-model="form.knowledgeId" :options="knowledgeList" @change="closeDrowdown"-->
44
- <!-- :show-all-levels="false" ref="cascader" :props="{ checkStrictly: true, ...propFormat }"-->
45
- <!-- clearable></el-cascader>-->
46
- <cy-tree-select v-model="form.knowledgeIds" filterable :data="knowledgeList"></cy-tree-select>
47
- </el-form-item>
48
- <el-form-item label="题目数量" prop="count">
49
- <el-input class="number_input" v-model="form.count" min="1" max="10" type="number" placeholder="请输入"
50
- @input="handleInput"></el-input>
51
- <div class="remind">(为避免您等待时间过长,一次性最多生成10道题)</div>
52
- </el-form-item>
53
- <el-form-item label="自定义提示语">
54
- <el-input v-model="form.prompt" placeholder="这里可以写对于题目的要求和补充,比如:“出题内容请围绕一下知识内容:智能水利物联网是指利用物联网技术和智能化手段,对水利工程设施进行实时监测、数据采集、远程控制和智能分析,以实现对水资源的高效利用、水利设施的智能运营和管理。通过传感器、无线通信技术、云计算平台和大数据分析等技术手段,实现对水利系统各个环节的智能监测和管理,提高水资源利用效率,降低水利工程运行成本,保障水利工程安全稳定运行。”" type="textarea" :autosize="{ minRows: 10, maxRows: 10} " maxlength="200" show-word-limit></el-input>
55
- </el-form-item>
56
- </el-form>
2
+ <div class="ai_contain">
3
+ <slot>
4
+ <span class="ai_button" @click="showAddQuestion">AI出题</span>
5
+ </slot>
6
+ <el-drawer :visible.sync="addShow" :direction="direction" :wrapperClosable="false" destroy-on-close>
7
+ <template slot="title">
8
+ <div class="add_question_title">
9
+ <img src="../static/logo.png" alt="" style="width: 32px; height: 18px;"/>
10
+ <span>出题</span>
11
+ </div>
12
+ <div class="ai-bot-switch-wrap" style="position: absolute; left: 120px">
13
+ <div class="ai-bot-switch" @click="dropdownVisible = !dropdownVisible">
14
+ <img src="../static/aiBot-icon.png" alt="" style="width: 24px; height: 24px">
15
+ <span class="ai-bot-label">
16
+ {{ currentBot === 'course' ? '课程智能体' : '通义千问' }}
17
+ </span>
18
+ <img src="../static/check-icon.png" alt="" style="width: 12px; height: 12px">
19
+ </div>
20
+ <div
21
+ v-if="dropdownVisible"
22
+ class="ai-bot-dropdown"
23
+ @mousedown.prevent
24
+ style="position: absolute; z-index: 1000; min-width: 20%;"
25
+ >
26
+ <div
27
+ class="ai-bot-dropdown-item"
28
+ :class="{ active: currentBot === 'tongyi' }"
29
+ @click="selectBot('tongyi')"
30
+ >通义千问
57
31
  </div>
58
- <div class="add_question_footer">
59
- <button class="button-handle button-cancel" @click="addShow = false">取 消</button>
60
- <button class="button-handle button-generate-question" @click="generate">生成题目</button>
32
+ <!-- <div-->
33
+ <!-- v-if="courseId"-->
34
+ <!-- class="ai-bot-dropdown-item"-->
35
+ <!-- :class="{ active: currentBot === 'course' }"-->
36
+ <!-- @click="selectBot('course')"-->
37
+ <!-- >课程智能体-->
38
+ <!-- </div>-->
39
+ </div>
40
+ </div>
41
+ </template>
42
+ <div class="add_question_body">
43
+ <div class="question-type-header">
44
+ <span class="required-mark">*</span>
45
+ <span class="section-title">题目类型:</span>
46
+ <span class="tip-text">(为避免您等待时间过长,一次性最多生成10道题)</span>
47
+ </div>
48
+
49
+ <!-- 题目类型列表 -->
50
+ <div class="question-type-list">
51
+ <div
52
+ v-for="(item, index) in quesTypeList"
53
+ :key="item.id"
54
+ class="question-type-item"
55
+ :class="{ 'disabled': !form.selectedTypes[item.id] }"
56
+ >
57
+ <div class="type-left">
58
+ <el-checkbox
59
+ v-model="form.selectedTypes[item.id]"
60
+ class="type-checkbox"
61
+ @change="handleTypeChange(item.id)"
62
+ ></el-checkbox>
63
+ <span class="type-name" :class="getTypeClass(item.id)">{{ item.name }}</span>
64
+ </div>
65
+
66
+ <div class="type-right">
67
+ <el-input
68
+ v-model="form.typeCounts[item.id]"
69
+ size="small"
70
+ class="count-input"
71
+ placeholder="请输入生成数量"
72
+ min="1"
73
+ max="10"
74
+ @input="handleCountInput(item.id)"
75
+ ></el-input>
76
+ <div class="slider-container" :class="{ 'disabled': !form.selectedTypes[item.id] }">
77
+ <el-slider
78
+ v-model="form.typeDifficulties[item.id]"
79
+ :min="0"
80
+ :max="4"
81
+ :disabled="!form.selectedTypes[item.id]"
82
+ class="difficulty-slider"
83
+ :show-tooltip="false"
84
+ ></el-slider>
85
+ </div>
86
+ <span class="difficulty-label" :class="{ 'disabled': !form.selectedTypes[item.id] }">
87
+ {{ form.selectedTypes[item.id] ? getDifficultyLabel(form.typeDifficulties[item.id]) : '题目难度' }}
88
+ </span>
61
89
  </div>
62
- </el-drawer>
63
- <el-dialog :visible.sync="showQues" :close-on-click-modal="false">
64
- <template slot="title">
65
- <div class="question_title">
66
- <img src="../static/robot.png">
67
- <span>{{ title }}</span>
68
- </div>
69
- </template>
70
- <div class="question_body" ref="generateDiv">
71
- <questionItem v-if="aiResponse.questions && aiResponse.questions.length > 0" @agree="agree" @join="join"
72
- v-for="item, idx in aiResponse.questions" :key="idx" :detail="item" :index="idx">
73
- </questionItem>
90
+ </div>
91
+ </div>
92
+
93
+ <!-- 关联知识点 -->
94
+ <div class="knowledge-section">
95
+ <div class="section-header">
96
+ <span class="required-mark">*</span>
97
+ <span class="section-title">关联知识点:</span>
98
+ <span class="tip-text">(最多可以关联1个知识点)</span>
99
+ </div>
100
+ <div class="knowledge-input">
101
+ <cy-tree-select
102
+ v-model="form.knowledgeIds"
103
+ filterable
104
+ :data="knowledgeList"
105
+ placeholder="请选择"
106
+ ></cy-tree-select>
107
+ <img src="../static/link-icon.png" class="el-icon-link knowledge-icon" alt=""/>
108
+ </div>
109
+ </div>
110
+
111
+ <!-- 自定义提示语 -->
112
+ <div class="prompt-section">
113
+ <div class="section-header">
114
+ <span class="section-title" style="margin-left: 15px;">自定义提示语:</span>
115
+ </div>
116
+ <el-input
117
+ v-model="form.prompt"
118
+ type="textarea"
119
+ :autosize="{ minRows: 8, maxRows: 10 }"
120
+ maxlength="200"
121
+ show-word-limit
122
+ placeholder="这里可以写对于题目的要求和补充,比如:出题内容请围绕以下知识内容:智能水利物联网是指利用物联网技术和智能化手段,对水利工程设施进行实时监测、数据采集、远程控制和智能分析,以实现对水资源的高效利用、水利设施的智能运营和管理。通过传感器、无线通信技术、云计算平台和大数据分析等技术手段,实现对水利系统各个环节的智能监测和管理,提高水资源利用效率,降低水利工程运行成本,保障水利工程安全稳定运行。"
123
+ class="prompt-textarea"
124
+ resize="none"
125
+ ></el-input>
126
+ </div>
127
+ </div>
128
+ <div class="add_question_footer">
129
+ <button class="button-handle button-cancel" @click="addShow = false">取 消</button>
130
+ <button class="button-handle button-generate-question" @click="generate">
131
+ <img src="../static/generate-question-icon.png" style="width: 24px; height: 24px;" alt=""></img>
132
+ 生成题目
133
+ </button>
134
+ </div>
135
+ </el-drawer>
136
+ <el-drawer :visible.sync="showQues" :wrapperClosable="false" destroy-on-close class="question-result-drawer">
137
+ <template slot="title">
138
+ <div class="add_question_title">
139
+ <img src="../static/logo.png" alt="" style="width: 32px; height: 18px;"/>
140
+ <span>出题</span>
141
+ </div>
142
+ </template>
143
+ <div class="drawer_content_wrapper">
144
+ <div class="question_summary_warp">
145
+ <div class="question_summary">
146
+ <span class="summary_text">共为您生成了 {{
147
+ aiResponse.questions ? aiResponse.questions.length : 0
148
+ }} 道题目</span>
149
+ <div class="question_types_summary">
150
+ <span v-for="typeInfo in getQuestionTypeSummary()" :key="typeInfo.id" class="type_item">
151
+ {{ typeInfo.name }} <span class="summary_text">{{ typeInfo.count }}</span> 道
152
+ </span>
74
153
  </div>
75
- <div class="question_footer" slot="footer">
76
- <span class="question_remind">题目内容由人工智能大模型生成,请您审慎核查,确认内容准确,无不妥后再使用</span>
77
- <el-button type="primary" @click="rebuild" :disabled="rebuildFlag">重新生成</el-button>
154
+ </div>
155
+ <div class="question_summary_button">
156
+ <button class="button-handle button-cancel" @click="rebuild">重新生成</button>
157
+ <button class="button-handle button-generate-question" @click="addAllToBank">
158
+ 加入题库
159
+ </button>
160
+ </div>
161
+ </div>
162
+ <div class="question_body" ref="generateDiv">
163
+ <div v-if="isGenerating" class="generating-loading">
164
+ <div class="loading-content">
165
+ <div class="loading-spinner"></div>
166
+ <div class="loading-text">AI生成中...</div>
78
167
  </div>
79
- </el-dialog>
80
- </div>
168
+ </div>
169
+ <questionItem v-if="aiResponse.questions && aiResponse.questions.length > 0" @agree="agree" @join="join"
170
+ @selectionChange="handleQuestionSelection"
171
+ :isJoined="joinedQuestions.includes(idx)"
172
+ :ref="`questionItem_${idx}`"
173
+ v-for="item, idx in aiResponse.questions" :key="idx" :detail="item" :index="idx">
174
+ </questionItem>
175
+ </div>
176
+ <div class="question_footer_fixed">
177
+ <div class="footer_warning">
178
+ <img src="../static/warning-icon.png" class="el-icon-info" alt=""/>
179
+ <span class="question_remind">题目内容由人工智能大模型生成,请您审慎核查,确认内容准确,无不妥后再使用</span>
180
+ </div>
181
+ </div>
182
+ </div>
183
+ </el-drawer>
184
+ </div>
81
185
  </template>
82
186
  <script>
83
187
  import questionItem from './questionItem.vue'
84
188
  import CyTreeSelect from './CyTreeSelect.vue'
85
- // import {
86
- // Button, Drawer, Form, FormItem, Checkbox, CheckboxGroup, Input, Cascader, Dialog, Loading, this.$message
87
- // } from "element-ui"
88
- import { getAiQuestion, agreeQuestion, createBurial, checkVersion } from '../api/index'
189
+ import {getAiQuestion, agreeQuestion, createBurial, checkVersion} from '../api/index'
89
190
  import Cookies from 'js-cookie';
191
+ import {Image} from 'element-ui';
192
+
90
193
  export default {
91
- name: 'aiQuestion',
92
- components: {
93
- // 'el-button': Button,
94
- // 'el-drawer': Drawer,
95
- // 'el-form': Form,
96
- // 'el-form-item': FormItem,
97
- // 'el-checkbox': Checkbox,
98
- // 'el-checkbox-group': CheckboxGroup,
99
- // 'el-input': Input,
100
- // 'el-cascader': Cascader,
101
- // 'el-dialog': Dialog,
102
- questionItem,
103
- CyTreeSelect
194
+ name: 'aiQuestion',
195
+ components: {
196
+ questionItem,
197
+ CyTreeSelect
198
+ },
199
+ props: {
200
+ top: {
201
+ type: [String, Number],
202
+ default: 0
104
203
  },
105
- props: {
106
- top: {
107
- type: [String, Number],
108
- default: 0
204
+ knowledgeList: {
205
+ type: Array,
206
+ default: () => [
207
+ {
208
+ id: '1',
209
+ label: '大学英语',
210
+ children: [
211
+ {
212
+ id: '1-1',
213
+ label: '用英语打招呼',
214
+ }
215
+ ]
109
216
  },
110
- knowledgeList: {
111
- type: Array,
112
- default: () => [
113
- {
114
- id: '1',
115
- label: '大学英语',
116
- children: [
117
- {
118
- id: '1-1',
119
- label: '用英语打招呼',
120
- }
121
- ]
122
- },
123
- {
124
- id: '2',
125
- label: '大学物理',
126
- children: [
127
- {
128
- id: '2-1',
129
- label: '结构力学',
130
- children: [
131
- {
132
- id: '2-1-1',
133
- label: '力的构成',
134
- }
135
- ]
136
- },
137
- {
138
- id: '2-2',
139
- label: '流体力学',
140
- }
141
- ]
142
- },
217
+ {
218
+ id: '2',
219
+ label: '大学物理',
220
+ children: [
221
+ {
222
+ id: '2-1',
223
+ label: '结构力学',
224
+ children: [
143
225
  {
144
- id: '3',
145
- label: '高数'
226
+ id: '2-1-1',
227
+ label: '力的构成',
146
228
  }
147
- ]
229
+ ]
230
+ },
231
+ {
232
+ id: '2-2',
233
+ label: '流体力学',
234
+ }
235
+ ]
148
236
  },
149
- propFormat: {
150
- type: Object,
151
- default: () => ({
152
- id: 'id',
153
- label: 'label',
154
- children: 'children'
155
- })
237
+ {
238
+ id: '3',
239
+ label: '高数'
240
+ }
241
+ ]
242
+ },
243
+ propFormat: {
244
+ type: Object,
245
+ default: () => ({
246
+ id: 'id',
247
+ label: 'label',
248
+ children: 'children'
249
+ })
250
+ },
251
+ businessSource: {
252
+ type: String,
253
+ default: "0",
254
+ },
255
+ courseId: {
256
+ type: String,
257
+ default: ''
258
+ }
259
+ },
260
+ data() {
261
+ return {
262
+ showQues: false,
263
+ addShow: false,
264
+ generateCount: 0,
265
+ direction: 'rtl',
266
+ rebuildFlag: true,
267
+ form: {
268
+ selectedTypes: {
269
+ single: false,
270
+ multiple: false,
271
+ judge: false,
272
+ gapfilling: false,
273
+ shortanswer: false
156
274
  },
157
- businessSource: {
158
- type: String,
159
- default: "0", // 默认值为空字符串
275
+ typeCounts: {
276
+ single: null,
277
+ multiple: null,
278
+ judge: null,
279
+ gapfilling: null,
280
+ shortanswer: null
160
281
  },
161
- courseId: {
162
- type: String,
163
- default: ''
164
- }
282
+ typeDifficulties: {
283
+ single: 0,
284
+ multiple: 0,
285
+ judge: 0,
286
+ gapfilling: 0,
287
+ shortanswer: 0
288
+ },
289
+ knowledgeIds: '',
290
+ knowledgeId: [],
291
+ knowledge: [],
292
+ prompt: ''
293
+ },
294
+ quesTypeList: [
295
+ {name: '单选题', id: 'single'},
296
+ {name: '多选题', id: 'multiple'},
297
+ {name: '填空题', id: 'gapfilling'},
298
+ {name: '判断题', id: 'judge'},
299
+ {name: '简答题', id: 'shortanswer'},
300
+ ],
301
+ difficultyList: [
302
+ {name: '超级简单', id: 0},
303
+ {name: '简单', id: 1},
304
+ {name: '一般', id: 2},
305
+ {name: '困难', id: 3},
306
+ {name: '非常困难', id: 4},
307
+ ],
308
+ loading: null,
309
+ aiResponse: {},
310
+ title: '单选题',
311
+ dropdownVisible: false,
312
+ currentBot: 'tongyi',
313
+ selectedQuestions: [], // 存储选中的题目
314
+ joinedQuestions: [], // 存储已加入题库的题目索引
315
+ retryCountMap: {}, // 存储每个题目的重试次数
316
+ isGenerating: false, // 内部loading状态
317
+ }
318
+ },
319
+ watch: {
320
+ showQues(e) {
321
+ if (!e) {
322
+ this.aiResponse = {}
323
+ }
324
+ }
325
+ },
326
+ mounted() {
327
+ let doc = document.getElementsByClassName('rtl')[0]
328
+ if (this.top) {
329
+ doc.style.top = this.top + 'px'
330
+ doc.style.height = `calc( 100% - ${this.top}px)`
331
+ }
332
+ //国窖平台没有简答题
333
+ if (this.businessSource === '4') {
334
+ this.quesTypeList = [
335
+ {name: '单选题', id: 'single'},
336
+ {name: '多选题', id: 'multiple'},
337
+ {name: '判断题', id: 'judge'},
338
+ {name: '填空题', id: 'gapfilling'},
339
+ ]
340
+ }
341
+ },
342
+ methods: {
343
+ // 处理题目类型选择变化
344
+ handleTypeChange(typeId) {
345
+ // 可以在这里添加额外的逻辑
165
346
  },
166
- data() {
167
- return {
168
- showQues: false,
169
- addShow: false,
170
- generateCount: 0,
171
- direction: 'rtl',
172
- rebuildFlag: true,
173
- form: {
174
- questionTypes: [],
175
- questionType: null,
176
- count: 1,
177
- knowledgeId: [],
178
- knowledgeIds: '',
179
- difficultys: [],
180
- difficulty: null,
181
- knowledge: []
182
- },
183
- quesTypeList: [
184
- { name: '单选题', id: 'single' },
185
- { name: '多选题', id: 'multiple' },
186
- { name: '判断题', id: 'judge' },
187
- { name: '填空题', id: 'gapfilling' },
188
- { name: '简答题', id: 'shortanswer' },
189
- ],
190
- difficultyList: [
191
- { name: '非常简单', id: 0 },
192
- { name: '简单', id: 1 },
193
- { name: '一般', id: 2 },
194
- { name: '困难', id: 3 },
195
- { name: '非常困难', id: 4 },
196
- ],
197
- rules: {
198
- questionTypes: [{ required: true, message: '此项必填', trigger: 'change' }],
199
- questionType: [{ required: true, message: '此项必填', trigger: 'change' }],
200
- count: [{ required: true, message: '此项必填', trigger: 'blur' }],
201
- knowledgeIds: [{ required: true, message: '此项必填', trigger: 'change' }],
202
- difficultys: [{ required: true, message: '此项必填', trigger: 'change' }],
203
- difficulty: [{ required: true, message: '此项必填', trigger: 'change' }],
204
- },
205
- loading: null,
206
- aiResponse: {},
207
- title: '单选题'
208
- }
347
+
348
+ // 处理数量变化,自动选中题目类型
349
+ handleCountChange(typeId) {
350
+ if (this.form.typeCounts[typeId] && this.form.typeCounts[typeId] > 0) {
351
+ this.form.selectedTypes[typeId] = true;
352
+ }
209
353
  },
210
- watch: {
211
- showQues(e) {
212
- if (!e) {
213
- this.aiResponse = {}
214
- }
215
- }
354
+
355
+ // 处理难度变化,自动选中题目类型
356
+ handleDifficultyChange(typeId) {
357
+ this.form.selectedTypes[typeId] = true;
216
358
  },
217
- mounted() {
218
- let doc = document.getElementsByClassName('rtl')[0]
219
- if (this.top) {
220
- doc.style.top = this.top + 'px'
221
- doc.style.height = `calc( 100% - ${this.top}px)`
222
- }
223
- //国窖平台没有简答题
224
- if(this.businessSource === '4'){
225
- this.quesTypeList = [
226
- { name: '单选题', id: 'single' },
227
- { name: '多选题', id: 'multiple' },
228
- { name: '判断题', id: 'judge' },
229
- { name: '填空题', id: 'gapfilling' },
230
- ]
231
- }
359
+
360
+ // 处理数量输入验证
361
+ handleCountInput(typeId) {
362
+ let inputValue = this.form.typeCounts[typeId];
363
+
364
+ // 如果输入为空,允许为空
365
+ if (inputValue === '' || inputValue === null || inputValue === undefined) {
366
+ return;
367
+ }
368
+
369
+ let value = parseInt(inputValue);
370
+ if (isNaN(value) || value < 1) {
371
+ this.form.typeCounts[typeId] = 1;
372
+ } else if (value > 10) {
373
+ this.form.typeCounts[typeId] = 10;
374
+ } else {
375
+ this.form.typeCounts[typeId] = value;
376
+ }
232
377
  },
233
- methods: {
234
- handleInput(value) {
235
- // value = parseFloat(value);
236
- value = parseInt(value);
237
- if (value < 1) {
238
- this.form.count = 1;
239
- } else if (value > 10) {
240
- this.form.count = 10;
241
- } else {
242
- this.form.count = value;
243
- }
378
+
379
+ // 获取题目类型的样式类
380
+ getTypeClass(typeId) {
381
+ const classMap = {
382
+ single: 'type-single',
383
+ multiple: 'type-multiple',
384
+ gapfilling: 'type-gapfilling',
385
+ judge: 'type-judge',
386
+ shortanswer: 'type-shortanswer'
387
+ }
388
+ return classMap[typeId] || ''
389
+ },
390
+
391
+ // 获取难度标签
392
+ getDifficultyLabel(difficultyId) {
393
+ const difficulty = this.difficultyList.find(item => item.id === difficultyId)
394
+ return difficulty ? difficulty.name : '一般'
395
+ },
396
+
397
+ //展示右边弹框
398
+ showAddQuestion() {
399
+ if (!Cookies.get("AiToken")) {
400
+ return this.$message.warning('未获取到登录信息,请重新登录')
401
+ }
402
+ this.resetForm()
403
+ this.addShow = !this.addShow
404
+ createBurial({
405
+ businessType: 3,
406
+ operatorType: 0,
407
+ businessSource: this.businessSource,
408
+ courseId: this.courseId
409
+ })
410
+ },
411
+
412
+ // 重置表单
413
+ resetForm() {
414
+ this.form = {
415
+ selectedTypes: {
416
+ single: false,
417
+ multiple: false,
418
+ judge: false,
419
+ gapfilling: false,
420
+ shortanswer: false
244
421
  },
245
- //选中节点后关闭下拉框
246
- closeDrowdown(e) {
247
- this.$nextTick(() => {
248
- this.$refs.cascader.dropDownVisible = false
249
- });
422
+ typeCounts: {
423
+ single: null,
424
+ multiple: null,
425
+ judge: null,
426
+ gapfilling: null,
427
+ shortanswer: null
250
428
  },
251
- //展示右边弹框
252
- showAddQuestion() {
253
- if (!Cookies.get("AiToken")) {
254
- return this.$message.warning('未获取到登录信息,请重新登录')
255
- }
256
- this.form = {
257
- questionTypes: [],
258
- questionType: null,
259
- count: 1,
260
- knowledgeId: [],
261
- knowledgeIds: '',
262
- difficultys: [],
263
- difficulty: null,
264
- knowledge: []
265
- }
266
- this.addShow = !this.addShow
267
- createBurial({
268
- businessType: 3,
269
- operatorType: 0,
270
- businessSource:this.businessSource,
271
- courseId: this.courseId
272
- })
429
+ typeDifficulties: {
430
+ single: 0,
431
+ multiple: 0,
432
+ judge: 0,
433
+ gapfilling: 0,
434
+ shortanswer: 0
273
435
  },
274
- //生成题目
275
- generate() {
276
- this.$refs.form.validate(valid => {
277
- if (valid) {
278
- this.form.knowledgeId = this.form.knowledgeIds.split(',');
279
- this.form.knowledgeId.forEach(item => {
280
- this.getKnowledgeName(item, this.knowledgeList)
281
- })
282
- this.form.questionTypes.push(this.form.questionType);
283
- this.form.difficultys.push(this.form.difficulty);
284
- if (this.form.questionTypes.length > 1) {
285
- this.title = '多类题'
286
- } else {
287
- this.title = this.quesTypeList.find(item => item.id == this.form.questionTypes[0]).name
288
- }
289
- this.showQues = true
290
- this.addShow = !this.addShow
291
- this.sendPost();
292
- this.$emit("questionCount", this.form.count);
293
- }
294
- })
295
- },
296
- //重新生成
297
- rebuild() {
298
- this.sendPost()
436
+ knowledgeIds: '',
437
+ knowledgeId: [],
438
+ knowledge: [],
439
+ prompt: ''
440
+ }
441
+ },
442
+
443
+ //生成题目
444
+ generate() {
445
+ // 验证是否选择了题目类型
446
+ const selectedTypes = Object.keys(this.form.selectedTypes).filter(key => this.form.selectedTypes[key])
447
+ if (selectedTypes.length === 0) {
448
+ return this.$message.warning('请至少选择一种题目类型')
449
+ }
450
+
451
+ // 验证是否选择了知识点
452
+ if (!this.form.knowledgeIds) {
453
+ return this.$message.warning('请选择关联知识点')
454
+ }
455
+
456
+ // 处理知识点数据
457
+ this.form.knowledgeId = this.form.knowledgeIds.split(',');
458
+ this.form.knowledgeId.forEach(item => {
459
+ this.getKnowledgeName(item, this.knowledgeList)
460
+ })
461
+
462
+ // 设置标题
463
+ if (selectedTypes.length > 1) {
464
+ this.title = '多类题'
465
+ } else {
466
+ this.title = this.quesTypeList.find(item => item.id === selectedTypes[0]).name
467
+ }
468
+
469
+ this.showQues = true
470
+ this.addShow = !this.addShow
471
+ this.sendPost();
472
+
473
+ // 计算总题目数量
474
+ const totalCount = selectedTypes.reduce((sum, type) => sum + this.form.typeCounts[type], 0)
475
+ this.$emit("questionCount", totalCount);
476
+ },
477
+
478
+ //重新生成
479
+ rebuild() {
480
+ // 重新生成时清空已加入题库的状态
481
+ this.joinedQuestions = [];
482
+ this.selectedQuestions = [];
483
+ this.sendPost()
484
+ },
485
+
486
+ showGenerateResult() {
487
+ this.generateCount++;
488
+ const selectedTypes = Object.keys(this.form.selectedTypes).filter(key => this.form.selectedTypes[key])
489
+ const totalCount = selectedTypes.reduce((sum, type) => sum + this.form.typeCounts[type], 0)
490
+
491
+ if (this.generateCount == totalCount) {
492
+ this.$message.success('题目(' + totalCount + '道)全部生成完毕!');
493
+ this.rebuildFlag = false;
494
+ this.isGenerating = false;
495
+ } else {
496
+ this.$message.success('生成' + this.generateCount + '道题目成功');
497
+ }
498
+ this.$nextTick(() => {
499
+ const generateDiv = this.$refs.generateDiv;
500
+ generateDiv.scrollTop = generateDiv.scrollHeight;
501
+ });
502
+ },
503
+
504
+ reSend() {
505
+ this.isGenerating = true;
506
+ const selectedTypes = Object.keys(this.form.selectedTypes).filter(key => this.form.selectedTypes[key])
507
+ const questionTypes = selectedTypes
508
+ const difficultys = selectedTypes.map(type => this.form.typeDifficulties[type])
509
+
510
+ let obj = {
511
+ modelType: 'aliyun',
512
+ questionAddCmd: {
513
+ questionTypes: questionTypes,
514
+ count: 1,
515
+ knowledge: this.form.knowledge.join(' '),
516
+ knowledgeId: this.form.knowledgeId[this.form.knowledgeId.length - 1],
517
+ difficultys: difficultys,
518
+ prompt: this.form.prompt
299
519
  },
300
- showGenerateResult(){
301
- this.generateCount++;
302
- if(this.generateCount == this.form.count){
303
- this.$message.success( '题目(' + this.form.count + '道)全部生成完毕!');
304
- this.rebuildFlag = false;
520
+ sessionId: this.aiResponse.sessionId ? this.aiResponse.sessionId : '',
521
+ businessSource: this.businessSource,
522
+ courseId: this.courseId,
523
+ }
524
+
525
+ getAiQuestion(obj).then(res => {
526
+ if (res.success) {
527
+ this.loading && this.loading.close()
528
+ if (res.data && res.data.questions && res.data.questions.length > 0) {
529
+ const newQuestion = res.data.questions[0];
530
+ const questionType = newQuestion.type;
531
+ // 检查是否有重复的题目标题和题型
532
+ const isDuplicate = this.checkDuplicateQuestion(newQuestion, questionType);
533
+
534
+ if (isDuplicate) {
535
+ const repetitionQuestions = this.getGeneratedQuestionsByType(questionType);
536
+ this.regenerateWithRepetitionForReSend(obj, repetitionQuestions, questionType);
537
+ return;
538
+ }
539
+
540
+ if (this.aiResponse.questions && this.aiResponse.questions.length > 0) {
541
+ this.aiResponse.questions.push(newQuestion);
542
+ } else {
543
+ this.aiResponse = res.data;
544
+ }
545
+ this.$set(newQuestion, 'evaluate', 0)
546
+ // 设置题目类型,便于后续统计
547
+ this.$set(newQuestion, 'type', questionType)
548
+ this.showGenerateResult();
305
549
  } else {
306
- this.$message.success('生成' + this.generateCount + '道题目成功');
550
+ this.$message.warning('题目获取失败,重新生成中...')
551
+ return this.reSend()
307
552
  }
308
- this.$nextTick(() => {
309
- const generateDiv = this.$refs.generateDiv;
310
- generateDiv.scrollTop = generateDiv.scrollHeight;
311
- });
312
- },
313
- reSend() {
314
- this.loading && this.loading.close()
315
- this.loading = this.$loading({
316
- target: '.el-dialog',
317
- text: 'AI生成中...'
318
- });
553
+ } else {
554
+ this.isGenerating = false;
555
+ this.$message.warning('题目获取失败')
556
+ this.rebuildFlag = false;
557
+ }
558
+ }).catch(error => {
559
+ console.log("error:" + error);
560
+ this.isGenerating = false;
561
+ this.$message.warning('题目获取失败')
562
+ this.rebuildFlag = false;
563
+ })
564
+ },
565
+
566
+ //发送请求
567
+ sendPost() {
568
+ checkVersion(this.businessSource).then(res => {
569
+ console.log(res);
570
+ if (res.errCode === '403') {
571
+ this.$message.warning(res.errMessage)
572
+ } else {
573
+ this.postQuestion();
574
+ }
575
+ });
576
+ },
577
+
578
+ postQuestion() {
579
+ this.rebuildFlag = true;
580
+ this.generateCount = 0;
581
+ this.aiResponse = {}
582
+ this.isGenerating = true;
583
+
584
+ const selectedTypes = Object.keys(this.form.selectedTypes).filter(key => this.form.selectedTypes[key])
585
+
586
+ // 按题型分别生成题目
587
+ selectedTypes.forEach(type => {
588
+ const count = this.form.typeCounts[type] || 1;
589
+ const difficulty = this.form.typeDifficulties[type];
590
+
591
+ // 为每种题型生成指定数量的题目
592
+ for (let i = 0; i < count; i++) {
319
593
  let obj = {
320
594
  modelType: 'aliyun',
321
595
  questionAddCmd: {
322
- questionTypes: this.form.questionTypes,
323
- // count: this.form.count,
596
+ questionTypes: [type], // 单个题型
324
597
  count: 1,
325
598
  knowledge: this.form.knowledge.join(' '),
326
599
  knowledgeId: this.form.knowledgeId[this.form.knowledgeId.length - 1],
327
- difficultys: this.form.difficultys,
600
+ difficultys: [difficulty], // 对应的难度
328
601
  prompt: this.form.prompt
329
602
  },
330
603
  sessionId: this.aiResponse.sessionId ? this.aiResponse.sessionId : '',
331
- businessSource:this.businessSource
332
-
604
+ businessSource: this.businessSource,
605
+ courseId: this.courseId,
333
606
  }
607
+
334
608
  getAiQuestion(obj).then(res => {
335
609
  if (res.success) {
336
- this.loading && this.loading.close()
337
610
  if (res.data && res.data.questions && res.data.questions.length > 0) {
611
+ const newQuestion = res.data.questions[0];
612
+
613
+ // 检查是否有重复的题目标题和题型
614
+ const isDuplicate = this.checkDuplicateQuestion(newQuestion, type);
615
+
616
+ if (isDuplicate) {
617
+ // 只传递当前题型
618
+ const repetitionQuestions = this.getGeneratedQuestionsByType(type);
619
+ this.regenerateWithRepetition(obj, repetitionQuestions, type);
620
+ return;
621
+ }
622
+
338
623
  if (this.aiResponse.questions && this.aiResponse.questions.length > 0) {
339
- this.aiResponse.questions.push(res.data.questions[0]);
624
+ this.aiResponse.questions.push(newQuestion);
340
625
  } else {
341
626
  this.aiResponse = res.data;
342
627
  }
343
- this.$set(res.data.questions[0], 'evaluate', 0)
628
+ this.$set(newQuestion, 'evaluate', 0)
629
+ // 设置题目类型,便于后续统计
630
+ this.$set(newQuestion, 'type', type)
344
631
  this.showGenerateResult();
345
632
  } else {
346
633
  this.$message.warning('题目获取失败,重新生成中...')
347
634
  return this.reSend()
348
635
  }
349
636
  } else {
350
- this.loading && this.loading.close()
637
+ this.isGenerating = false;
351
638
  this.$message.warning('题目获取失败')
352
639
  this.rebuildFlag = false;
353
640
  }
354
641
  }).catch(error => {
355
642
  console.log("error:" + error);
356
- this.loading && this.loading.close()
643
+ this.isGenerating = false;
357
644
  this.$message.warning('题目获取失败')
358
645
  this.rebuildFlag = false;
359
646
  })
360
- },
361
- //发送请求
362
- sendPost() {
363
- checkVersion(this.businessSource).then(res => {
364
- console.log(res);
365
- if (res.errCode === '403') {
366
- this.$message.warning(res.errMessage)
367
- } else {
368
- this.postQuestion();
369
- }
370
- });
371
- // this.postQuestion();
372
- },
373
- postQuestion() {
374
- this.rebuildFlag = true;
375
- this.generateCount = 0;
376
- this.aiResponse = {}
377
- this.loading && this.loading.close()
378
- this.loading = this.$loading({
379
- target: '.el-dialog',
380
- text: 'AI生成中...'
381
- });
382
- for(let i = 0; i<this.form.count; i++){
383
- let obj = {
384
- modelType: 'aliyun',
385
- questionAddCmd: {
386
- questionTypes: this.form.questionTypes,
387
- // count: this.form.count,
388
- count: 1,
389
- knowledge: this.form.knowledge.join(' '),
390
- knowledgeId: this.form.knowledgeId[this.form.knowledgeId.length - 1],
391
- difficultys: this.form.difficultys,
392
- prompt: this.form.prompt
393
- },
394
- sessionId: this.aiResponse.sessionId ? this.aiResponse.sessionId : '',
395
- businessSource:this.businessSource
396
- }
397
- getAiQuestion(obj).then(res => {
398
- // console.log("questions obj is :" + JSON.stringify(res.data.questions));
399
- // console.log("res is :" + JSON.stringify(res));
400
- if (res.success) {
401
- this.loading && this.loading.close()
402
- // this.aiResponse = {}
403
- // this.aiResponse = res.data || {}
404
- if (res.data && res.data.questions && res.data.questions.length > 0) {
405
- // this.aiResponse.questions.forEach(item => {
406
- // this.$set(item, 'evaluate', 0)
407
- // })
408
- if (this.aiResponse.questions && this.aiResponse.questions.length > 0) {
409
- this.aiResponse.questions.push(res.data.questions[0]);
410
- } else {
411
- this.aiResponse = res.data;
412
- }
413
- this.$set(res.data.questions[0], 'evaluate', 0)
414
- this.showGenerateResult();
415
- } else {
416
- this.$message.warning('题目获取失败,重新生成中...')
417
- return this.reSend()
418
- }
419
- } else {
420
- this.loading && this.loading.close()
421
- this.$message.warning('题目获取失败')
422
- this.rebuildFlag = false;
423
- }
424
- }).catch(error => {
425
- console.log("error:" + error);
426
- this.loading && this.loading.close()
427
- this.$message.warning('题目获取失败')
428
- this.rebuildFlag = false;
429
- })
647
+ }
648
+ });
649
+ },
650
+
651
+ //获取知识点名称
652
+ getKnowledgeName(e, array) {
653
+ array.forEach(item => {
654
+ if (e == item.id) {
655
+ return this.form.knowledge.push(item.label)
656
+ } else if (item.children && item.children.length > 0) {
657
+ this.getKnowledgeName(e, item.children)
658
+ }
659
+ });
660
+ },
661
+
662
+ //点赞
663
+ agree(e, j) {
664
+ this.aiResponse.questions[e].evaluate = j
665
+ let obj = {
666
+ modelType: 'aliyun',
667
+ ...this.aiResponse
668
+ }
669
+ agreeQuestion(obj).then(res => {
670
+ // 处理响应
671
+ }).catch(error => {
672
+ console.log(error);
673
+ })
674
+ },
675
+
676
+ //加入题库
677
+ join(e) {
678
+ this.$emit('joinQuestionBank', {...e, knowledgePointsId: this.form.knowledgeId[this.form.knowledgeId.length - 1]})
679
+
680
+ // 当单个题目加入题库后,从选中列表中移除该题目
681
+ const questionIndex = this.aiResponse.questions.findIndex(q => q === e);
682
+ if (questionIndex !== -1) {
683
+ this.selectedQuestions = this.selectedQuestions.filter(q => q.index !== questionIndex);
684
+
685
+ // 将题目索引添加到已加入题库列表
686
+ if (!this.joinedQuestions.includes(questionIndex)) {
687
+ this.joinedQuestions.push(questionIndex);
688
+ }
689
+ }
690
+ },
691
+
692
+ selectBot(bot) {
693
+ this.currentBot = bot;
694
+ this.dropdownVisible = false;
695
+ },
696
+
697
+ // 处理题目选择变化
698
+ handleQuestionSelection(data) {
699
+ const { index, selected, detail } = data;
700
+
701
+ if (selected) {
702
+ // 添加到选中列表
703
+ if (!this.selectedQuestions.find(q => q.index === index)) {
704
+ this.selectedQuestions.push({ index, detail });
705
+ }
706
+ } else {
707
+ // 从选中列表移除
708
+ this.selectedQuestions = this.selectedQuestions.filter(q => q.index !== index);
709
+ }
710
+ },
711
+
712
+ // 获取题目类型统计
713
+ getQuestionTypeSummary() {
714
+ if (!this.aiResponse.questions || this.aiResponse.questions.length === 0) {
715
+ return [];
716
+ }
717
+
718
+ const typeCounts = {};
719
+ const typeNameMap = {
720
+ single: '单选题',
721
+ multiple: '多选题',
722
+ gapfilling: '填空题',
723
+ judge: '判断题',
724
+ shortanswer: '简答题'
725
+ };
726
+
727
+ // 统计各类型题目数量
728
+ this.aiResponse.questions.forEach(question => {
729
+ const type = question.type || 'single'; // 默认为单选题
730
+ typeCounts[type] = (typeCounts[type] || 0) + 1;
731
+ });
732
+
733
+ // 转换为显示格式
734
+ return Object.keys(typeCounts).map(typeId => ({
735
+ id: typeId,
736
+ name: typeNameMap[typeId] || typeId,
737
+ count: typeCounts[typeId]
738
+ }));
739
+ },
740
+
741
+ // 批量加入题库
742
+ addAllToBank() {
743
+ if (this.selectedQuestions.length === 0) {
744
+ return this.$message.warning('请先选择要加入题库的题目');
745
+ }
746
+
747
+ // 批量加入选中的题目到题库
748
+ this.selectedQuestions.forEach(({ index, detail }) => {
749
+ // 调用单个题目的加入题库方法
750
+ this.$emit('joinQuestionBank', {
751
+ ...detail,
752
+ knowledgePointsId: this.form.knowledgeId[this.form.knowledgeId.length - 1]
753
+ });
754
+
755
+ // 将题目索引添加到已加入题库列表
756
+ if (!this.joinedQuestions.includes(index)) {
757
+ this.joinedQuestions.push(index);
758
+ }
759
+
760
+ // 直接调用子组件方法清空选中状态
761
+ this.$nextTick(() => {
762
+ const questionItemRef = this.$refs[`questionItem_${index}`];
763
+ if (questionItemRef) {
764
+ // 如果是数组,取第一个元素;如果不是数组,直接使用
765
+ const componentInstance = Array.isArray(questionItemRef) ? questionItemRef[0] : questionItemRef;
766
+ if (componentInstance && componentInstance.clearSelection) {
767
+ componentInstance.clearSelection();
430
768
  }
431
- },
432
- //获取知识点名称
433
- getKnowledgeName(e, array) {
434
- array.forEach(item => {
435
- if (e == item.id) {
436
- return this.form.knowledge.push(item.label)
437
- } else if (item.children && item.children.length > 0) {
438
- this.getKnowledgeName(e, item.children)
439
- }
440
- });
441
- },
442
- //点赞
443
- agree(e, j) {
444
- this.aiResponse.questions[e].evaluate = j
445
- let obj = {
446
- modelType: 'aliyun',
447
- ...this.aiResponse
769
+ }
770
+ });
771
+ });
772
+
773
+ // 清空选中状态
774
+ this.selectedQuestions = [];
775
+ },
776
+
777
+ // 检查题目是否重复
778
+ checkDuplicateQuestion(newQuestion, questionType) {
779
+ if (!this.aiResponse.questions || this.aiResponse.questions.length === 0) {
780
+ return false;
781
+ }
782
+ return this.aiResponse.questions.some(existingQuestion => {
783
+ return existingQuestion.title === newQuestion.title &&
784
+ existingQuestion.type === questionType;
785
+ });
786
+ },
787
+
788
+ // 获取指定题型的已生成题目信息
789
+ getGeneratedQuestionsByType(questionType) {
790
+ if (!this.aiResponse.questions || this.aiResponse.questions.length === 0) {
791
+ return [];
792
+ }
793
+ return this.aiResponse.questions
794
+ .filter(question => question.type === questionType)
795
+ .map(question => ({
796
+ title: question.title,
797
+ type: question.type
798
+ }));
799
+ },
800
+
801
+ // 重复题目信息重新生成
802
+ regenerateWithRepetition(originalObj, repetitionQuestions, questionType, retryCount = 0) {
803
+ console.log('检测到重复题目,重新生成,已生成题目:', repetitionQuestions, '重试次数:', retryCount);
804
+
805
+ // 如果重试次数超过3次,跳过此题目
806
+ if (retryCount >= 3) {
807
+ console.log('重试次数超过3次,跳过此题目');
808
+ this.isGenerating = false;
809
+ this.$message.warning('AI生成重复题目超过3次,已跳过该题目');
810
+ return;
811
+ }
812
+
813
+ const newObj = {
814
+ ...originalObj,
815
+ repetitionQuestions: repetitionQuestions // 传递所有已生成的题目信息
816
+ };
817
+
818
+ getAiQuestion(newObj).then(res => {
819
+ if (res.success) {
820
+ this.loading && this.loading.close()
821
+ if (res.data && res.data.questions && res.data.questions.length > 0) {
822
+ const newQuestion = res.data.questions[0];
823
+ // 再次检查是否还有重复
824
+ const isStillDuplicate = this.checkDuplicateQuestion(newQuestion, questionType);
825
+
826
+ if (isStillDuplicate) {
827
+ console.log('重新生成后仍有重复,继续重试,当前重试次数:', retryCount + 1);
828
+ // 递归调用,增加重试次数,重新获取最新的当前题型已生成题目列表
829
+ const updatedRepetitionQuestions = this.getGeneratedQuestionsByType(questionType);
830
+ this.regenerateWithRepetition(originalObj, updatedRepetitionQuestions, questionType, retryCount + 1);
831
+ return;
832
+ }
833
+
834
+ if (this.aiResponse.questions && this.aiResponse.questions.length > 0) {
835
+ this.aiResponse.questions.push(newQuestion);
836
+ } else {
837
+ this.aiResponse = res.data;
448
838
  }
449
- agreeQuestion(obj).then(res => {
839
+ this.$set(newQuestion, 'evaluate', 0)
840
+ // 设置题目类型,便于后续统计
841
+ this.$set(newQuestion, 'type', questionType)
842
+ this.showGenerateResult();
843
+ } else {
844
+ this.$message.warning('重新生成题目失败')
845
+ }
846
+ } else {
847
+ this.$message.warning('重新生成题目失败')
848
+ }
849
+ }).catch(error => {
850
+ console.log("重新生成题目错误:" + error);
851
+ this.$message.warning('重新生成题目失败')
852
+ });
853
+ },
450
854
 
451
- }).catch(error => {
452
- console.log(error);
453
- })
454
- },
455
- //加入题库
456
- join(e) {
457
- this.$emit('joinQuestionBank', { ...e, knowledgePointsId: this.form.knowledgeId[this.form.knowledgeId.length - 1] })
855
+ // 重新生成的重复检测重新生成
856
+ regenerateWithRepetitionForReSend(originalObj, repetitionQuestions, questionType, retryCount = 0) {
857
+ console.log('reSend检测到重复题目,重新生成,已生成题目:', repetitionQuestions, '重试次数:', retryCount);
858
+ // 如果重试次数超过3次,跳过此题目
859
+ if (retryCount >= 3) {
860
+ console.log('reSend重试次数超过3次,跳过此题目');
861
+ this.isGenerating = false;
862
+ this.$message.warning('AI生成重复题目超过3次,已跳过该题目');
863
+ return;
864
+ }
865
+ const newObj = {
866
+ ...originalObj,
867
+ repetitionQuestions: repetitionQuestions
868
+ };
869
+
870
+ getAiQuestion(newObj).then(res => {
871
+ if (res.success) {
872
+ this.loading && this.loading.close()
873
+ if (res.data && res.data.questions && res.data.questions.length > 0) {
874
+ const newQuestion = res.data.questions[0];
875
+
876
+ // 再次检查是否还有重复
877
+ const isStillDuplicate = this.checkDuplicateQuestion(newQuestion, questionType);
878
+
879
+ if (isStillDuplicate) {
880
+ console.log('reSend重新生成后仍有重复,继续重试,当前重试次数:', retryCount + 1);
881
+ // 递归调用,增加重试次数,重新获取最新的当前题型已生成题目列表
882
+ const updatedRepetitionQuestions = this.getGeneratedQuestionsByType(questionType);
883
+ this.regenerateWithRepetitionForReSend(originalObj, updatedRepetitionQuestions, questionType, retryCount + 1);
884
+ return;
885
+ }
886
+
887
+ if (this.aiResponse.questions && this.aiResponse.questions.length > 0) {
888
+ this.aiResponse.questions.push(newQuestion);
889
+ } else {
890
+ this.aiResponse = res.data;
891
+ }
892
+ this.$set(newQuestion, 'evaluate', 0)
893
+ // 设置题目类型,便于后续统计
894
+ this.$set(newQuestion, 'type', questionType)
895
+ this.showGenerateResult();
896
+ } else {
897
+ this.$message.warning('重新生成题目失败,重新生成中...')
898
+ return this.reSend()
899
+ }
900
+ } else {
901
+ this.loading && this.loading.close()
902
+ this.$message.warning('重新生成题目失败')
903
+ this.rebuildFlag = false;
458
904
  }
459
- }
460
- }
905
+ }).catch(error => {
906
+ console.log("reSend重新生成题目错误:" + error);
907
+ this.loading && this.loading.close()
908
+ this.$message.warning('重新生成题目失败')
909
+ this.rebuildFlag = false;
910
+ });
911
+ },
912
+ }
913
+ }
461
914
  </script>
462
915
  <style lang="scss" scoped>
463
916
  .ai_contain {
464
917
  display: inline-block;
918
+ font-family: "Microsoft YaHei", 微软雅黑, sans-serif;
465
919
 
466
920
  ::v-deep {
467
921
  .el-drawer.rtl {
468
- border-radius: 15px 0 0 15px;
922
+ border-radius: 20px;
923
+ width: 720px !important;
469
924
  }
470
925
 
471
926
  .el-icon-close:before {
472
927
  color: #0e0e0e;
473
928
  font-weight: bold;
474
- font-size: 16px;
929
+ font-size: 24px;
475
930
  }
476
931
 
477
932
  .el-drawer__header {
478
933
  padding-top: 0;
479
934
  height: 64px;
480
- background:
481
- linear-gradient(to bottom, rgba(255,255,255,0), white), /* 底部渐变到白色 */
482
- linear-gradient(to right, #BCB9F4, #D5C6F8, #E6C6F9); /* 从左到右的渐变色带 */
935
+ background: linear-gradient(to bottom, rgba(255, 255, 255, 0), white),
936
+ linear-gradient(to right, #BCB9F4, #D5C6F8, #E6C6F9);
937
+ margin-bottom: 0;
938
+ border-radius: 20px;
483
939
  }
484
940
 
485
941
  .el-drawer__body {
486
- padding: 0 20px;
942
+ padding: 0 30px;
487
943
  display: flex;
488
944
  flex-direction: column;
489
945
  justify-content: space-between;
490
946
  }
491
947
 
492
- .el-form-item__label {
493
- font-weight: 700;
948
+ .el-checkbox__inner {
949
+ width: 20px;
950
+ height: 20px;
951
+ border-radius: 4px;
494
952
  }
495
953
 
496
- .el-input__inner {
497
- padding-right: 0;
498
- border-radius: 25px;
954
+ .el-checkbox__input.is-checked .el-checkbox__inner {
955
+ background-color: #7C4DFF;
956
+ border-color: #7C4DFF;
499
957
  }
500
958
 
501
- .el-cascader {
502
- width: 100%;
503
- }
504
-
505
- .el-radio__inner {
506
- width: 24px;
507
- height: 24px;
508
- }
509
-
510
- .el-radio__input.is-checked+.el-radio__label {
511
- color: #606266;
959
+ .el-checkbox__inner::after {
960
+ width: 4px;
961
+ height: 9px;
962
+ border: 3px solid #fff;
963
+ border-left: 0;
964
+ border-top: 0;
965
+ left: 6px;
966
+ top: 2px;
512
967
  }
513
968
 
514
- .el-radio__input.is-checked .el-radio__inner {
515
- border-color: #7050D0;
516
- background: #7050D0;
969
+ .el-slider__runway {
970
+ height: 6px;
971
+ background-color: #E5E5E5;
972
+ border-radius: 3px;
517
973
  }
518
974
 
519
- .el-radio__input.is-checked .el-radio__inner::after {
520
- display: none;
975
+ .el-slider__button {
976
+ width: 20px;
977
+ height: 20px;
978
+ border: 1px solid #7C4DFF;
979
+ background-color: #7C4DFF;
521
980
  }
522
981
 
523
- /* 使用Element UI的对号图标 */
524
- .el-radio__input.is-checked .el-radio__inner::before {
525
- font-family: "element-icons", serif;
526
- content: "\e6da"; /* Element UI check图标的Unicode */
527
- position: absolute;
528
- top: 50%;
529
- left: 50%;
530
- transform: translate(-50%, -50%);
531
- font-size: 12px;
532
- color: #fff;
982
+ .el-slider__bar {
983
+ background-color: #7C4DFF;
533
984
  }
534
985
 
535
- .el-select .el-input__inner {
536
- border-radius: 25px;
986
+ .el-input__inner {
987
+ border-radius: 12px;
988
+ height: 48px !important;
989
+ border: 1px solid #E0E0E0;
537
990
  }
538
991
 
539
992
  .el-textarea__inner {
540
993
  border-radius: 16px;
994
+ border: 1px solid #E0E0E0;
995
+ font-family: "Microsoft YaHei", 微软雅黑, sans-serif;
541
996
  }
542
997
 
543
998
  .el-loading-mask {
@@ -545,26 +1000,6 @@ export default {
545
1000
  }
546
1001
  }
547
1002
 
548
- .button-handle {
549
- width: 168px;
550
- height: 48px;
551
- font-size: 14px;
552
- border-radius: 25px;
553
- border: none;
554
- color: #fff;
555
- cursor: pointer;
556
- }
557
-
558
- .button-generate-question {
559
- background: linear-gradient(to bottom, #7050D0, #2828E0);
560
- }
561
-
562
- .button-cancel {
563
- border: 1px solid #2828E0;
564
- background: transparent;
565
- color: #3030C0;
566
- }
567
-
568
1003
  .ai_button {
569
1004
  cursor: pointer;
570
1005
  user-select: none;
@@ -582,29 +1017,431 @@ export default {
582
1017
  font-size: 18px;
583
1018
  color: #333333;
584
1019
  font-weight: bold;
585
- gap: 5px;
1020
+ gap: 7px;
586
1021
  }
587
1022
 
588
1023
  .add_question_body {
589
- .remind {
590
- font-size: 14px;
591
- color: #9BA1AE;
1024
+ .question-type-header {
1025
+ margin-bottom: 20px;
1026
+
1027
+ .required-mark {
1028
+ color: #FF4455;
1029
+ margin-right: 4px;
1030
+ }
1031
+
1032
+ .section-title {
1033
+ font-size: 14px;
1034
+ color: #303040;
1035
+ }
1036
+
1037
+ .tip-text {
1038
+ font-size: 12px;
1039
+ color: #999;
1040
+ margin-left: 20px;
1041
+ }
1042
+ }
1043
+
1044
+ .question-type-list {
1045
+ margin-bottom: 30px;
1046
+
1047
+ .question-type-item {
1048
+ display: flex;
1049
+ align-items: center;
1050
+ padding: 5px 10px;
1051
+ margin-bottom: 12px;
1052
+ transition: all 0.3s ease;
1053
+
1054
+ .type-left {
1055
+ display: flex;
1056
+ align-items: center;
1057
+
1058
+ .type-checkbox {
1059
+ margin-right: 12px;
1060
+ }
1061
+
1062
+ .type-name {
1063
+ font-size: 14px;
1064
+ font-weight: bold;
1065
+ border-radius: 54px;
1066
+ color: #fff;
1067
+ height: 40px;
1068
+ width: 80px;
1069
+ display: flex;
1070
+ align-items: center;
1071
+ justify-content: center;
1072
+
1073
+ &.type-single {
1074
+ background: #8060F029;
1075
+ color: #6040F0;
1076
+ }
1077
+
1078
+ &.type-multiple {
1079
+ background: #0090F029;
1080
+ color: #0090FF;
1081
+ }
1082
+
1083
+ &.type-gapfilling {
1084
+ background: #60C09029;
1085
+ color: #60C090;
1086
+ }
1087
+
1088
+ &.type-judge {
1089
+ background: #F0A00029;
1090
+ color: #F0A000;
1091
+ }
1092
+
1093
+ &.type-shortanswer {
1094
+ background: #E060C029;
1095
+ color: #E060C0;
1096
+ }
1097
+ }
1098
+ }
1099
+
1100
+ .type-right {
1101
+ display: flex;
1102
+ align-items: center;
1103
+ gap: 20px;
1104
+
1105
+ .count-input {
1106
+ width: 160px;
1107
+ margin-left: 20px;
1108
+
1109
+ ::v-deep .el-input__inner {
1110
+ height: 42px !important;
1111
+ border-radius: 56px;
1112
+ text-align: center;
1113
+ font-size: 12px;
1114
+ padding: 0 !important;
1115
+ }
1116
+ }
1117
+
1118
+ .slider-container {
1119
+ width: 240px;
1120
+ margin-left: 10px;
1121
+
1122
+ &.disabled {
1123
+ ::v-deep .el-slider__button {
1124
+ background-color: #ffffff;
1125
+ border: 1px solid #797979;
1126
+ }
1127
+ }
1128
+ }
1129
+
1130
+ .difficulty-label {
1131
+ font-size: 14px;
1132
+ color: #6040E0;
1133
+ min-width: 80px;
1134
+ font-weight: bold;
1135
+
1136
+ &.disabled {
1137
+ color: #CCC;
1138
+ }
1139
+ }
1140
+
1141
+ .placeholder-text {
1142
+ font-size: 12px;
1143
+ color: #DDDDE0;
1144
+ width: 160px;
1145
+ border: 1px solid #DDDDE0;
1146
+ border-radius: 56px;
1147
+ height: 40px;
1148
+ text-align: center;
1149
+ margin-left: 20px;
1150
+ display: flex;
1151
+ align-items: center;
1152
+ justify-content: center;
1153
+ }
1154
+ }
1155
+ }
1156
+ }
1157
+
1158
+ .knowledge-section, .prompt-section {
1159
+ margin-bottom: 24px;
1160
+
1161
+ .section-header {
1162
+ margin-bottom: 12px;
1163
+
1164
+ .required-mark {
1165
+ color: #FF4455;
1166
+ margin-right: 4px;
1167
+ }
1168
+
1169
+ .section-title {
1170
+ font-size: 14px;
1171
+ color: #303040;
1172
+ }
1173
+
1174
+ .tip-text {
1175
+ font-size: 12px;
1176
+ color: #999;
1177
+ margin-left: 20px;
1178
+ }
1179
+ }
1180
+
1181
+ .knowledge-input {
1182
+ position: relative;
1183
+
1184
+ .knowledge-icon {
1185
+ position: absolute;
1186
+ right: 12px;
1187
+ top: 50%;
1188
+ transform: translateY(-50%);
1189
+ color: #999;
1190
+ font-size: 16px;
1191
+ width: 24px;
1192
+ height: 24px;
1193
+ }
1194
+ }
1195
+ }
1196
+
1197
+ .prompt-textarea {
1198
+ .el-textarea__inner {
1199
+ min-height: 120px !important;
1200
+ font-family: "Microsoft YaHei", 微软雅黑, sans-serif;
1201
+ }
592
1202
  }
593
1203
  }
594
1204
 
595
1205
  .add_question_footer {
596
- padding: 20px 0;
597
- border-top: 1px solid #E6E6E6;
1206
+ padding-bottom: 30px;
598
1207
  display: flex;
599
1208
  justify-content: center;
600
1209
  align-items: center;
601
1210
  gap: 30px;
1211
+
1212
+ .button-handle {
1213
+ width: 168px;
1214
+ height: 48px;
1215
+ font-size: 14px;
1216
+ border-radius: 25px;
1217
+ border: none;
1218
+ color: #fff;
1219
+ cursor: pointer;
1220
+ transition: all 0.3s ease;
1221
+
1222
+ &:hover {
1223
+ transform: translateY(-1px);
1224
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
1225
+ }
1226
+ }
1227
+
1228
+ .button-generate-question {
1229
+ background: linear-gradient(to bottom, #7050D0 0%, #2828E0 100%);
1230
+ width: 280px;
1231
+ display: flex;
1232
+ justify-content: center;
1233
+ align-items: center;
1234
+ gap: 10px;
1235
+ }
1236
+
1237
+ .button-cancel {
1238
+ border: 1px solid #7050D0;
1239
+ background: transparent;
1240
+ color: #3030C0;
1241
+ }
602
1242
  }
603
- }
604
1243
 
605
- /* 对话框样式需要全局作用域,因为它会脱离当前组件 */
606
- </style>
1244
+ .ai-bot-switch {
1245
+ display: flex;
1246
+ align-items: center;
1247
+ background: transparent;
1248
+ justify-content: center;
1249
+ border-radius: 49px;
1250
+ -webkit-user-select: none;
1251
+ -moz-user-select: none;
1252
+ user-select: none;
1253
+ height: 40px;
1254
+ width: 144px;
1255
+ cursor: pointer;
1256
+ z-index: 2;
1257
+ border: 1px solid #6040E0A3;
1258
+ font-size: 12px;
1259
+ color: #182028;
1260
+ }
1261
+
1262
+ .ai-bot-label {
1263
+ font-weight: 500;
1264
+ margin-right: 2px;
1265
+ margin-left: 5px;
1266
+ width: 72px;
1267
+ }
1268
+
1269
+ .ai-bot-dropdown {
1270
+ background: #fff;
1271
+ border-radius: 8px;
1272
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
1273
+ border: 1px solid #e0e0ff;
1274
+ padding: 4px 0;
1275
+ }
1276
+
1277
+ .ai-bot-dropdown-item {
1278
+ padding: 8px 20px;
1279
+ cursor: pointer;
1280
+ font-size: 14px;
1281
+ color: #333;
1282
+ transition: background 0.2s;
1283
+ }
1284
+
1285
+ .ai-bot-dropdown-item.active,
1286
+ .ai-bot-dropdown-item:hover {
1287
+ background: #f0f2f8;
1288
+ color: #3830E0;
1289
+ }
1290
+
1291
+ // 题目结果抽屉样式
1292
+ .question-result-drawer {
1293
+ ::v-deep .el-drawer__body {
1294
+ padding: 0;
1295
+ height: 100%;
1296
+ display: flex;
1297
+ flex-direction: column;
1298
+ }
1299
+
1300
+ // 内部loading样式
1301
+ .generating-loading {
1302
+ display: flex;
1303
+ justify-content: center;
1304
+ align-items: center;
1305
+ height: 200px;
1306
+
1307
+ .loading-content {
1308
+ display: flex;
1309
+ flex-direction: column;
1310
+ align-items: center;
1311
+ gap: 16px;
1312
+
1313
+ .loading-spinner {
1314
+ width: 40px;
1315
+ height: 40px;
1316
+ border: 4px solid #f3f3f3;
1317
+ border-top: 4px solid #7C4DFF;
1318
+ border-radius: 50%;
1319
+ animation: spin 1s linear infinite;
1320
+ }
1321
+
1322
+ .loading-text {
1323
+ font-size: 14px;
1324
+ color: #7C4DFF;
1325
+ font-weight: 500;
1326
+ }
1327
+ }
1328
+ }
1329
+
1330
+ @keyframes spin {
1331
+ 0% { transform: rotate(0deg); }
1332
+ 100% { transform: rotate(360deg); }
1333
+ }
1334
+
1335
+ .drawer_content_wrapper {
1336
+ display: flex;
1337
+ flex-direction: column;
1338
+ height: 100%;
1339
+ padding: 0 20px;
1340
+ }
1341
+
1342
+ .question_summary_warp {
1343
+ display: flex;
1344
+ justify-content: space-between;
1345
+ margin-bottom: 20px;
1346
+
1347
+ .question_summary {
1348
+ .summary_text {
1349
+ color: #6040E0;
1350
+ font-size: 14px;
1351
+ font-weight: bold;
1352
+ }
1353
+
1354
+ .question_types_summary {
1355
+ .type_item {
1356
+ font-size: 12px;
1357
+ color: #182028A3;
1358
+ }
1359
+ }
1360
+ }
1361
+
1362
+ .question_summary_button {
1363
+ display: flex;
1364
+ gap: 15px;
1365
+ margin-right: 15px;
1366
+
1367
+ .button-handle {
1368
+ width: 96px;
1369
+ height: 40px;
1370
+ font-size: 14px;
1371
+ border-radius: 25px;
1372
+ border: none;
1373
+ color: #fff;
1374
+ cursor: pointer;
1375
+ transition: all 0.3s ease;
1376
+ }
1377
+ .button-generate-question {
1378
+ background: linear-gradient(to bottom, #7050D0 0%, #2828E0 100%);
1379
+ display: flex;
1380
+ justify-content: center;
1381
+ align-items: center;
1382
+ gap: 10px;
1383
+ }
1384
+
1385
+ .button-cancel {
1386
+ border: 1px solid #7050D0;
1387
+ background: transparent;
1388
+ color: #3030C0;
1389
+ }
1390
+ }
1391
+ }
1392
+
1393
+ .question_body {
1394
+ flex: 1;
1395
+ overflow-y: auto;
1396
+ padding-right: 10px;
1397
+
1398
+ &::-webkit-scrollbar {
1399
+ width: 6px;
1400
+ }
1401
+
1402
+ &::-webkit-scrollbar-track {
1403
+ background: rgba(255, 255, 255, 0.3);
1404
+ border-radius: 3px;
1405
+ }
607
1406
 
1407
+ &::-webkit-scrollbar-thumb {
1408
+ background: rgba(124, 77, 255, 0.3);
1409
+ border-radius: 3px;
1410
+
1411
+ &:hover {
1412
+ background: rgba(124, 77, 255, 0.5);
1413
+ }
1414
+ }
1415
+ }
1416
+
1417
+ .question_footer_fixed {
1418
+ flex-shrink: 0;
1419
+ padding: 0 0 10px 0;
1420
+ background: rgba(255, 255, 255, 0.9);
1421
+ border-top: 1px solid rgba(255, 255, 255, 0.3);
1422
+ backdrop-filter: blur(10px);
1423
+
1424
+ .footer_warning {
1425
+ display: flex;
1426
+ align-items: center;
1427
+ gap: 8px;
1428
+
1429
+ .el-icon-info {
1430
+ width: 24px;
1431
+ height: 24px;
1432
+ flex-shrink: 0;
1433
+ }
1434
+
1435
+ .question_remind {
1436
+ font-size: 12px;
1437
+ color: #797979;
1438
+ line-height: 1.4;
1439
+ }
1440
+ }
1441
+ }
1442
+ }
1443
+ }
1444
+ </style>
608
1445
 
609
1446
  <style lang="scss">
610
1447
  .ai-custom-dialog.el-dialog {
@@ -616,7 +1453,7 @@ export default {
616
1453
  align-items: center;
617
1454
  font-weight: 700;
618
1455
  font-size: 23px;
619
- color: #333333;
1456
+ color: #181820;
620
1457
 
621
1458
  img {
622
1459
  margin-right: 10px;
@@ -652,11 +1489,25 @@ export default {
652
1489
  .question_footer {
653
1490
  display: flex;
654
1491
  justify-content: space-between;
1492
+ align-items: center;
655
1493
 
656
- .question_remind {
657
- align-self: flex-end;
658
- font-size: 14px;
659
- color: #9BA1AE;
1494
+ .footer_warning {
1495
+ display: flex;
1496
+ align-items: center;
1497
+ gap: 8px;
1498
+ flex: 1;
1499
+
1500
+ .el-icon-info {
1501
+ color: #7C4DFF;
1502
+ font-size: 16px;
1503
+ flex-shrink: 0;
1504
+ }
1505
+
1506
+ .question_remind {
1507
+ font-size: 14px;
1508
+ color: #9BA1AE;
1509
+ line-height: 1.4;
1510
+ }
660
1511
  }
661
1512
  }
662
1513
  }