fix: 添加难度选项 #2
This commit is contained in:
@@ -50,19 +50,20 @@ public class AdminController {
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
|
||||
// API: 获取题目列表(支持全部、按类型、按分值、按标题模糊查询、联合查询)
|
||||
// API: 获取题目列表(支持全部、按类型、按分值、按标题模糊查询、联合查询、按难度查询)
|
||||
@GetMapping("/admin/api/questions")
|
||||
@ResponseBody
|
||||
public ResponseEntity<Map<String, Object>> getQuestions(
|
||||
@RequestParam(required = false) Integer type,
|
||||
@RequestParam(required = false) Integer score,
|
||||
@RequestParam(required = false) Integer level,
|
||||
@RequestParam(required = false) String title) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
List<question> questions;
|
||||
|
||||
if (type != null || title != null) {
|
||||
// 联合查询:按类型和/或标题
|
||||
questions = adminService.getQuestionsByTypeAndTitle(type, title);
|
||||
if (type != null || level != null || title != null) {
|
||||
// 联合查询:按类型、难度和/或标题
|
||||
questions = adminService.getQuestionsByConditions(type, level, title);
|
||||
} else if (score != null) {
|
||||
questions = adminService.getQuestionsByScore(score);
|
||||
} else {
|
||||
@@ -83,10 +84,11 @@ public class AdminController {
|
||||
@RequestParam(required = false) String answer_c,
|
||||
@RequestParam(required = false) String answer_d,
|
||||
@RequestParam String r_answer,
|
||||
@RequestParam Integer level,
|
||||
@RequestParam Integer score) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
try {
|
||||
adminService.addChoiceQuestion(title, type, answer_a, answer_b, answer_c, answer_d, r_answer, score);
|
||||
adminService.addChoiceQuestion(title, type, answer_a, answer_b, answer_c, answer_d, r_answer, level, score);
|
||||
result.put("success", true);
|
||||
result.put("message", "添加成功");
|
||||
} catch (Exception e) {
|
||||
@@ -100,12 +102,12 @@ public class AdminController {
|
||||
@ResponseBody
|
||||
public ResponseEntity<Map<String, Object>> addEssayQuestion(@RequestParam String title,
|
||||
@RequestParam Integer type,
|
||||
@RequestParam(required = false) String answer,
|
||||
@RequestParam(required = false) String r_answer,
|
||||
@RequestParam Integer level,
|
||||
@RequestParam Integer score) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
try {
|
||||
adminService.addEssayQuestion(title, type, answer, r_answer, score);
|
||||
adminService.addEssayQuestion(title, type, r_answer, level, score);
|
||||
result.put("success", true);
|
||||
result.put("message", "添加成功");
|
||||
} catch (Exception e) {
|
||||
@@ -137,8 +139,9 @@ public class AdminController {
|
||||
|
||||
@PostMapping("/admin/mkexam/create")
|
||||
@ResponseBody
|
||||
public examPaper createExam(@RequestParam(required = false) String title) {
|
||||
return makeExamService.createExamPaper(title);
|
||||
public examPaper createExam(@RequestParam(required = false) String title,
|
||||
@RequestParam(required = false, defaultValue = "1") Integer level) {
|
||||
return makeExamService.createExamPaper(title, level);
|
||||
}
|
||||
|
||||
@GetMapping("/admin/mkexam/detail/{uid}")
|
||||
@@ -191,6 +194,7 @@ public class AdminController {
|
||||
@RequestParam(required = false) String answerC,
|
||||
@RequestParam(required = false) String answerD,
|
||||
@RequestParam(required = false) String rAnswer,
|
||||
@RequestParam Integer level,
|
||||
@RequestParam Integer score) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
try {
|
||||
@@ -205,6 +209,7 @@ public class AdminController {
|
||||
.id(id)
|
||||
.type(type)
|
||||
.title(title)
|
||||
.level(level)
|
||||
.score(score)
|
||||
.rAnswer(rAnswer);
|
||||
|
||||
|
||||
@@ -12,12 +12,12 @@ public interface adminService extends IService<question> {
|
||||
* 添加选择题(type=1)
|
||||
*/
|
||||
boolean addChoiceQuestion(String title, Integer type, String answer_a, String answer_b,
|
||||
String answer_c, String answer_d, String r_answer, Integer score);
|
||||
String answer_c, String answer_d, String r_answer, Integer level, Integer score);
|
||||
|
||||
/**
|
||||
* 添加简答题(type=2)
|
||||
*/
|
||||
boolean addEssayQuestion(String title, Integer type, String answer, String r_answer, Integer score);
|
||||
boolean addEssayQuestion(String title, Integer type, String r_answer, Integer level, Integer score);
|
||||
|
||||
/**
|
||||
* 根据类型查询所有题目
|
||||
@@ -32,6 +32,15 @@ public interface adminService extends IService<question> {
|
||||
*/
|
||||
List<question> getQuestionsByTypeAndTitle(Integer type, String title);
|
||||
|
||||
/**
|
||||
* 根据条件查询题目(类型、难度、标题联合查询)
|
||||
* @param type 题目类型,可为null
|
||||
* @param level 题目难度,可为null
|
||||
* @param title 标题关键词,可为null,支持模糊匹配
|
||||
* @return 符合条件的题目列表
|
||||
*/
|
||||
List<question> getQuestionsByConditions(Integer type, Integer level, String title);
|
||||
|
||||
/**
|
||||
* 根据分数查询所有题目
|
||||
*/
|
||||
|
||||
@@ -16,7 +16,7 @@ public class adminServiceImpl extends ServiceImpl<questionMapper, question> impl
|
||||
|
||||
@Override
|
||||
public boolean addChoiceQuestion(String title, Integer type, String answer_a, String answer_b,
|
||||
String answer_c, String answer_d, String r_answer, Integer score) {
|
||||
String answer_c, String answer_d, String r_answer, Integer level, Integer score) {
|
||||
question q = question.builder()
|
||||
.title(title)
|
||||
.type(type)
|
||||
@@ -25,17 +25,19 @@ public class adminServiceImpl extends ServiceImpl<questionMapper, question> impl
|
||||
.answerC(answer_c)
|
||||
.answerD(answer_d)
|
||||
.rAnswer(r_answer)
|
||||
.level(level)
|
||||
.score(score)
|
||||
.build();
|
||||
return save(q);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addEssayQuestion(String title, Integer type, String answer, String r_answer, Integer score) {
|
||||
public boolean addEssayQuestion(String title, Integer type, String r_answer, Integer level, Integer score) {
|
||||
question q = question.builder()
|
||||
.title(title)
|
||||
.type(type)
|
||||
.rAnswer(r_answer)
|
||||
.level(level)
|
||||
.score(score)
|
||||
.build();
|
||||
return save(q);
|
||||
@@ -62,6 +64,23 @@ public class adminServiceImpl extends ServiceImpl<questionMapper, question> impl
|
||||
return list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<question> getQuestionsByConditions(Integer type, Integer level, String title) {
|
||||
LambdaQueryWrapper<question> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
if (type != null) {
|
||||
wrapper.eq(question::getType, type);
|
||||
}
|
||||
if (level != null) {
|
||||
wrapper.eq(question::getLevel, level);
|
||||
}
|
||||
if (title != null && !title.trim().isEmpty()) {
|
||||
wrapper.like(question::getTitle, title.trim());
|
||||
}
|
||||
|
||||
return list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<question> getQuestionsByScore(Integer score) {
|
||||
LambdaQueryWrapper<question> wrapper = new LambdaQueryWrapper<>();
|
||||
@@ -95,10 +114,12 @@ public class adminServiceImpl extends ServiceImpl<questionMapper, question> impl
|
||||
String title = (String) qData.get("title");
|
||||
Integer type = ((Number) qData.get("type")).intValue();
|
||||
Integer score = ((Number) qData.get("score")).intValue();
|
||||
Integer level = qData.get("level") != null ? ((Number) qData.get("level")).intValue() : 1;
|
||||
|
||||
question.questionBuilder builder = question.builder()
|
||||
.title(title)
|
||||
.type(type)
|
||||
.level(level)
|
||||
.score(score);
|
||||
|
||||
if (type == 1) {
|
||||
|
||||
@@ -30,12 +30,20 @@ public class makeExamServiceImpl extends ServiceImpl<examPaperMapper, examPaper>
|
||||
|
||||
@Override
|
||||
public examPaper createExamPaper(String title) {
|
||||
return createExamPaper(title, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public examPaper createExamPaper(String title, Integer level) {
|
||||
// 生成唯一UID
|
||||
String uid = UidGenerator.generateUniqueUid(this::isUidExists);
|
||||
|
||||
// 获取所有题目
|
||||
List<question> allChoiceQuestions = getQuestionsByType(1);
|
||||
List<question> allEssayQuestions = getQuestionsByType(2);
|
||||
// 根据难度获取题目
|
||||
// level=1: 只选level=1的题目
|
||||
// level=2: 选level=1或2的题目
|
||||
// level=3: 选level=1、2、3的题目
|
||||
List<question> allChoiceQuestions = getQuestionsByTypeAndLevel(1, level);
|
||||
List<question> allEssayQuestions = getQuestionsByTypeAndLevel(2, level);
|
||||
|
||||
if (allChoiceQuestions.size() < 5 || allEssayQuestions.size() < 5) {
|
||||
throw new RuntimeException("题库题目不足(至少需要5道选择题和5道简答题)");
|
||||
@@ -75,6 +83,7 @@ public class makeExamServiceImpl extends ServiceImpl<examPaperMapper, examPaper>
|
||||
examPaper paper = examPaper.builder()
|
||||
.uid(uid)
|
||||
.title(title)
|
||||
.level(level)
|
||||
.select(selectIds)
|
||||
.content(contentIds)
|
||||
.build();
|
||||
@@ -103,6 +112,16 @@ public class makeExamServiceImpl extends ServiceImpl<examPaperMapper, examPaper>
|
||||
return questionMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
private List<question> getQuestionsByTypeAndLevel(Integer type, Integer level) {
|
||||
LambdaQueryWrapper<question> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(question::getType, type);
|
||||
// level=1: 只选level<=1的题目
|
||||
// level=2: 选level<=2的题目
|
||||
// level=3: 选level<=3的题目
|
||||
wrapper.le(question::getLevel, level);
|
||||
return questionMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 寻找精确100分的组合
|
||||
*/
|
||||
@@ -289,6 +308,7 @@ public class makeExamServiceImpl extends ServiceImpl<examPaperMapper, examPaper>
|
||||
|
||||
result.put("uid", paper.getUid());
|
||||
result.put("title", paper.getTitle());
|
||||
result.put("level", paper.getLevel());
|
||||
|
||||
// 解析选择题ID列表
|
||||
List<Integer> choiceIds = parseIdList(paper.getSelect());
|
||||
|
||||
@@ -11,6 +11,13 @@ public interface makeExamService {
|
||||
/**
|
||||
* 创建试卷
|
||||
* 自动生成uid,按7:3比例选择题和简答题,总分100分
|
||||
* @param title 试卷标题
|
||||
* @param level 试卷难度(1=低级只选1级题,2=中级选1-2级题,3=高级选1-3级题)
|
||||
*/
|
||||
examPaper createExamPaper(String title, Integer level);
|
||||
|
||||
/**
|
||||
* 创建试卷(默认难度1)
|
||||
*/
|
||||
examPaper createExamPaper(String title);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ public class examPaper {
|
||||
private Integer id;
|
||||
private String uid;
|
||||
private String title;
|
||||
private Integer level;
|
||||
|
||||
@TableField("`select`")
|
||||
private String select;
|
||||
|
||||
@@ -15,6 +15,7 @@ public class question {
|
||||
private Integer id;
|
||||
private String title;
|
||||
private Integer type;
|
||||
private Integer level;
|
||||
private String answerA;
|
||||
private String answerB;
|
||||
private String answerC;
|
||||
|
||||
@@ -146,6 +146,18 @@
|
||||
background: #fef9c3;
|
||||
color: #a16207;
|
||||
}
|
||||
.tag-level-1 {
|
||||
background: #dcfce7;
|
||||
color: #15803d;
|
||||
}
|
||||
.tag-level-2 {
|
||||
background: #dbeafe;
|
||||
color: #1d4ed8;
|
||||
}
|
||||
.tag-level-3 {
|
||||
background: #fce7f3;
|
||||
color: #be185d;
|
||||
}
|
||||
.score-badge {
|
||||
background: #f59e0b;
|
||||
color: white;
|
||||
@@ -247,9 +259,19 @@
|
||||
<option value="D">D</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>分值</label>
|
||||
<input type="number" name="score" id="choiceScore" placeholder="请输入分值" required min="0">
|
||||
<div class="grid-2">
|
||||
<div class="form-group">
|
||||
<label>难度<span class="required">*</span></label>
|
||||
<select name="level" id="choiceLevel" required>
|
||||
<option value="1">低级</option>
|
||||
<option value="2">中级</option>
|
||||
<option value="3">高级</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>分值</label>
|
||||
<input type="number" name="score" id="choiceScore" placeholder="请输入分值" required min="0">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">添加选择题</button>
|
||||
</form>
|
||||
@@ -267,9 +289,19 @@
|
||||
<label>参考答案</label>
|
||||
<textarea name="r_answer" id="essayRAnswer" placeholder="选填"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>分值</label>
|
||||
<input type="number" name="score" id="essayScore" placeholder="请输入分值" required min="0">
|
||||
<div class="grid-2">
|
||||
<div class="form-group">
|
||||
<label>难度<span class="required">*</span></label>
|
||||
<select name="level" id="essayLevel" required>
|
||||
<option value="1">低级</option>
|
||||
<option value="2">中级</option>
|
||||
<option value="3">高级</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>分值</label>
|
||||
<input type="number" name="score" id="essayScore" placeholder="请输入分值" required min="0">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">添加简答题</button>
|
||||
</form>
|
||||
@@ -290,6 +322,15 @@
|
||||
<option value="2">简答题</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>难度</label>
|
||||
<select id="queryLevel">
|
||||
<option value="">全部难度</option>
|
||||
<option value="1">低级</option>
|
||||
<option value="2">中级</option>
|
||||
<option value="3">高级</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
@@ -403,6 +444,7 @@
|
||||
document.getElementById('queryScoreTag').style.display = 'none';
|
||||
document.getElementById('queryTitleTag').style.display = 'none';
|
||||
document.getElementById('queryType').value = '';
|
||||
document.getElementById('queryLevel').value = '';
|
||||
document.getElementById('queryTitle').value = '';
|
||||
fetchQuestions('/admin/api/questions');
|
||||
}
|
||||
@@ -410,10 +452,11 @@
|
||||
// 联合查询:按类型和/或标题
|
||||
function queryQuestions() {
|
||||
const type = document.getElementById('queryType').value;
|
||||
const level = document.getElementById('queryLevel').value;
|
||||
const title = document.getElementById('queryTitle').value.trim();
|
||||
|
||||
// 如果类型和标题都为空,查询全部
|
||||
if (!type && !title) {
|
||||
// 如果类型、难度和标题都为空,查询全部
|
||||
if (!type && !level && !title) {
|
||||
loadAllQuestions();
|
||||
return;
|
||||
}
|
||||
@@ -440,6 +483,7 @@
|
||||
let url = '/admin/api/questions?';
|
||||
const params = [];
|
||||
if (type) params.push('type=' + encodeURIComponent(type));
|
||||
if (level) params.push('level=' + encodeURIComponent(level));
|
||||
if (title) params.push('title=' + encodeURIComponent(title));
|
||||
url += params.join('&');
|
||||
|
||||
@@ -502,7 +546,8 @@
|
||||
<tr>
|
||||
<th width="60">ID</th>
|
||||
<th>题目</th>
|
||||
<th width="100">类型</th>
|
||||
<th width="80">类型</th>
|
||||
<th width="80">难度</th>
|
||||
<th>正确答案</th>
|
||||
<th width="80">分值</th>
|
||||
<th width="150">操作</th>
|
||||
@@ -516,6 +561,10 @@
|
||||
'<span class="tag tag-choice">选择题</span>' :
|
||||
'<span class="tag tag-essay">简答题</span>';
|
||||
|
||||
const levelMap = {1: '低级', 2: '中级', 3: '高级'};
|
||||
const levelClassMap = {1: 'tag-level-1', 2: 'tag-level-2', 3: 'tag-level-3'};
|
||||
const levelTag = `<span class="tag ${levelClassMap[q.level] || 'tag-level-1'}">${levelMap[q.level] || '低级'}</span>`;
|
||||
|
||||
let optionsHtml = '';
|
||||
if (q.type == 1) {
|
||||
const parts = [];
|
||||
@@ -537,6 +586,7 @@
|
||||
<td>${q.id}</td>
|
||||
<td style="max-width: 300px; word-wrap: break-word;">${escapeHtml(q.title)}</td>
|
||||
<td>${typeTag}</td>
|
||||
<td>${levelTag}</td>
|
||||
<td>${optionsHtml}</td>
|
||||
<td>${rAnswerHtml}</td>
|
||||
<td><span class="score-badge">${q.score}</span></td>
|
||||
@@ -689,6 +739,11 @@
|
||||
resultDiv.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
if (q.level && (q.level < 1 || q.level > 3)) {
|
||||
resultDiv.innerHTML = '<div class="alert" style="background: #fee; color: #c33;">第 ' + (i + 1) + ' 题难度错误,level必须是1(低级)、2(中级)或3(高级)</div>';
|
||||
resultDiv.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
if (!q.score || q.score < 0) {
|
||||
resultDiv.innerHTML = '<div class="alert" style="background: #fee; color: #c33;">第 ' + (i + 1) + ' 题分值错误</div>';
|
||||
resultDiv.style.display = 'block';
|
||||
@@ -753,6 +808,7 @@
|
||||
document.getElementById('editType').value = q.type;
|
||||
document.getElementById('editTitle').value = q.title || '';
|
||||
document.getElementById('editScore').value = q.score || '';
|
||||
document.getElementById('editLevel').value = q.level || 1;
|
||||
|
||||
if (q.type == 1) {
|
||||
// 选择题表单
|
||||
@@ -791,6 +847,7 @@
|
||||
formData.append('type', currentEditType);
|
||||
formData.append('title', document.getElementById('editTitle').value);
|
||||
formData.append('score', document.getElementById('editScore').value);
|
||||
formData.append('level', document.getElementById('editLevel').value);
|
||||
|
||||
if (currentEditType == 1) {
|
||||
formData.append('answerA', document.getElementById('editAnswerA').value);
|
||||
@@ -831,11 +888,16 @@
|
||||
const title = formData.get('title');
|
||||
const score = formData.get('score');
|
||||
const rAnswer = formData.get('rAnswer');
|
||||
const level = formData.get('level') || 1;
|
||||
|
||||
const typeTag = type == 1 ?
|
||||
'<span class="tag tag-choice">选择题</span>' :
|
||||
'<span class="tag tag-essay">简答题</span>';
|
||||
|
||||
const levelMap = {1: '低级', 2: '中级', 3: '高级'};
|
||||
const levelClassMap = {1: 'tag-level-1', 2: 'tag-level-2', 3: 'tag-level-3'};
|
||||
const levelTag = `<span class="tag ${levelClassMap[level] || 'tag-level-1'}">${levelMap[level] || '低级'}</span>`;
|
||||
|
||||
let optionsHtml = '';
|
||||
if (type == 1) {
|
||||
const answerA = formData.get('answerA');
|
||||
@@ -861,6 +923,7 @@
|
||||
<td>${id}</td>
|
||||
<td style="max-width: 300px; word-wrap: break-word;">${escapeHtml(title)}</td>
|
||||
<td>${typeTag}</td>
|
||||
<td>${levelTag}</td>
|
||||
<td>${optionsHtml}</td>
|
||||
<td>${rAnswerHtml}</td>
|
||||
<td><span class="score-badge">${score}</span></td>
|
||||
@@ -885,7 +948,7 @@
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info" style="margin-bottom: 16px;">
|
||||
<strong>格式说明:</strong>JSON数组格式,每道题包含 title(必填), type(1=选择题,2=简答题), score(分值) 等字段
|
||||
<strong>格式说明:</strong>JSON数组格式,每道题包含 title(必填), type(1=选择题,2=简答题), level(1=低级,2=中级,3=高级), score(分值) 等字段
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@@ -894,6 +957,7 @@
|
||||
{
|
||||
"title": "选择题示例",
|
||||
"type": 1,
|
||||
"level": 1,
|
||||
"answerA": "选项A",
|
||||
"answerB": "选项B",
|
||||
"answerC": "选项C",
|
||||
@@ -904,6 +968,7 @@
|
||||
{
|
||||
"title": "简答题示例",
|
||||
"type": 2,
|
||||
"level": 2,
|
||||
"rAnswer": "参考答案",
|
||||
"score": 10
|
||||
}
|
||||
@@ -979,9 +1044,19 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>分值 <span class="required">*</span></label>
|
||||
<input type="number" id="editScore" required min="0">
|
||||
<div class="grid-2">
|
||||
<div class="form-group">
|
||||
<label>难度 <span class="required">*</span></label>
|
||||
<select id="editLevel" required>
|
||||
<option value="1">低级</option>
|
||||
<option value="2">中级</option>
|
||||
<option value="3">高级</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>分值 <span class="required">*</span></label>
|
||||
<input type="number" id="editScore" required min="0">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
|
||||
@@ -220,6 +220,26 @@
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.tag-level-1 {
|
||||
background: #dcfce7;
|
||||
color: #15803d;
|
||||
}
|
||||
.tag-level-2 {
|
||||
background: #dbeafe;
|
||||
color: #1d4ed8;
|
||||
}
|
||||
.tag-level-1 {
|
||||
background: #dcfce7;
|
||||
color: #15803d;
|
||||
}
|
||||
.tag-level-2 {
|
||||
background: #dbeafe;
|
||||
color: #1d4ed8;
|
||||
}
|
||||
.tag-level-3 {
|
||||
background: #fce7f3;
|
||||
color: #be185d;
|
||||
}
|
||||
.action-btns {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
@@ -268,6 +288,8 @@
|
||||
<div class="exam-meta">
|
||||
<p><strong>试卷UID:</strong> <span id="modalUid" style="font-family: monospace;"></span></p>
|
||||
<p style="margin-top: 8px;">
|
||||
<strong>难度:</strong> <span id="modalLevel" style="font-weight: bold;">-</span>
|
||||
<span style="margin: 0 10px;">|</span>
|
||||
<strong>总分:</strong> <span id="modalTotalScore" style="font-size: 18px; font-weight: bold;">0</span> 分
|
||||
<span style="margin: 0 10px;">|</span>
|
||||
<strong>选择题:</strong> <span id="modalChoiceCount">0</span> 道
|
||||
@@ -331,6 +353,7 @@
|
||||
<th width="60">ID</th>
|
||||
<th width="280">UID</th>
|
||||
<th>试卷标题</th>
|
||||
<th width="100">难度</th>
|
||||
<th width="220">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -339,11 +362,17 @@
|
||||
|
||||
examPapers.forEach(paper => {
|
||||
const title = paper.title || '未命名试卷';
|
||||
const level = paper.level || 1;
|
||||
const levelMap = {1: '低级', 2: '中级', 3: '高级'};
|
||||
const levelClassMap = {1: 'tag-level-1', 2: 'tag-level-2', 3: 'tag-level-3'};
|
||||
const levelTag = `<span class="${levelClassMap[level] || 'tag-level-1'}">${levelMap[level] || '低级'}</span>`;
|
||||
|
||||
html += `
|
||||
<tr>
|
||||
<td>${paper.id}</td>
|
||||
<td style="font-family: monospace; color: #666; font-size: 13px;">${paper.uid}</td>
|
||||
<td>${escapeHtml(title)}</td>
|
||||
<td>${levelTag}</td>
|
||||
<td>
|
||||
<div class="action-btns">
|
||||
<button class="btn btn-primary btn-sm" onclick="viewExamDetail('${paper.uid}')">查看内容</button>
|
||||
@@ -409,8 +438,12 @@
|
||||
fetch('/admin/mkexam/detail/' + uid)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
const levelMap = {1: '低级', 2: '中级', 3: '高级'};
|
||||
const level = data.level || 1;
|
||||
|
||||
document.getElementById('modalTitle').textContent = data.title || '未命名试卷';
|
||||
document.getElementById('modalUid').textContent = data.uid;
|
||||
document.getElementById('modalLevel').textContent = levelMap[level] || '低级';
|
||||
document.getElementById('modalTotalScore').textContent = data.totalScore;
|
||||
document.getElementById('modalChoiceCount').textContent = data.choiceCount;
|
||||
document.getElementById('modalEssayCount').textContent = data.essayCount;
|
||||
|
||||
@@ -327,6 +327,14 @@
|
||||
<label for="examTitle">试卷标题</label>
|
||||
<input type="text" id="examTitle" placeholder="请输入试卷标题(可选)" maxlength="100">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="examLevel">试卷难度</label>
|
||||
<select id="examLevel" style="width: 100%; padding: 12px 16px; border: 2px solid #e0e0e0; border-radius: 8px; font-size: 14px;">
|
||||
<option value="1">低级(只选1级难度的题目)</option>
|
||||
<option value="2">中级(选1-2级难度的题目)</option>
|
||||
<option value="3">高级(选1-3级难度的题目)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button class="btn btn-primary create-btn" onclick="createExam()">创建试卷</button>
|
||||
</div>
|
||||
@@ -354,6 +362,9 @@
|
||||
<button class="copy-btn" onclick="copyUid()">复制</button>
|
||||
</div>
|
||||
|
||||
<h3 style="margin-top: 16px;">试卷难度</h3>
|
||||
<div class="exam-title" id="examLevelDisplay" style="background: #e8f5e9; border-left-color: #4caf50;">-</div>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">选择题数量</div>
|
||||
@@ -391,8 +402,9 @@
|
||||
|
||||
<script>
|
||||
function createExam() {
|
||||
// 获取试卷标题
|
||||
// 获取试卷标题和难度
|
||||
const title = document.getElementById('examTitle').value.trim();
|
||||
const level = document.getElementById('examLevel').value;
|
||||
|
||||
// 显示加载,隐藏创建按钮
|
||||
document.getElementById('createSection').style.display = 'none';
|
||||
@@ -405,6 +417,7 @@
|
||||
if (title) {
|
||||
formData.append('title', title);
|
||||
}
|
||||
formData.append('level', level);
|
||||
|
||||
fetch('/admin/mkexam/create', {
|
||||
method: 'POST',
|
||||
@@ -440,6 +453,11 @@
|
||||
// 显示UID
|
||||
document.getElementById('uidDisplay').textContent = data.uid;
|
||||
|
||||
// 显示难度
|
||||
const levelMap = {1: '低级', 2: '中级', 3: '高级'};
|
||||
const level = data.level || 1;
|
||||
document.getElementById('examLevelDisplay').textContent = levelMap[level] || '低级';
|
||||
|
||||
// 更新统计
|
||||
const choiceQuestions = data.choiceQuestions || [];
|
||||
const essayQuestions = data.essayQuestions || [];
|
||||
|
||||
122
test_import.json
122
test_import.json
@@ -2,6 +2,7 @@
|
||||
{
|
||||
"title": "Java中,以下哪个关键字用于定义接口?",
|
||||
"type": 1,
|
||||
"level": 1,
|
||||
"answerA": "class",
|
||||
"answerB": "interface",
|
||||
"answerC": "extends",
|
||||
@@ -9,9 +10,32 @@
|
||||
"rAnswer": "B",
|
||||
"score": 5
|
||||
},
|
||||
{
|
||||
"title": "以下哪个不是Java的基本数据类型?",
|
||||
"type": 1,
|
||||
"level": 1,
|
||||
"answerA": "int",
|
||||
"answerB": "String",
|
||||
"answerC": "boolean",
|
||||
"answerD": "double",
|
||||
"rAnswer": "B",
|
||||
"score": 5
|
||||
},
|
||||
{
|
||||
"title": "Java中,main方法的正确写法是?",
|
||||
"type": 1,
|
||||
"level": 1,
|
||||
"answerA": "public void main(String[] args)",
|
||||
"answerB": "public static void main(String[] args)",
|
||||
"answerC": "public static int main(String[] args)",
|
||||
"answerD": "static void main(String args)",
|
||||
"rAnswer": "B",
|
||||
"score": 5
|
||||
},
|
||||
{
|
||||
"title": "Spring框架的核心特性是什么?",
|
||||
"type": 1,
|
||||
"level": 2,
|
||||
"answerA": "AOP面向切面编程",
|
||||
"answerB": "IOC控制反转",
|
||||
"answerC": "MVC设计模式",
|
||||
@@ -19,26 +43,104 @@
|
||||
"rAnswer": "D",
|
||||
"score": 5
|
||||
},
|
||||
{
|
||||
"title": "MySQL中,以下哪个关键字用于删除表?",
|
||||
"type": 1,
|
||||
"level": 2,
|
||||
"answerA": "DELETE",
|
||||
"answerB": "DROP",
|
||||
"answerC": "REMOVE",
|
||||
"answerD": "CLEAR",
|
||||
"rAnswer": "B",
|
||||
"score": 5
|
||||
},
|
||||
{
|
||||
"title": "以下哪个设计模式属于创建型模式?",
|
||||
"type": 1,
|
||||
"level": 2,
|
||||
"answerA": "观察者模式",
|
||||
"answerB": "单例模式",
|
||||
"answerC": "策略模式",
|
||||
"answerD": "装饰器模式",
|
||||
"rAnswer": "B",
|
||||
"score": 5
|
||||
},
|
||||
{
|
||||
"title": "Redis支持的数据结构包括?",
|
||||
"type": 1,
|
||||
"level": 3,
|
||||
"answerA": "String",
|
||||
"answerB": "List",
|
||||
"answerC": "Set",
|
||||
"answerD": "以上都是",
|
||||
"rAnswer": "D",
|
||||
"score": 5
|
||||
},
|
||||
{
|
||||
"title": "Java虚拟机(JVM)的主要组成部分包括?",
|
||||
"type": 1,
|
||||
"level": 3,
|
||||
"answerA": "类加载器",
|
||||
"answerB": "运行时数据区",
|
||||
"answerC": "执行引擎",
|
||||
"answerD": "以上都是",
|
||||
"rAnswer": "D",
|
||||
"score": 5
|
||||
},
|
||||
{
|
||||
"title": "简述Java中Exception和Error的区别。",
|
||||
"type": 2,
|
||||
"rAnswer": "Exception是程序可以处理的异常,通常由程序逻辑错误引起;Error是严重错误,如内存溢出,程序无法处理。",
|
||||
"level": 1,
|
||||
"rAnswer": "Exception是程序可以处理的异常,通常由程序逻辑错误引起,如NullPointerException;Error是严重错误,如OutOfMemoryError,程序通常无法处理。",
|
||||
"score": 10
|
||||
},
|
||||
{
|
||||
"title": "什么是面向对象编程的三大特性?",
|
||||
"type": 2,
|
||||
"level": 1,
|
||||
"rAnswer": "封装:将数据和方法包装在一起;继承:子类继承父类的特性;多态:同一操作作用于不同对象产生不同行为。",
|
||||
"score": 10
|
||||
},
|
||||
{
|
||||
"title": "简述JDK、JRE和JVM的区别。",
|
||||
"type": 2,
|
||||
"level": 2,
|
||||
"rAnswer": "JDK是Java开发工具包,包含JRE和开发工具;JRE是Java运行时环境,包含JVM和核心类库;JVM是Java虚拟机,负责字节码的执行。",
|
||||
"score": 10
|
||||
},
|
||||
{
|
||||
"title": "MySQL中,如何优化慢查询?",
|
||||
"type": 2,
|
||||
"rAnswer": "1. 添加索引;2. 优化SQL语句;3. 使用EXPLAIN分析查询计划;4. 避免全表扫描;5. 适当分表分库。",
|
||||
"level": 2,
|
||||
"rAnswer": "1. 添加合适的索引;2. 优化SQL语句,避免全表扫描;3. 使用EXPLAIN分析查询计划;4. 优化表结构;5. 适当进行分表分库。",
|
||||
"score": 15
|
||||
},
|
||||
{
|
||||
"title": "以下哪个不是Java的基本数据类型?",
|
||||
"type": 1,
|
||||
"answerA": "int",
|
||||
"answerB": "String",
|
||||
"answerC": "boolean",
|
||||
"answerD": "double",
|
||||
"rAnswer": "B",
|
||||
"score": 5
|
||||
"title": "解释Spring中的IOC和AOP概念。",
|
||||
"type": 2,
|
||||
"level": 2,
|
||||
"rAnswer": "IOC(控制反转)是将对象的创建和管理交给Spring容器,降低耦合度;AOP(面向切面编程)是将横切关注点(如日志、事务)从业务逻辑中分离,提高代码复用性。",
|
||||
"score": 15
|
||||
},
|
||||
{
|
||||
"title": "什么是线程安全问题?如何解决?",
|
||||
"type": 2,
|
||||
"level": 3,
|
||||
"rAnswer": "线程安全是指多个线程同时访问共享资源时可能出现数据不一致的问题。解决方法包括:1. synchronized关键字;2. ReentrantLock锁;3. 原子类;4. ThreadLocal;5. 使用线程安全的集合类。",
|
||||
"score": 15
|
||||
},
|
||||
{
|
||||
"title": "简述分布式事务的解决方案。",
|
||||
"type": 2,
|
||||
"level": 3,
|
||||
"rAnswer": "1. 两阶段提交(2PC):准备阶段和提交阶段;2. 三阶段提交(3PC):增加了预提交阶段;3. TCC:Try-Confirm-Cancel;4. 本地消息表;5. 最大努力通知;6. Saga模式。",
|
||||
"score": 15
|
||||
},
|
||||
{
|
||||
"title": "什么是JVM的垃圾回收机制?",
|
||||
"type": 2,
|
||||
"level": 3,
|
||||
"rAnswer": "垃圾回收是JVM自动管理内存的机制,主要回收堆内存中不再使用的对象。常用算法包括:标记-清除、标记-整理、复制算法、分代收集算法。垃圾收集器有Serial、Parallel、CMS、G1等。",
|
||||
"score": 15
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user