Commit 859161da96ef708f719c08401c01198b5956aa2c

Authored by LH_PC
2 parents f1d1ffae ce926d5e

Merge branch 'ezTeach-2.0.0.0.release' of http://120.78.57.84/baoman/Ezquiz_Plat…

…form into ezTeach-2.0.0.0.release
src/api/apis/apis.js
@@ -1059,4 +1059,8 @@ export default { @@ -1059,4 +1059,8 @@ export default {
1059 getGradeList(data) { 1059 getGradeList(data) {
1060 return defaltGetService(setUpUrls.teacherGradeList, data); 1060 return defaltGetService(setUpUrls.teacherGradeList, data);
1061 }, 1061 },
  1062 + // 获取班级信息
  1063 + getWrongQuestionSave(data) {
  1064 + return defaltService(setUpUrls.getWrongQuestionSave, data);
  1065 + },
1062 }; 1066 };
src/api/urls/apis.js
@@ -495,9 +495,9 @@ export default { @@ -495,9 +495,9 @@ export default {
495 //获取即时测报表学生名单和题目列表 495 //获取即时测报表学生名单和题目列表
496 listStudentsAndQuestions: "/api_html/teaching/listStudentsAndQuestions", 496 listStudentsAndQuestions: "/api_html/teaching/listStudentsAndQuestions",
497 //分页查询授课端日志列表 497 //分页查询授课端日志列表
498 - deviceZipLogList: "/api_html/school/manager/deviceZipLogList", 498 + deviceZipLogList: "/api_html/school/manager/deviceZipLogList",
499 //获取即时测报表录分情况 499 //获取即时测报表录分情况
500 - getScoreType: "/api_html/teaching/getScoreType", 500 + getScoreType: "/api_html/teaching/getScoreType",
501 // 获取错题本列表 501 // 获取错题本列表
502 wrongQuestionList: "api_html/teaching/wrongQuestion/list", 502 wrongQuestionList: "api_html/teaching/wrongQuestion/list",
503 // 获取科目列表 503 // 获取科目列表
@@ -506,4 +506,6 @@ export default { @@ -506,4 +506,6 @@ export default {
506 teacherClassList: "api_html/teaching/classList", 506 teacherClassList: "api_html/teaching/classList",
507 // 获取班级信息 507 // 获取班级信息
508 teacherGradeList: "api_html/teaching/grade", 508 teacherGradeList: "api_html/teaching/grade",
  509 + // 保存接口
  510 + getWrongQuestionSave: "api_html/teaching/wrongQuestion/save",
509 }; 511 };
src/assets/css/base.css
@@ -49,6 +49,6 @@ input[type="number"]::-webkit-outer-spin-button { @@ -49,6 +49,6 @@ input[type="number"]::-webkit-outer-spin-button {
49 background: #667ffd; 49 background: #667ffd;
50 } 50 }
51 51
52 -.el-message-box .el-button--default { 52 +/* .el-message-box .el-button--default {
53 color: #fff; 53 color: #fff;
54 -} 54 +} */
src/views/basic/askTestQuestion/components/wrongQuestionDialog.vue
@@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
7 > 7 >
8 <div class="test-box" v-if="groups"> 8 <div class="test-box" v-if="groups">
9 <div class="test"> 9 <div class="test">
10 - <div class="test-title" @dblclick="enableEditing"> 10 + <div class="test-title" @click="enableEditing">
11 <div 11 <div
12 class="test-title" 12 class="test-title"
13 v-if="!isEditing" 13 v-if="!isEditing"
@@ -25,7 +25,7 @@ @@ -25,7 +25,7 @@
25 left: tooltipPosition.x + 'px', 25 left: tooltipPosition.x + 'px',
26 }" 26 }"
27 > 27 >
28 - 击设置试卷标题 28 + 击设置试卷标题
29 </div> 29 </div>
30 <el-input 30 <el-input
31 class="test-title" 31 class="test-title"
@@ -41,25 +41,38 @@ @@ -41,25 +41,38 @@
41 <div 41 <div
42 class="outer-item" 42 class="outer-item"
43 v-for="(group, index) in groups" 43 v-for="(group, index) in groups"
44 - :key="group.id" 44 + :key="group.questionType"
45 :class="{ 45 :class="{
46 - border: selectedIndex === group.id, 46 + border: selectedIndex === group.questionType,
47 }" 47 }"
  48 + @mouseenter="handleGroupMouseEnter(group.questionType, 'all')"
  49 + @mouseleave="handleGroupMouseLeave"
48 > 50 >
49 <div 51 <div
50 - v-if="selectedIndex === group.id" 52 + v-if="selectedIndex == group.questionType"
51 class="border-del" 53 class="border-del"
52 - @click="handleDel(group.id, 'all')" 54 + @click="handleDel(group.questionType, 'all')"
53 > 55 >
54 删除 56 删除
55 </div> 57 </div>
56 <!-- 每个组的标题 --> 58 <!-- 每个组的标题 -->
57 - <div class="test-group-title" @click="selectItem(group.id, 'all')"> 59 + <div
  60 + class="test-group-title"
  61 + :class="{
  62 + border: selectedIndex == group.questionType,
  63 + }"
  64 + @mouseenter="handleGroupMouseEnter(group.questionType, 'all')"
  65 + >
58 <div class="size-16" style="font-weight: 600"> 66 <div class="size-16" style="font-weight: 600">
59 {{ index + 1 }}、 67 {{ index + 1 }}、
60 <span 68 <span
  69 + @mouseenter="
  70 + (event) => handleItemMouseEnter(event, group.questionType)
  71 + "
  72 + @mouseleave="handleItemMouseLeave"
  73 + :class="isItemHovered == group.questionType ? 'hover' : ''"
61 class="size-16" 74 class="size-16"
62 - @dblclick="groupEditing(index)" 75 + @click="groupEditing(index)"
63 v-show="!group.input" 76 v-show="!group.input"
64 >{{ group.questionTitle }}</span 77 >{{ group.questionTitle }}</span
65 > 78 >
@@ -74,6 +87,17 @@ @@ -74,6 +87,17 @@
74 @keyup.enter.native="groupSave(index)" 87 @keyup.enter.native="groupSave(index)"
75 style="width: fit-content" 88 style="width: fit-content"
76 /> 89 />
  90 + <div
  91 + v-if="isItemHovered == group.questionType"
  92 + class="tooltip"
  93 + :style="{
  94 + top: tooltipPosition.y + 'px',
  95 + left: tooltipPosition.x + 'px',
  96 + }"
  97 + >
  98 + 单击设置试卷标题
  99 + </div>
  100 +
77 <span 101 <span
78 v-if="group.subQuestionIds" 102 v-if="group.subQuestionIds"
79 class="size-16" 103 class="size-16"
@@ -87,36 +111,69 @@ @@ -87,36 +111,69 @@
87 111
88 <!-- 内部的题目列表,允许组内排序 --> 112 <!-- 内部的题目列表,允许组内排序 -->
89 <div 113 <div
90 - style="display: flex; padding: 10px 0"  
91 - v-for="(question, subIndex) in group.subQuestionIds" 114 + style="display: flex"
  115 + v-for="question in group.subQuestionIds"
92 :key="question.id" 116 :key="question.id"
93 :class="{ 117 :class="{
94 border: selectedItemIndex === question.id, 118 border: selectedItemIndex === question.id,
95 }" 119 }"
96 - @click="selectItem(question.id, 'number')" 120 + @mouseenter="handleGroupMouseEnter(question.id, 'number')"
  121 + @mouseleave="handleGroupMouseLeave"
97 class="inner-item" 122 class="inner-item"
98 > 123 >
99 <div 124 <div
100 v-if="selectedItemIndex === question.id" 125 v-if="selectedItemIndex === question.id"
101 class="border-del" 126 class="border-del"
102 - @click="handleDel(question.id, 'number')" 127 + @click="handleDel(question.id, group.questionType)"
103 > 128 >
104 删除 129 删除
105 </div> 130 </div>
106 - <div class="size color">{{ question.globalIndex }}、</div> 131 + <div class="size color" style="margin-top: 20px">
  132 + <div
  133 + style="max-width: 100px; white-space: nowrap; /* 不换行 */"
  134 + >
  135 + {{ question.globalIndex }}、
  136 + </div>
  137 + </div>
107 138
108 <iframe 139 <iframe
109 :src="question.screenshot" 140 :src="question.screenshot"
110 - :ref="'iframe' + subIndex"  
111 - @load="onIFrameLoad(subIndex)"  
112 - style="width: 100%" 141 + :ref="'iframe' + question.id"
  142 + @load="onIFrameLoad(question.id)"
  143 + style="
  144 + width: 100%;
  145 + pointer-events: none;
  146 + border: none; /* 启用点击穿透 */
  147 + "
113 ></iframe> 148 ></iframe>
114 </div> 149 </div>
115 </div> 150 </div>
116 </div> 151 </div>
117 </div> 152 </div>
118 <div class="edit"> 153 <div class="edit">
119 - <div class="edit-title"></div> 154 + <div class="edit-title">
  155 + <div class="edit-title-info">
  156 + 共<span>{{ list.length }}</span
  157 + >小题,卷面分<span>{{ sumValues() }}</span
  158 + >分
  159 + </div>
  160 + <div class="edit-button">
  161 + <el-button class="button-width" type="primary" @click="handleSava"
  162 + >保存试卷</el-button
  163 + >
  164 + <el-button class="button-width" plain>保存并打印</el-button>
  165 + <el-button
  166 + class="button-width"
  167 + type="danger"
  168 + plain
  169 + @click="handleClear"
  170 + >清空题目</el-button
  171 + >
  172 + <el-button class="button-width" plain @click="handleSelect"
  173 + >继续选题</el-button
  174 + >
  175 + </div>
  176 + </div>
120 <div class="edit-info"> 177 <div class="edit-info">
121 <div class="title size color" style="padding: 15px 0 0 10px"> 178 <div class="title size color" style="padding: 15px 0 0 10px">
122 题目排序 179 题目排序
@@ -173,6 +230,7 @@ @@ -173,6 +230,7 @@
173 </el-dialog> 230 </el-dialog>
174 </template> 231 </template>
175 <script> 232 <script>
  233 +import { number } from "echarts";
176 import draggable from "vuedraggable"; 234 import draggable from "vuedraggable";
177 export default { 235 export default {
178 components: { draggable }, 236 components: { draggable },
@@ -184,7 +242,7 @@ export default { @@ -184,7 +242,7 @@ export default {
184 return false; 242 return false;
185 }, 243 },
186 }, 244 },
187 - ids: { 245 + list: {
188 // 用于请求详情接口 246 // 用于请求详情接口
189 type: Array, 247 type: Array,
190 default() { 248 default() {
@@ -198,6 +256,13 @@ export default { @@ -198,6 +256,13 @@ export default {
198 return null; 256 return null;
199 }, 257 },
200 }, 258 },
  259 + gradeId: {
  260 + // 科目
  261 + type: [String, Number],
  262 + default() {
  263 + return null;
  264 + },
  265 + },
201 }, 266 },
202 data() { 267 data() {
203 return { 268 return {
@@ -207,8 +272,10 @@ export default { @@ -207,8 +272,10 @@ export default {
207 selectedIndex: null, // 当前选中的外层对象的索引 272 selectedIndex: null, // 当前选中的外层对象的索引
208 selectedItemIndex: null, // 当前选中的内层对象的索引 273 selectedItemIndex: null, // 当前选中的内层对象的索引
209 isHovered: false, // 用于控制边框和提示文字的显示 274 isHovered: false, // 用于控制边框和提示文字的显示
  275 + isItemHovered: false, // 用于控制边框和提示文字的显示
210 tooltipPosition: { x: 0, y: 0 }, // 提示文字的位置 276 tooltipPosition: { x: 0, y: 0 }, // 提示文字的位置
211 time: "", 277 time: "",
  278 + testData: [], // 存一份list
212 questionList: [ 279 questionList: [
213 { label: "全部", value: null }, 280 { label: "全部", value: null },
214 { label: "单选题", value: 2 }, 281 { label: "单选题", value: 2 },
@@ -216,91 +283,56 @@ export default { @@ -216,91 +283,56 @@ export default {
216 { label: "判断题", value: 4 }, 283 { label: "判断题", value: 4 },
217 { label: "主观题", value: 5 }, 284 { label: "主观题", value: 5 },
218 ], 285 ],
219 - groups: [  
220 - {  
221 - id: 1,  
222 - name: "单选题",  
223 - input: false,  
224 - items: [  
225 - { id: 1, content: "组一题目1", globalIndex: 1 },  
226 - { id: 2, content: "组一题目2", globalIndex: 2 },  
227 - { id: 3, content: "组一题目3", globalIndex: 3 },  
228 - ],  
229 - },  
230 - {  
231 - id: 2,  
232 - name: "多选题",  
233 - input: false,  
234 - items: [  
235 - { id: 4, content: "组二题目1", globalIndex: 4 },  
236 - { id: 5, content: "组二题目2", globalIndex: 5 },  
237 - { id: 6, content: "组二题目3", globalIndex: 6 },  
238 - ],  
239 - },  
240 - {  
241 - id: 3,  
242 - name: "填空题",  
243 - input: false,  
244 - items: [  
245 - { id: 7, content: "组三题目1", globalIndex: 7 },  
246 - { id: 8, content: "组三题目2", globalIndex: 8 },  
247 - { id: 9, content: "组三题目3", globalIndex: 9 },  
248 - { id: 10, content: "组三题目3", globalIndex: 10 },  
249 - { id: 11, content: "组三题目3", globalIndex: 11 },  
250 - { id: 12, content: "组三题目3", globalIndex: 12 },  
251 - { id: 13, content: "组三题目3", globalIndex: 13 },  
252 - { id: 14, content: "组三题目3", globalIndex: 14 },  
253 - { id: 15, content: "组三题目3", globalIndex: 15 },  
254 - { id: 16, content: "组三题目3", globalIndex: 16 },  
255 - { id: 17, content: "组三题目3", globalIndex: 17 },  
256 - ],  
257 - },  
258 - {  
259 - id: 4,  
260 - name: "问答题",  
261 - input: false,  
262 - items: [  
263 - { id: 18, content: "组二题目1", globalIndex: 18 },  
264 - { id: 19, content: "组二题目2", globalIndex: 19 },  
265 - { id: 20, content: "组二题目3", globalIndex: 20 },  
266 - ],  
267 - },  
268 - ], 286 + groups: [],
269 }; 287 };
270 }, 288 },
271 289
272 watch: { 290 watch: {
273 visible(val) { 291 visible(val) {
274 if (val) { 292 if (val) {
275 - // 初始化  
276 - this.init(); 293 + // 上面数据测试
  294 + this.testData = JSON.parse(localStorage.getItem("testData")) || [];
  295 + this.groups = JSON.parse(localStorage.getItem("question")) || [];
  296 + // 如果都没有直接return 执行初始化
  297 + if (this.groups.length == 0) {
  298 + this.init();
  299 + return;
  300 + }
  301 + // 如果一致 就直接赋值
  302 + if (
  303 + this.arraysHaveSameIds(this.testData, this.list) &&
  304 + JSON.parse(localStorage.getItem("question"))
  305 + ) {
  306 + return;
  307 + } else {
  308 + let data = this.findMissingIds(this.testData, this.list);
  309 + this.testAddDel(this.groups, data.addArr, data.delArr);
  310 + }
277 } else { 311 } else {
278 - // 关闭后处理  
279 - localStorage.setItem("question", JSON.stringify(this.groups)); 312 + this.testDataFun();
280 } 313 }
281 }, 314 },
282 }, 315 },
283 methods: { 316 methods: {
284 // 初始化 317 // 初始化
285 - async init() {  
286 - let dataList = JSON.parse(localStorage.getItem("question"));  
287 - if (dataList && dataList.length > 0) {  
288 - this.groups = dataList;  
289 - return;  
290 - }  
291 - console.log("初始化成功"); 318 + init() {
  319 + console.log("初始化");
292 this.time = this.getFormattedDate(); 320 this.time = this.getFormattedDate();
293 this.inputValue = this.time + this.subjectName + "--错题复习"; 321 this.inputValue = this.time + this.subjectName + "--错题复习";
294 - this.$request  
295 - .getWrongQuestionList({ ids: this.ids, page: 1, size: 99999 })  
296 - .then((res) => {  
297 - this.groups = this.classifyByType(  
298 - res.data.records.map((item, index) => {  
299 - return { ...item, globalIndex: index };  
300 - })  
301 - );  
302 - console.log(this.groups, "=-=-=");  
303 - }); 322 + this.groups = this.classifyByType(
  323 + this.list.map((item, index) => {
  324 + return { ...item, globalIndex: index + 1 };
  325 + })
  326 + );
  327 + this.updateGlobalIndexes();
  328 + },
  329 + // 随时保存testData
  330 + testDataFun() {
  331 + this.testData =
  332 + this.groups.reduce((accumulator, current) => {
  333 + return accumulator.concat(current.subQuestionIds);
  334 + }, []) || [];
  335 + localStorage.setItem("testData", JSON.stringify(this.testData));
304 }, 336 },
305 /** 按钮 - 取消 */ 337 /** 按钮 - 取消 */
306 handleClose() { 338 handleClose() {
@@ -324,6 +356,7 @@ export default { @@ -324,6 +356,7 @@ export default {
324 allItems.forEach((item, index) => { 356 allItems.forEach((item, index) => {
325 item.globalIndex = index + 1; 357 item.globalIndex = index + 1;
326 }); 358 });
  359 + localStorage.setItem("question", JSON.stringify(this.groups));
327 }, 360 },
328 classifyByType(arr) { 361 classifyByType(arr) {
329 const result = {}; 362 const result = {};
@@ -337,17 +370,29 @@ export default { @@ -337,17 +370,29 @@ export default {
337 370
338 // 将结果对象转换为数组 371 // 将结果对象转换为数组
339 return Object.keys(result).map((type) => ({ 372 return Object.keys(result).map((type) => ({
340 - type: type, 373 + questionType: type,
  374 + input: false,
341 questionTitle: this.questionList.find((item) => item.value == type) 375 questionTitle: this.questionList.find((item) => item.value == type)
342 .label, 376 .label,
343 questionScore: 1, 377 questionScore: 1,
344 subQuestionIds: result[type], 378 subQuestionIds: result[type],
345 })); 379 }));
346 }, 380 },
347 - onIFrameLoad(index) {  
348 - const iframeRef = this.$refs["iframe" + index][0]; // 获取对应的 iframe  
349 - console.log(iframeRef, "ref");  
350 - console.log(iframeRef.contentWindow.document, "document"); 381 + onIFrameLoad(id) {
  382 + const iframeRef = this.$refs["iframe" + id][0]; // 获取对应的 iframe
  383 + const doc = iframeRef.contentDocument || iframeRef.contentWindow.document;
  384 + const body = iframeRef.contentWindow.document.body;
  385 + const height = body.scrollHeight; // 获取内容的高度
  386 + console.log(height, "body高度");
  387 + iframeRef.style.height = `${height}px`; // 设置 iframe 的高度
  388 + // 获取第一个P标签
  389 + const firstP = doc.getElementsByTagName("p")[0];
  390 + // 或者修改第一个 < p > 标签的内容;
  391 + if (firstP) {
  392 + console.log(firstP, firstP.innerHTML, "-----innerHTML-----");
  393 + let a = this.processString(firstP.innerHTML);
  394 + firstP.innerHTML = a;
  395 + }
351 }, 396 },
352 397
353 // 启动编辑模式 398 // 启动编辑模式
@@ -357,20 +402,18 @@ export default { @@ -357,20 +402,18 @@ export default {
357 this.$nextTick(() => { 402 this.$nextTick(() => {
358 // 在 DOM 更新完成后让输入框获取焦点 403 // 在 DOM 更新完成后让输入框获取焦点
359 this.$refs.editInput.focus(); 404 this.$refs.editInput.focus();
360 - console.log(this.$refs.editInput);  
361 }); 405 });
362 }, 406 },
363 407
364 // 保存修改后的内容,回车或失去焦点时触发 408 // 保存修改后的内容,回车或失去焦点时触发
365 save() { 409 save() {
366 - console.log("回车事件");  
367 -  
368 this.isEditing = false; 410 this.isEditing = false;
369 - console.log("保存的内容:", this.inputValue); // 可根据需要将内容保存到其他地方 411 + localStorage.setItem("question", JSON.stringify(this.groups));
370 }, 412 },
371 413
372 // 单题title修改 414 // 单题title修改
373 groupEditing(index) { 415 groupEditing(index) {
  416 + this.isItemHovered = null;
374 this.$set(this.groups[index], "input", true); 417 this.$set(this.groups[index], "input", true);
375 418
376 // DOM 更新完成后让对应的输入框获取焦点 419 // DOM 更新完成后让对应的输入框获取焦点
@@ -382,6 +425,7 @@ export default { @@ -382,6 +425,7 @@ export default {
382 // 单体title保存 425 // 单体title保存
383 groupSave(index) { 426 groupSave(index) {
384 this.$set(this.groups[index], "input", false); 427 this.$set(this.groups[index], "input", false);
  428 + localStorage.setItem("question", JSON.stringify(this.groups));
385 }, 429 },
386 // 高亮 430 // 高亮
387 selectItem(id, type) { 431 selectItem(id, type) {
@@ -396,14 +440,31 @@ export default { @@ -396,14 +440,31 @@ export default {
396 // 删除题目 440 // 删除题目
397 handleDel(id, type) { 441 handleDel(id, type) {
398 if (type == "all") { 442 if (type == "all") {
399 - console.log("删除整个大题"); 443 + let _index = this.groups.findIndex((item) => item.questionType == id);
  444 + if (_index != -1) {
  445 + this.groups.splice(_index, 1);
  446 + localStorage.setItem("question", JSON.stringify(this.groups));
  447 + this.updateGlobalIndexes();
  448 + this.testDataFun();
  449 + localStorage.setItem("testlist", JSON.stringify(this.testData));
  450 + this.$emit("setQuestions");
  451 + }
400 } else { 452 } else {
401 - console.log("删除整个小题"); 453 + this.groups.forEach((item) => {
  454 + if (type == item.questionType) {
  455 + item.subQuestionIds = item.subQuestionIds.filter(
  456 + (item) => item.id !== id
  457 + );
  458 + }
  459 + });
  460 + localStorage.setItem("question", JSON.stringify(this.groups));
  461 + this.updateGlobalIndexes();
  462 + this.testDataFun();
  463 + localStorage.setItem("testlist", JSON.stringify(this.testData));
  464 + this.$emit("setQuestions");
402 } 465 }
403 }, 466 },
404 handleMouseEnter(event) { 467 handleMouseEnter(event) {
405 - console.log(event);  
406 -  
407 this.isHovered = true; 468 this.isHovered = true;
408 // 计算提示文字位置 (鼠标右下角) 469 // 计算提示文字位置 (鼠标右下角)
409 this.tooltipPosition = { 470 this.tooltipPosition = {
@@ -424,6 +485,219 @@ export default { @@ -424,6 +485,219 @@ export default {
424 const day = today.getDate(); // 获取日期 485 const day = today.getDate(); // 获取日期
425 return `${year}年${month}月${day}日`; 486 return `${year}年${month}月${day}日`;
426 }, 487 },
  488 +
  489 + // 接收两个参数
  490 + arraysHaveSameIds(arr1, arr2) {
  491 + // 检查两个数组长度是否相同
  492 + if (arr1.length !== arr2.length) {
  493 + return false;
  494 + }
  495 + // 提取两个数组中所有的 id
  496 + const ids1 = arr1.map((item) => item.id);
  497 + const ids2 = arr2.map((item) => item.id);
  498 + // 将 id 数组排序
  499 + ids1.sort();
  500 + ids2.sort();
  501 + // 比较两个 id 数组是否一致
  502 + for (let i = 0; i < ids1.length; i++) {
  503 + if (ids1[i] !== ids2[i]) {
  504 + return false;
  505 + }
  506 + }
  507 + return true;
  508 + },
  509 + // 接收两个参数
  510 + arrContrast(arr1, arr2) {
  511 + // 如果两个数组的长度不同,直接返回 true
  512 + if (arr1.length !== arr2.length) {
  513 + return true;
  514 + }
  515 +
  516 + // 创建一个 id 集合来存放 arr1 中的 id
  517 + const idsSet1 = new Set(arr1.map((obj) => obj.id));
  518 + // 创建一个 id 集合来存放 arr2 中的 id
  519 + const idsSet2 = new Set(arr2.map((obj) => obj.id));
  520 +
  521 + // 比较两个集合的大小,如果不同,返回 true
  522 + if (idsSet1.size !== idsSet2.size) {
  523 + return true;
  524 + }
  525 +
  526 + // 比较两个集合的内容
  527 + for (const id of idsSet1) {
  528 + if (!idsSet2.has(id)) {
  529 + return true;
  530 + }
  531 + }
  532 +
  533 + // 如果所有的 id 都一致,返回 false
  534 + return false;
  535 + },
  536 + processString(input) {
  537 + // 正则表达式:匹配最多三位数字
  538 + const regex = /^(\d{1,3})/;
  539 +
  540 + // 测试字符串是否以数字开头
  541 + const match = input.match(regex);
  542 +
  543 + if (match) {
  544 + // 如果匹配,去掉开头的数字并返回剩余部分
  545 + return input.substring(match[0].length + 1).trim();
  546 + }
  547 +
  548 + // 如果没有匹配,直接返回原字符串
  549 + return input;
  550 + },
  551 +
  552 + // 接受两个参数,返回需要删除的数据以及需要添加的数据
  553 + findMissingIds(array1, array2) {
  554 + // 找出第一个数组中存在但第二个数组中不存在的 id
  555 + const delArr = array1.filter(
  556 + (item1) => !array2.some((item2) => item2.id === item1.id)
  557 + );
  558 + // 找出第二个数组中存在但第一个数组中不存在的 id
  559 + const addArr = array2.filter(
  560 + (item2) => !array1.some((item1) => item1.id === item2.id)
  561 + );
  562 +
  563 + return {
  564 + delArr,
  565 + addArr,
  566 + };
  567 + },
  568 +
  569 + // 添加,删除
  570 + // 需要的数据,添加的数据,删除的数据
  571 + testAddDel(arr1, arr2, arr3) {
  572 + console.log(arr1, arr3);
  573 + if (arr2 && arr2.length > 0) {
  574 + // 添加数据,如果没有type重新创建一个type
  575 + arr2.forEach((item2) => {
  576 + const typeMatch = arr1.find(
  577 + (item1) => item1.questionType == item2.questionType
  578 + );
  579 + if (typeMatch) {
  580 + typeMatch.subQuestionIds.push(item2);
  581 + } else {
  582 + arr1.push({
  583 + questionType: item2.questionType,
  584 + subQuestionIds: [item2],
  585 + questionTitle: this.questionList.find(
  586 + (item) => item.value == item2.questionType
  587 + ).label,
  588 + input: false,
  589 + questionScore: 1,
  590 + });
  591 + }
  592 + });
  593 + }
  594 + if (arr3 && arr3.length > 0) {
  595 + // 删除数据
  596 + arr3.forEach((item3) => {
  597 + // 在 array1 中找到匹配的 type
  598 + let matchedArray1Item = arr1.find(
  599 + (item1) => item1.questionType == item3.questionType
  600 + );
  601 + if (matchedArray1Item) {
  602 + // 如果找到了匹配的 type,遍历 matchedArray1Item 的 items
  603 + let _index = matchedArray1Item.subQuestionIds.findIndex(
  604 + (item) => item.id === item3.id
  605 + );
  606 + console.log("_index");
  607 +
  608 + if (_index != -1) {
  609 + matchedArray1Item.subQuestionIds.splice(_index, 1);
  610 + }
  611 + } else {
  612 + return;
  613 + }
  614 + });
  615 + }
  616 +
  617 + this.updateGlobalIndexes();
  618 + },
  619 +
  620 + // 鼠标移入
  621 + handleGroupMouseEnter(id, type) {
  622 + if (type == "all") {
  623 + this.selectedIndex = id;
  624 + this.selectedItemIndex = null;
  625 + } else {
  626 + this.selectedItemIndex = id;
  627 + this.selectedIndex = null;
  628 + }
  629 + },
  630 + // 鼠标离开
  631 + handleGroupMouseLeave() {
  632 + this.selectedIndex = null;
  633 + this.selectedItemIndex = null;
  634 + },
  635 +
  636 + // 小标题移入
  637 + handleItemMouseEnter(event, type) {
  638 + this.tooltipPosition = {
  639 + x: 50,
  640 + y: 40,
  641 + };
  642 +
  643 + this.isItemHovered = type;
  644 + },
  645 + // 小标题移除
  646 + handleItemMouseLeave() {
  647 + this.isItemHovered = null;
  648 + },
  649 +
  650 + // 保存
  651 + handleSava() {
  652 + let param = {
  653 + paperTitle: this.inputValue,
  654 + subjectName: this.subjectName,
  655 + gradeId: this.gradeId,
  656 + questions: this.groups.filter((item) => item.subQuestionIds.length > 0),
  657 + };
  658 + this.$request.getWrongQuestionSave(param).then((res) => {
  659 + if (res.status == 0) {
  660 + this.groups = [];
  661 + this.testData = [];
  662 + localStorage.setItem("testlist", JSON.stringify(this.questions));
  663 + localStorage.setItem("question", JSON.stringify(this.groups));
  664 + this.$emit("setQuestions");
  665 + this.testDataFun();
  666 + this.handleClose();
  667 + }
  668 + });
  669 + },
  670 +
  671 + // 清空
  672 + handleClear() {
  673 + this.$confirm("确定要清空试题篮内的全部题目吗?", "提示", {
  674 + confirmButtonText: "确定",
  675 + cancelButtonText: "取消",
  676 + type: "warning",
  677 + })
  678 + .then(() => {
  679 + this.groups = [];
  680 + this.testDataFun();
  681 + localStorage.setItem("question", JSON.stringify(this.groups));
  682 + localStorage.setItem("testlist", JSON.stringify(this.testData));
  683 + this.$emit("setQuestions");
  684 + this.handleClose();
  685 + })
  686 + .catch(() => {
  687 + console.log("取消");
  688 + });
  689 + },
  690 + // 继续选题
  691 + handleSelect() {
  692 + this.handleClose();
  693 + },
  694 +
  695 + // 计算总分
  696 + sumValues() {
  697 + return this.groups.reduce((sum, item) => {
  698 + return sum + Number(item.questionScore) * item.subQuestionIds.length;
  699 + }, 0);
  700 + },
427 }, 701 },
428 }; 702 };
429 </script> 703 </script>
@@ -433,13 +707,16 @@ export default { @@ -433,13 +707,16 @@ export default {
433 } 707 }
434 .test-box { 708 .test-box {
435 display: flex; 709 display: flex;
  710 + min-height: 742px;
436 .test { 711 .test {
437 flex: 1; 712 flex: 1;
438 height: fit-content; 713 height: fit-content;
439 - max-height: 642px; 714 + max-height: 742px;
440 overflow-y: auto; 715 overflow-y: auto;
441 box-shadow: 0 0 10px 0 #999999; 716 box-shadow: 0 0 10px 0 #999999;
442 margin-right: 20px; 717 margin-right: 20px;
  718 + box-sizing: border-box;
  719 + padding: 0 20px;
443 .test-title { 720 .test-title {
444 font-size: 20px !important; 721 font-size: 20px !important;
445 font-weight: 700; 722 font-weight: 700;
@@ -459,7 +736,10 @@ export default { @@ -459,7 +736,10 @@ export default {
459 } 736 }
460 .test-group { 737 .test-group {
461 .test-group-title { 738 .test-group-title {
462 - padding: 10px 0; 739 + position: relative;
  740 + height: 40px;
  741 + line-height: 30px;
  742 + padding: 10px 0 0 0;
463 } 743 }
464 } 744 }
465 } 745 }
@@ -471,16 +751,38 @@ export default { @@ -471,16 +751,38 @@ export default {
471 width: 100%; 751 width: 100%;
472 height: 200px; 752 height: 200px;
473 border: 1px solid #999999; 753 border: 1px solid #999999;
  754 + .edit-title-info {
  755 + height: 60px;
  756 + width: 100%;
  757 + text-align: center;
  758 + font-weight: 600;
  759 + font-size: 16px !important;
  760 + line-height: 60px;
  761 + color: #000000;
  762 + span {
  763 + color: rgb(234, 163, 73);
  764 + }
  765 + }
  766 + .edit-button {
  767 + box-sizing: border-box;
  768 + padding: 0 20px;
  769 + display: flex;
  770 + flex-wrap: wrap;
  771 + justify-content: space-around;
  772 + .button-width {
  773 + width: 160px !important;
  774 + margin: 10px 0;
  775 + }
  776 + }
474 } 777 }
475 .edit-info { 778 .edit-info {
476 flex: 1; 779 flex: 1;
477 border: 1px solid #999999; 780 border: 1px solid #999999;
478 margin-top: 20px; 781 margin-top: 20px;
479 - max-height: 400px; 782 + max-height: 500px;
480 padding: 10px; 783 padding: 10px;
481 overflow: auto; 784 overflow: auto;
482 .group-item { 785 .group-item {
483 - padding: 10px;  
484 margin: 10px; 786 margin: 10px;
485 background: rgba(243, 243, 243, 0.6); 787 background: rgba(243, 243, 243, 0.6);
486 border-radius: 5px; 788 border-radius: 5px;
@@ -551,8 +853,7 @@ export default { @@ -551,8 +853,7 @@ export default {
551 .outer-item, 853 .outer-item,
552 .inner-item { 854 .inner-item {
553 height: fit-content; 855 height: fit-content;
554 - padding: 0 20px;  
555 - margin: 5px; 856 + margin-bottom: 20px;
556 } 857 }
557 .tooltip { 858 .tooltip {
558 position: absolute; 859 position: absolute;
@@ -565,5 +866,10 @@ export default { @@ -565,5 +866,10 @@ export default {
565 .hover { 866 .hover {
566 border: 1px solid rgb(115, 142, 246); 867 border: 1px solid rgb(115, 142, 246);
567 background: rgb(233, 237, 253); 868 background: rgb(233, 237, 253);
  869 + width: fit-content;
  870 +}
  871 +.title-bg {
  872 + background: rgb(115, 142, 246);
  873 + border-bottom: 1px solid rgb(115, 142, 246);
568 } 874 }
569 </style> 875 </style>
570 \ No newline at end of file 876 \ No newline at end of file
src/views/basic/askTestQuestion/wrongQuestion.vue
@@ -160,7 +160,9 @@ @@ -160,7 +160,9 @@
160 <iframe 160 <iframe
161 class="topic-info" 161 class="topic-info"
162 :src="item.screenshot" 162 :src="item.screenshot"
163 - style="height: 100% !important" 163 + :ref="'iframe' + index"
  164 + style="width: 100%"
  165 + @load="onIFrameLoad(index)"
164 /> 166 />
165 <!-- <div class="topic-info" v-html="item.modifiedHtml"></div> --> 167 <!-- <div class="topic-info" v-html="item.modifiedHtml"></div> -->
166 <div class="topic-bottom"> 168 <div class="topic-bottom">
@@ -172,7 +174,7 @@ @@ -172,7 +174,7 @@
172 <span class="size">{{ item.knowledge }}</span> 174 <span class="size">{{ item.knowledge }}</span>
173 </div> 175 </div>
174 <el-button 176 <el-button
175 - v-if="!questions.includes(item.id)" 177 + v-if="!questions.some((obj) => item.id === obj.id)"
176 class="button" 178 class="button"
177 type="primary" 179 type="primary"
178 @click="handleAddTest(item)" 180 @click="handleAddTest(item)"
@@ -207,7 +209,7 @@ @@ -207,7 +209,7 @@
207 v-if=" 209 v-if="
208 !arraysEqual( 210 !arraysEqual(
209 topicList.map((item) => item.id), 211 topicList.map((item) => item.id),
210 - questions 212 + questions.map((item) => item.id)
211 ) 213 )
212 " 214 "
213 @click="handleAllTest()" 215 @click="handleAllTest()"
@@ -233,8 +235,11 @@ @@ -233,8 +235,11 @@
233 </div> 235 </div>
234 <wrongQuestionDialog 236 <wrongQuestionDialog
235 :visible.sync="visible" 237 :visible.sync="visible"
236 - :ids="questions" 238 + :list="questions"
237 :subjectName="formData.subjectName" 239 :subjectName="formData.subjectName"
  240 + :sectionId="formData.sectionId"
  241 + :gradeId="formData.grade"
  242 + @setQuestions="setQuestions"
238 /> 243 />
239 </div> 244 </div>
240 </template> 245 </template>
@@ -286,7 +291,7 @@ export default { @@ -286,7 +291,7 @@ export default {
286 formData: { 291 formData: {
287 classId: 1, // 班级选中 292 classId: 1, // 班级选中
288 // type: "onDay", // 时间类型 293 // type: "onDay", // 时间类型
289 - type: "onWeek", // 时间类型 294 + type: "term", // 时间类型
290 dateRange: ["2022-03-01", "2023-05-01"], // 开始结束时间 295 dateRange: ["2022-03-01", "2023-05-01"], // 开始结束时间
291 startScoreRate: 0, // 开始区间 296 startScoreRate: 0, // 开始区间
292 endScoreRate: 100, // 结束区间 297 endScoreRate: 100, // 结束区间
@@ -295,13 +300,19 @@ export default { @@ -295,13 +300,19 @@ export default {
295 grade: null, // 年级 300 grade: null, // 年级
296 gradeName: null, // 年级名称 301 gradeName: null, // 年级名称
297 }, 302 },
298 - questions: JSON.parse(localStorage.getItem("testlist")) || [], 303 + questions: [],
299 }; 304 };
300 }, 305 },
301 created() { 306 created() {
302 this.handleDate(this.formData.type); 307 this.handleDate(this.formData.type);
303 this.queryLoading = true; 308 this.queryLoading = true;
304 this.getGradeList(); 309 this.getGradeList();
  310 + if (!localStorage.getItem("testlist")) {
  311 + localStorage.setItem("testlist", JSON.stringify(this.questions));
  312 + } else {
  313 + this.questions = JSON.parse(localStorage.getItem("testlist"));
  314 + console.log(this.questions, "===");
  315 + }
305 }, 316 },
306 methods: { 317 methods: {
307 async loadAndModifyHTML(array) { 318 async loadAndModifyHTML(array) {
@@ -416,14 +427,15 @@ export default { @@ -416,14 +427,15 @@ export default {
416 427
417 // 加入试卷 428 // 加入试卷
418 handleAddTest(item) { 429 handleAddTest(item) {
419 - if (this.questions.includes(item.id)) return; 430 + // if (this.questions.includes(item.id)) return;
  431 + if (this.questions.some((obj) => obj.id === item.id)) return;
420 this.questions = JSON.parse(localStorage.getItem("testlist")) || []; 432 this.questions = JSON.parse(localStorage.getItem("testlist")) || [];
421 - this.questions.push(item.id); 433 + this.questions.push(item);
422 localStorage.setItem("testlist", JSON.stringify(this.questions)); 434 localStorage.setItem("testlist", JSON.stringify(this.questions));
423 }, 435 },
424 // 移除试卷 436 // 移除试卷
425 handleDel(item) { 437 handleDel(item) {
426 - let _index = this.questions.indexOf(item.id); 438 + let _index = this.questions.findIndex((obj) => obj.id === item.id);
427 if (_index !== -1) { 439 if (_index !== -1) {
428 this.questions.splice(_index, 1); // 从索引位置删除一个元素 440 this.questions.splice(_index, 1); // 从索引位置删除一个元素
429 localStorage.setItem("testlist", JSON.stringify(this.questions)); 441 localStorage.setItem("testlist", JSON.stringify(this.questions));
@@ -432,12 +444,13 @@ export default { @@ -432,12 +444,13 @@ export default {
432 // 全部选中 444 // 全部选中
433 handleAllTest() { 445 handleAllTest() {
434 // 获取原来的localStorage 446 // 获取原来的localStorage
435 - let list = JSON.parse(localStorage.getItem("testlist"));  
436 - console.log(list); 447 + let list = JSON.parse(localStorage.getItem("testlist")) || [];
437 448
438 let ids = this.topicList.map((item) => { 449 let ids = this.topicList.map((item) => {
439 - return item.id; 450 + return item;
440 }); 451 });
  452 + console.log(list, "-=-11-=");
  453 +
441 this.questions = this.removeDuplicates([...ids, ...list]); 454 this.questions = this.removeDuplicates([...ids, ...list]);
442 localStorage.setItem("testlist", JSON.stringify(this.questions)); 455 localStorage.setItem("testlist", JSON.stringify(this.questions));
443 }, 456 },
@@ -445,7 +458,7 @@ export default { @@ -445,7 +458,7 @@ export default {
445 handleDelTest() { 458 handleDelTest() {
446 let list = this.removeMatchingValues( 459 let list = this.removeMatchingValues(
447 this.topicList.map((item) => item.id), 460 this.topicList.map((item) => item.id),
448 - this.questions 461 + this.questions.map((item) => item.id)
449 ); 462 );
450 this.questions = list; 463 this.questions = list;
451 localStorage.setItem("testlist", JSON.stringify(this.questions)); 464 localStorage.setItem("testlist", JSON.stringify(this.questions));
@@ -457,7 +470,14 @@ export default { @@ -457,7 +470,14 @@ export default {
457 }, 470 },
458 // 去重 471 // 去重
459 removeDuplicates(arr) { 472 removeDuplicates(arr) {
460 - return [...new Set(arr)]; 473 + const seen = new Set();
  474 + return arr.filter((item) => {
  475 + if (!seen.has(item.id)) {
  476 + seen.add(item.id);
  477 + return true;
  478 + }
  479 + return false;
  480 + });
461 }, 481 },
462 // 去除第二数组中存在第一数组中的值 482 // 去除第二数组中存在第一数组中的值
463 removeMatchingValues(arr1, arr2) { 483 removeMatchingValues(arr1, arr2) {
@@ -542,6 +562,30 @@ export default { @@ -542,6 +562,30 @@ export default {
542 // 如果没有匹配,直接返回原字符串 562 // 如果没有匹配,直接返回原字符串
543 return input; 563 return input;
544 }, 564 },
  565 +
  566 + // 获取ref
  567 + onIFrameLoad(index) {
  568 + const iframeRef = this.$refs["iframe" + index][0]; // 获取对应的 iframe
  569 + const doc = iframeRef.contentDocument || iframeRef.contentWindow.document;
  570 + const body = iframeRef.contentWindow.document.body;
  571 + const height = body.scrollHeight; // 获取内容的高度
  572 + iframeRef.style.height = `${height}px`; // 设置 iframe 的高度
  573 + // 获取第一个P标签
  574 + const firstP = doc.getElementsByTagName("p")[0];
  575 + // 或者修改第一个 < p > 标签的内容;
  576 + if (firstP) {
  577 + console.log(firstP, firstP.innerHTML, "-----innerHTML-----");
  578 + let a = this.processString(firstP.innerHTML);
  579 + firstP.innerHTML = a;
  580 + }
  581 + },
  582 +
  583 + // 子集删除后 父级需要更新
  584 + setQuestions() {
  585 + console.log("我被调用");
  586 +
  587 + this.questions = JSON.parse(localStorage.getItem("testlist")) || [];
  588 + },
545 }, 589 },
546 }; 590 };
547 </script> 591 </script>
@@ -604,10 +648,8 @@ export default { @@ -604,10 +648,8 @@ export default {
604 border-radius: 5px 5px 0 0; 648 border-radius: 5px 5px 0 0;
605 } 649 }
606 .topic-info { 650 .topic-info {
607 - height: 100% !important;  
608 - // flex: 1;  
609 - // overflow-y: auto;  
610 - padding: 10px 20px; 651 + overflow-y: auto;
  652 + max-height: 300px;
611 } 653 }
612 .topic-bottom { 654 .topic-bottom {
613 height: 45px; 655 height: 45px;
vue.config.js
1 const path = require("path"); 1 const path = require("path");
2 const resolve = (dir) => path.join(__dirname, dir); 2 const resolve = (dir) => path.join(__dirname, dir);
3 const IS_PROD = ["production", "prod"].includes(process.env.NODE_ENV); 3 const IS_PROD = ["production", "prod"].includes(process.env.NODE_ENV);
4 -const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin; 4 +const BundleAnalyzerPlugin =
  5 + require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
5 6
6 const TimeStamp = new Date().getTime(); 7 const TimeStamp = new Date().getTime();
7 8
@@ -26,13 +27,13 @@ module.exports = { @@ -26,13 +27,13 @@ module.exports = {
26 // target:"http://ezquiz.sunvotecloud.cn", 27 // target:"http://ezquiz.sunvotecloud.cn",
27 // target:"http://192.168.1.151:8089", 28 // target:"http://192.168.1.151:8089",
28 // target: "https://ezquiz.sunvotecloud.cn", 29 // target: "https://ezquiz.sunvotecloud.cn",
29 - // target: "http://121.40.127.171:8090",  
30 - target:"http://127.0.0.1:8089", 30 + target: "http://121.40.127.171:8090",
  31 + // target:"http://127.0.0.1:8089",
31 changeOrigin: true, 32 changeOrigin: true,
32 ws: true, 33 ws: true,
33 }, 34 },
34 }, 35 },
35 - disableHostCheck: true 36 + disableHostCheck: true,
36 }, 37 },
37 // configureWebpack: { 38 // configureWebpack: {
38 // devtool: 'source-map', 39 // devtool: 'source-map',
@@ -51,8 +52,8 @@ module.exports = { @@ -51,8 +52,8 @@ module.exports = {
51 // } 52 // }
52 }, 53 },
53 chainWebpack: (config) => { 54 chainWebpack: (config) => {
54 - config.plugin('html').tap(args => {  
55 - args[0].title = '云平台'; 55 + config.plugin("html").tap((args) => {
  56 + args[0].title = "云平台";
56 return args; 57 return args;
57 }); 58 });
58 config.resolve.alias 59 config.resolve.alias
@@ -108,4 +109,3 @@ module.exports = { @@ -108,4 +109,3 @@ module.exports = {
108 }); 109 });
109 }, 110 },
110 }; 111 };
111 -