Merge pull request '随机试卷支持难度与知识点筛选' (#3) from menft into master
Reviewed-on: http://114.55.243.137:6688/dianliang/ai-course/pulls/3
This commit is contained in:
commit
3eacc73c8d
@ -39,6 +39,8 @@ import xyz.playedu.common.util.StringUtil;
|
|||||||
import xyz.playedu.exam.constants.ExamConstant;
|
import xyz.playedu.exam.constants.ExamConstant;
|
||||||
import xyz.playedu.exam.domain.*;
|
import xyz.playedu.exam.domain.*;
|
||||||
import xyz.playedu.exam.service.*;
|
import xyz.playedu.exam.service.*;
|
||||||
|
import xyz.playedu.jc.domain.Knowledge;
|
||||||
|
import xyz.playedu.jc.service.IKnowledgeService;
|
||||||
import xyz.playedu.resource.service.ResourceService;
|
import xyz.playedu.resource.service.ResourceService;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@ -62,6 +64,8 @@ public class QuestionController {
|
|||||||
|
|
||||||
@Autowired private ResourceService resourceService;
|
@Autowired private ResourceService resourceService;
|
||||||
|
|
||||||
|
@Autowired private IKnowledgeService knowledgeService;
|
||||||
|
|
||||||
@GetMapping("/index")
|
@GetMapping("/index")
|
||||||
@BackendPermission(slug = BPermissionConstant.EXAM_QUESTION)
|
@BackendPermission(slug = BPermissionConstant.EXAM_QUESTION)
|
||||||
@Log(title = "试题-列表", businessType = BusinessTypeConstant.GET)
|
@Log(title = "试题-列表", businessType = BusinessTypeConstant.GET)
|
||||||
@ -1178,4 +1182,54 @@ public class QuestionController {
|
|||||||
.size());
|
.size());
|
||||||
examQuestionCategoryService.updateById(examQuestionCategory);
|
examQuestionCategoryService.updateById(examQuestionCategory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据题库ID、题型、难度获取关联的知识点列表(去重)
|
||||||
|
* 用于随机试卷配置时的知识点筛选
|
||||||
|
* 返回格式: [{code: "MATH001", name: "导数与微分"}, ...]
|
||||||
|
*
|
||||||
|
* @author menft
|
||||||
|
*/
|
||||||
|
@GetMapping("/knowledge-codes")
|
||||||
|
@BackendPermission(slug = BPermissionConstant.EXAM_QUESTION)
|
||||||
|
public JsonResponse getKnowledgeCodes(@RequestParam HashMap<String, Object> params) {
|
||||||
|
String categoryIds = MapUtils.getString(params, "category_ids");
|
||||||
|
Integer type = MapUtils.getInteger(params, "type");
|
||||||
|
Integer level = MapUtils.getInteger(params, "level");
|
||||||
|
|
||||||
|
if (StringUtil.isEmpty(categoryIds)) {
|
||||||
|
return JsonResponse.data(List.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取去重的知识点编码
|
||||||
|
List<String> knowledgeCodes =
|
||||||
|
examQuestionService.getDistinctKnowledgeCodes(categoryIds, type, level);
|
||||||
|
if (knowledgeCodes.isEmpty()) {
|
||||||
|
return JsonResponse.data(List.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询知识点名称
|
||||||
|
List<Knowledge> knowledgeList = knowledgeService.getByKnowledgeCodes(knowledgeCodes);
|
||||||
|
Map<String, String> codeToName =
|
||||||
|
knowledgeList.stream()
|
||||||
|
.collect(
|
||||||
|
Collectors.toMap(
|
||||||
|
Knowledge::getKnowledgeCode,
|
||||||
|
Knowledge::getName,
|
||||||
|
(v1, v2) -> v1));
|
||||||
|
|
||||||
|
// 构建返回结果
|
||||||
|
List<Map<String, String>> result =
|
||||||
|
knowledgeCodes.stream()
|
||||||
|
.map(
|
||||||
|
code -> {
|
||||||
|
Map<String, String> item = new HashMap<>();
|
||||||
|
item.put("code", code);
|
||||||
|
item.put("name", codeToName.getOrDefault(code, code));
|
||||||
|
return item;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return JsonResponse.data(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -961,41 +961,53 @@ public class PaperController {
|
|||||||
JSONObject source = randomRules.getJSONObject("source");
|
JSONObject source = randomRules.getJSONObject("source");
|
||||||
List<Integer> categoryIds = source.getBeanList("category_ids", Integer.class);
|
List<Integer> categoryIds = source.getBeanList("category_ids", Integer.class);
|
||||||
filter.setCategoryIds(categoryIds);
|
filter.setCategoryIds(categoryIds);
|
||||||
// 题型、数量
|
// 题型、数量、难度、知识点
|
||||||
JSONObject score = randomRules.getJSONObject("score");
|
JSONObject score = randomRules.getJSONObject("score");
|
||||||
JSONObject type1 = score.getJSONObject(ExamConstant.TYPE_1 + "");
|
JSONObject type1 = score.getJSONObject(ExamConstant.TYPE_1 + "");
|
||||||
if (StringUtil.isNotNull(type1)) {
|
if (StringUtil.isNotNull(type1)) {
|
||||||
filter.setType1Number(type1.getInt("number"));
|
filter.setType1Number(type1.getInt("number"));
|
||||||
|
filter.setType1Level(type1.getInt("level"));
|
||||||
|
filter.setType1KnowledgeCodes(type1.getStr("knowledge_codes"));
|
||||||
} else {
|
} else {
|
||||||
filter.setType1Number(0);
|
filter.setType1Number(0);
|
||||||
}
|
}
|
||||||
JSONObject type2 = score.getJSONObject(ExamConstant.TYPE_2 + "");
|
JSONObject type2 = score.getJSONObject(ExamConstant.TYPE_2 + "");
|
||||||
if (StringUtil.isNotNull(type2)) {
|
if (StringUtil.isNotNull(type2)) {
|
||||||
filter.setType2Number(type2.getInt("number"));
|
filter.setType2Number(type2.getInt("number"));
|
||||||
|
filter.setType2Level(type2.getInt("level"));
|
||||||
|
filter.setType2KnowledgeCodes(type2.getStr("knowledge_codes"));
|
||||||
} else {
|
} else {
|
||||||
filter.setType2Number(0);
|
filter.setType2Number(0);
|
||||||
}
|
}
|
||||||
JSONObject type3 = score.getJSONObject(ExamConstant.TYPE_3 + "");
|
JSONObject type3 = score.getJSONObject(ExamConstant.TYPE_3 + "");
|
||||||
if (StringUtil.isNotNull(type3)) {
|
if (StringUtil.isNotNull(type3)) {
|
||||||
filter.setType3Number(type3.getInt("number"));
|
filter.setType3Number(type3.getInt("number"));
|
||||||
|
filter.setType3Level(type3.getInt("level"));
|
||||||
|
filter.setType3KnowledgeCodes(type3.getStr("knowledge_codes"));
|
||||||
} else {
|
} else {
|
||||||
filter.setType3Number(0);
|
filter.setType3Number(0);
|
||||||
}
|
}
|
||||||
JSONObject type4 = score.getJSONObject(ExamConstant.TYPE_4 + "");
|
JSONObject type4 = score.getJSONObject(ExamConstant.TYPE_4 + "");
|
||||||
if (StringUtil.isNotNull(type4)) {
|
if (StringUtil.isNotNull(type4)) {
|
||||||
filter.setType4Number(type4.getInt("number"));
|
filter.setType4Number(type4.getInt("number"));
|
||||||
|
filter.setType4Level(type4.getInt("level"));
|
||||||
|
filter.setType4KnowledgeCodes(type4.getStr("knowledge_codes"));
|
||||||
} else {
|
} else {
|
||||||
filter.setType4Number(0);
|
filter.setType4Number(0);
|
||||||
}
|
}
|
||||||
JSONObject type5 = score.getJSONObject(ExamConstant.TYPE_5 + "");
|
JSONObject type5 = score.getJSONObject(ExamConstant.TYPE_5 + "");
|
||||||
if (StringUtil.isNotNull(type5)) {
|
if (StringUtil.isNotNull(type5)) {
|
||||||
filter.setType5Number(type5.getInt("number"));
|
filter.setType5Number(type5.getInt("number"));
|
||||||
|
filter.setType5Level(type5.getInt("level"));
|
||||||
|
filter.setType5KnowledgeCodes(type5.getStr("knowledge_codes"));
|
||||||
} else {
|
} else {
|
||||||
filter.setType5Number(0);
|
filter.setType5Number(0);
|
||||||
}
|
}
|
||||||
JSONObject type6 = score.getJSONObject(ExamConstant.TYPE_6 + "");
|
JSONObject type6 = score.getJSONObject(ExamConstant.TYPE_6 + "");
|
||||||
if (StringUtil.isNotNull(type6)) {
|
if (StringUtil.isNotNull(type6)) {
|
||||||
filter.setType6Number(type6.getInt("number"));
|
filter.setType6Number(type6.getInt("number"));
|
||||||
|
filter.setType6Level(type6.getInt("level"));
|
||||||
|
filter.setType6KnowledgeCodes(type6.getStr("knowledge_codes"));
|
||||||
} else {
|
} else {
|
||||||
filter.setType6Number(0);
|
filter.setType6Number(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,14 +12,26 @@ public class ExamQuestionFilter {
|
|||||||
private List<Integer> categoryIds;
|
private List<Integer> categoryIds;
|
||||||
|
|
||||||
private Integer type1Number;
|
private Integer type1Number;
|
||||||
|
private Integer type1Level;
|
||||||
|
private String type1KnowledgeCodes;
|
||||||
|
|
||||||
private Integer type2Number;
|
private Integer type2Number;
|
||||||
|
private Integer type2Level;
|
||||||
|
private String type2KnowledgeCodes;
|
||||||
|
|
||||||
private Integer type3Number;
|
private Integer type3Number;
|
||||||
|
private Integer type3Level;
|
||||||
|
private String type3KnowledgeCodes;
|
||||||
|
|
||||||
private Integer type4Number;
|
private Integer type4Number;
|
||||||
|
private Integer type4Level;
|
||||||
|
private String type4KnowledgeCodes;
|
||||||
|
|
||||||
private Integer type5Number;
|
private Integer type5Number;
|
||||||
|
private Integer type5Level;
|
||||||
|
private String type5KnowledgeCodes;
|
||||||
|
|
||||||
private Integer type6Number;
|
private Integer type6Number;
|
||||||
|
private Integer type6Level;
|
||||||
|
private String type6KnowledgeCodes;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,5 +20,12 @@ public interface IKnowledgeService extends IService<Knowledge> {
|
|||||||
JSONObject getByIdVo(Integer id);
|
JSONObject getByIdVo(Integer id);
|
||||||
JSONObject stuGetByIdVo(Integer id);
|
JSONObject stuGetByIdVo(Integer id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据知识点编码列表批量查询知识点
|
||||||
|
*
|
||||||
|
* @param codes 知识点编码列表
|
||||||
|
* @return 知识点列表
|
||||||
|
* @author menft
|
||||||
|
*/
|
||||||
|
List<Knowledge> getByKnowledgeCodes(List<String> codes);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -217,6 +217,16 @@ public class KnowledgeServiceImpl extends ServiceImpl<KnowledgeMapper, Knowledge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Knowledge> getByKnowledgeCodes(List<String> codes) {
|
||||||
|
if (codes == null || codes.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
LambdaQueryWrapper<Knowledge> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
|
queryWrapper.in(Knowledge::getKnowledgeCode, codes);
|
||||||
|
return list(queryWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 递增编码
|
* 递增编码
|
||||||
* @param code 当前编码
|
* @param code 当前编码
|
||||||
|
|||||||
@ -22,4 +22,6 @@ public interface ExamQuestionMapper extends BaseMapper<ExamQuestion> {
|
|||||||
Long paginateCount(ExamQuestionPaginateFilter filter);
|
Long paginateCount(ExamQuestionPaginateFilter filter);
|
||||||
|
|
||||||
List<ExamQuestion> chunksByCategoryIdAndLimit(ExamQuestionFilter filter);
|
List<ExamQuestion> chunksByCategoryIdAndLimit(ExamQuestionFilter filter);
|
||||||
|
|
||||||
|
List<String> getDistinctKnowledgeCodes(String categoryIds, Integer type, Integer level);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,4 +43,11 @@ public interface ExamQuestionService extends IService<ExamQuestion> {
|
|||||||
List<ExamQuestion> chunksByCategoryId(Integer categoryId);
|
List<ExamQuestion> chunksByCategoryId(Integer categoryId);
|
||||||
|
|
||||||
List<ExamQuestion> chunksByCategoryIdAndLimit(ExamQuestionFilter filter);
|
List<ExamQuestion> chunksByCategoryIdAndLimit(ExamQuestionFilter filter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据题库ID、题型、难度获取去重的知识点编码列表
|
||||||
|
*
|
||||||
|
* @author menft
|
||||||
|
*/
|
||||||
|
List<String> getDistinctKnowledgeCodes(String categoryIds, Integer type, Integer level);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -116,4 +116,22 @@ public class ExamQuestionServiceImpl extends ServiceImpl<ExamQuestionMapper, Exa
|
|||||||
public List<ExamQuestion> chunksByCategoryIdAndLimit(ExamQuestionFilter filter) {
|
public List<ExamQuestion> chunksByCategoryIdAndLimit(ExamQuestionFilter filter) {
|
||||||
return getBaseMapper().chunksByCategoryIdAndLimit(filter);
|
return getBaseMapper().chunksByCategoryIdAndLimit(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getDistinctKnowledgeCodes(String categoryIds, Integer type, Integer level) {
|
||||||
|
// 获取所有 knowledge_code 字段(可能是逗号分隔的多个值)
|
||||||
|
List<String> rawCodes = getBaseMapper().getDistinctKnowledgeCodes(categoryIds, type, level);
|
||||||
|
if (rawCodes == null || rawCodes.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
// 拆分并去重
|
||||||
|
return rawCodes.stream()
|
||||||
|
.filter(code -> code != null && !code.isEmpty())
|
||||||
|
.flatMap(code -> java.util.Arrays.stream(code.split(",")))
|
||||||
|
.map(String::trim)
|
||||||
|
.filter(code -> !code.isEmpty())
|
||||||
|
.distinct()
|
||||||
|
.sorted()
|
||||||
|
.collect(java.util.stream.Collectors.toList());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,6 +107,16 @@
|
|||||||
#{categoryId}
|
#{categoryId}
|
||||||
</foreach>
|
</foreach>
|
||||||
</if>
|
</if>
|
||||||
|
<if test="type1Level != null">
|
||||||
|
and `exam_question`.`level` = #{type1Level}
|
||||||
|
</if>
|
||||||
|
<if test="type1KnowledgeCodes != null and type1KnowledgeCodes != ''">
|
||||||
|
and (
|
||||||
|
<foreach collection="type1KnowledgeCodes.split(',')" item="code" separator=" OR ">
|
||||||
|
FIND_IN_SET(#{code}, `exam_question`.`knowledge_code`)
|
||||||
|
</foreach>
|
||||||
|
)
|
||||||
|
</if>
|
||||||
</where>
|
</where>
|
||||||
ORDER BY RAND()
|
ORDER BY RAND()
|
||||||
LIMIT #{type1Number})
|
LIMIT #{type1Number})
|
||||||
@ -122,6 +132,16 @@
|
|||||||
#{categoryId}
|
#{categoryId}
|
||||||
</foreach>
|
</foreach>
|
||||||
</if>
|
</if>
|
||||||
|
<if test="type2Level != null">
|
||||||
|
and `exam_question`.`level` = #{type2Level}
|
||||||
|
</if>
|
||||||
|
<if test="type2KnowledgeCodes != null and type2KnowledgeCodes != ''">
|
||||||
|
and (
|
||||||
|
<foreach collection="type2KnowledgeCodes.split(',')" item="code" separator=" OR ">
|
||||||
|
FIND_IN_SET(#{code}, `exam_question`.`knowledge_code`)
|
||||||
|
</foreach>
|
||||||
|
)
|
||||||
|
</if>
|
||||||
</where>
|
</where>
|
||||||
ORDER BY RAND()
|
ORDER BY RAND()
|
||||||
LIMIT #{type2Number})
|
LIMIT #{type2Number})
|
||||||
@ -137,6 +157,16 @@
|
|||||||
#{categoryId}
|
#{categoryId}
|
||||||
</foreach>
|
</foreach>
|
||||||
</if>
|
</if>
|
||||||
|
<if test="type3Level != null">
|
||||||
|
and `exam_question`.`level` = #{type3Level}
|
||||||
|
</if>
|
||||||
|
<if test="type3KnowledgeCodes != null and type3KnowledgeCodes != ''">
|
||||||
|
and (
|
||||||
|
<foreach collection="type3KnowledgeCodes.split(',')" item="code" separator=" OR ">
|
||||||
|
FIND_IN_SET(#{code}, `exam_question`.`knowledge_code`)
|
||||||
|
</foreach>
|
||||||
|
)
|
||||||
|
</if>
|
||||||
</where>
|
</where>
|
||||||
ORDER BY RAND()
|
ORDER BY RAND()
|
||||||
LIMIT #{type3Number})
|
LIMIT #{type3Number})
|
||||||
@ -152,6 +182,16 @@
|
|||||||
#{categoryId}
|
#{categoryId}
|
||||||
</foreach>
|
</foreach>
|
||||||
</if>
|
</if>
|
||||||
|
<if test="type4Level != null">
|
||||||
|
and `exam_question`.`level` = #{type4Level}
|
||||||
|
</if>
|
||||||
|
<if test="type4KnowledgeCodes != null and type4KnowledgeCodes != ''">
|
||||||
|
and (
|
||||||
|
<foreach collection="type4KnowledgeCodes.split(',')" item="code" separator=" OR ">
|
||||||
|
FIND_IN_SET(#{code}, `exam_question`.`knowledge_code`)
|
||||||
|
</foreach>
|
||||||
|
)
|
||||||
|
</if>
|
||||||
</where>
|
</where>
|
||||||
ORDER BY RAND()
|
ORDER BY RAND()
|
||||||
LIMIT #{type4Number})
|
LIMIT #{type4Number})
|
||||||
@ -167,6 +207,16 @@
|
|||||||
#{categoryId}
|
#{categoryId}
|
||||||
</foreach>
|
</foreach>
|
||||||
</if>
|
</if>
|
||||||
|
<if test="type5Level != null">
|
||||||
|
and `exam_question`.`level` = #{type5Level}
|
||||||
|
</if>
|
||||||
|
<if test="type5KnowledgeCodes != null and type5KnowledgeCodes != ''">
|
||||||
|
and (
|
||||||
|
<foreach collection="type5KnowledgeCodes.split(',')" item="code" separator=" OR ">
|
||||||
|
FIND_IN_SET(#{code}, `exam_question`.`knowledge_code`)
|
||||||
|
</foreach>
|
||||||
|
)
|
||||||
|
</if>
|
||||||
</where>
|
</where>
|
||||||
ORDER BY RAND()
|
ORDER BY RAND()
|
||||||
LIMIT #{type5Number})
|
LIMIT #{type5Number})
|
||||||
@ -182,8 +232,36 @@
|
|||||||
#{categoryId}
|
#{categoryId}
|
||||||
</foreach>
|
</foreach>
|
||||||
</if>
|
</if>
|
||||||
|
<if test="type6Level != null">
|
||||||
|
and `exam_question`.`level` = #{type6Level}
|
||||||
|
</if>
|
||||||
|
<if test="type6KnowledgeCodes != null and type6KnowledgeCodes != ''">
|
||||||
|
and (
|
||||||
|
<foreach collection="type6KnowledgeCodes.split(',')" item="code" separator=" OR ">
|
||||||
|
FIND_IN_SET(#{code}, `exam_question`.`knowledge_code`)
|
||||||
|
</foreach>
|
||||||
|
)
|
||||||
|
</if>
|
||||||
</where>
|
</where>
|
||||||
ORDER BY RAND()
|
ORDER BY RAND()
|
||||||
LIMIT #{type6Number})
|
LIMIT #{type6Number})
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="getDistinctKnowledgeCodes" resultType="java.lang.String">
|
||||||
|
SELECT DISTINCT `knowledge_code`
|
||||||
|
FROM `exam_question`
|
||||||
|
<where>
|
||||||
|
AND `knowledge_code` IS NOT NULL
|
||||||
|
AND `knowledge_code` != ''
|
||||||
|
<if test="categoryIds != null and categoryIds != ''">
|
||||||
|
AND `category_id` IN (${categoryIds})
|
||||||
|
</if>
|
||||||
|
<if test="type != null">
|
||||||
|
AND `type` = #{type}
|
||||||
|
</if>
|
||||||
|
<if test="level != null">
|
||||||
|
AND `level` = #{level}
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@ -134,3 +134,17 @@ export function storeBatch(categoryId: number, startLine: number, questions: str
|
|||||||
export function uploadTxt(params: any) {
|
export function uploadTxt(params: any) {
|
||||||
return client.post('/backend/v1/exam/question/import', params);
|
return client.post('/backend/v1/exam/question/import', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据题库ID、题型、难度获取关联的知识点编码列表
|
||||||
|
* @param categoryIds 题库ID列表(逗号分隔)
|
||||||
|
* @param type 题型
|
||||||
|
* @param level 难度
|
||||||
|
*/
|
||||||
|
export function getKnowledgeCodes(categoryIds: string, type?: number, level?: number | null) {
|
||||||
|
return client.get('/backend/v1/exam/question/knowledge-codes', {
|
||||||
|
category_ids: categoryIds,
|
||||||
|
type: type,
|
||||||
|
level: level,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import styles from './paper.module.less';
|
import styles from './paper.module.less';
|
||||||
import { message, Button, Table, InputNumber, Spin, Modal } from 'antd';
|
import { message, Button, Table, InputNumber, Spin, Modal, Select } from 'antd';
|
||||||
import { paper } from '../../../../api';
|
import { paper, question } from '../../../../api';
|
||||||
import type { ColumnsType } from 'antd/es/table';
|
import type { ColumnsType } from 'antd/es/table';
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
@ -10,6 +10,14 @@ import { ExclamationCircleFilled } from '@ant-design/icons';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
const { confirm } = Modal;
|
const { confirm } = Modal;
|
||||||
|
|
||||||
|
// 难度选项
|
||||||
|
const levelOptions = [
|
||||||
|
{ value: null, label: '不限' },
|
||||||
|
{ value: 1, label: '简单' },
|
||||||
|
{ value: 2, label: '中等' },
|
||||||
|
{ value: 3, label: '困难' },
|
||||||
|
];
|
||||||
|
|
||||||
interface DataType {
|
interface DataType {
|
||||||
id: React.Key;
|
id: React.Key;
|
||||||
admin_id: number;
|
admin_id: number;
|
||||||
@ -61,28 +69,49 @@ export const RendomPaper: React.FC<PropInterface> = ({ type }) => {
|
|||||||
const [choice, setChoice] = useState({
|
const [choice, setChoice] = useState({
|
||||||
number: 0,
|
number: 0,
|
||||||
score: 0,
|
score: 0,
|
||||||
|
level: null as number | null,
|
||||||
|
knowledge_codes: '' as string,
|
||||||
});
|
});
|
||||||
const [select, setSelect] = useState({
|
const [select, setSelect] = useState({
|
||||||
number: 0,
|
number: 0,
|
||||||
score: 0,
|
score: 0,
|
||||||
missed_score: 0,
|
missed_score: 0,
|
||||||
|
level: null as number | null,
|
||||||
|
knowledge_codes: '' as string,
|
||||||
});
|
});
|
||||||
const [input, setInput] = useState({
|
const [input, setInput] = useState({
|
||||||
number: 0,
|
number: 0,
|
||||||
score: 0,
|
score: 0,
|
||||||
|
level: null as number | null,
|
||||||
|
knowledge_codes: '' as string,
|
||||||
});
|
});
|
||||||
const [judge, setJudge] = useState({
|
const [judge, setJudge] = useState({
|
||||||
number: 0,
|
number: 0,
|
||||||
score: 0,
|
score: 0,
|
||||||
|
level: null as number | null,
|
||||||
|
knowledge_codes: '' as string,
|
||||||
});
|
});
|
||||||
const [qa, setQa] = useState({
|
const [qa, setQa] = useState({
|
||||||
number: 0,
|
number: 0,
|
||||||
score: 0,
|
score: 0,
|
||||||
|
level: null as number | null,
|
||||||
|
knowledge_codes: '' as string,
|
||||||
});
|
});
|
||||||
const [cap, setCap] = useState({
|
const [cap, setCap] = useState({
|
||||||
number: 0,
|
number: 0,
|
||||||
score: 0,
|
score: 0,
|
||||||
missed_score: 0,
|
missed_score: 0,
|
||||||
|
level: null as number | null,
|
||||||
|
knowledge_codes: '' as string,
|
||||||
|
});
|
||||||
|
// 各题型的知识点选项
|
||||||
|
const [knowledgeOptions, setKnowledgeOptions] = useState<Record<number, any[]>>({
|
||||||
|
1: [],
|
||||||
|
2: [],
|
||||||
|
3: [],
|
||||||
|
4: [],
|
||||||
|
5: [],
|
||||||
|
6: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -91,6 +120,23 @@ export const RendomPaper: React.FC<PropInterface> = ({ type }) => {
|
|||||||
setId(Number(result.get('id')));
|
setId(Number(result.get('id')));
|
||||||
}, [result.get('cid'), result.get('title'), result.get('id')]);
|
}, [result.get('cid'), result.get('title'), result.get('id')]);
|
||||||
|
|
||||||
|
// 根据题库、题型、难度加载知识点列表
|
||||||
|
const loadKnowledgeCodes = async (type: number, level: number | null) => {
|
||||||
|
if (questions.length === 0) return;
|
||||||
|
try {
|
||||||
|
const res: any = await question.getKnowledgeCodes(questions.join(','), type, level);
|
||||||
|
const items = res.data || [];
|
||||||
|
// 后端返回 [{code: "xxx", name: "yyy"}, ...]
|
||||||
|
const options = items.map((item: { code: string; name: string }) => ({
|
||||||
|
value: item.code,
|
||||||
|
label: item.name,
|
||||||
|
}));
|
||||||
|
setKnowledgeOptions((prev) => ({ ...prev, [type]: options }));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载知识点失败', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (id === 0) {
|
if (id === 0) {
|
||||||
return;
|
return;
|
||||||
@ -187,6 +233,8 @@ export const RendomPaper: React.FC<PropInterface> = ({ type }) => {
|
|||||||
setChoice({
|
setChoice({
|
||||||
number: score[1].number,
|
number: score[1].number,
|
||||||
score: score[1].score,
|
score: score[1].score,
|
||||||
|
level: score[1].level || null,
|
||||||
|
knowledge_codes: score[1].knowledge_codes || '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (score[2]) {
|
if (score[2]) {
|
||||||
@ -194,24 +242,32 @@ export const RendomPaper: React.FC<PropInterface> = ({ type }) => {
|
|||||||
number: score[2].number,
|
number: score[2].number,
|
||||||
score: score[2].score,
|
score: score[2].score,
|
||||||
missed_score: score[2].missed_score,
|
missed_score: score[2].missed_score,
|
||||||
|
level: score[2].level || null,
|
||||||
|
knowledge_codes: score[2].knowledge_codes || '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (score[3]) {
|
if (score[3]) {
|
||||||
setInput({
|
setInput({
|
||||||
number: score[3].number,
|
number: score[3].number,
|
||||||
score: score[3].score,
|
score: score[3].score,
|
||||||
|
level: score[3].level || null,
|
||||||
|
knowledge_codes: score[3].knowledge_codes || '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (score[4]) {
|
if (score[4]) {
|
||||||
setJudge({
|
setJudge({
|
||||||
number: score[4].number,
|
number: score[4].number,
|
||||||
score: score[4].score,
|
score: score[4].score,
|
||||||
|
level: score[4].level || null,
|
||||||
|
knowledge_codes: score[4].knowledge_codes || '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (score[5]) {
|
if (score[5]) {
|
||||||
setQa({
|
setQa({
|
||||||
number: score[5].number,
|
number: score[5].number,
|
||||||
score: score[5].score,
|
score: score[5].score,
|
||||||
|
level: score[5].level || null,
|
||||||
|
knowledge_codes: score[5].knowledge_codes || '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (score[6]) {
|
if (score[6]) {
|
||||||
@ -219,10 +275,33 @@ export const RendomPaper: React.FC<PropInterface> = ({ type }) => {
|
|||||||
number: score[6].number,
|
number: score[6].number,
|
||||||
score: score[6].score,
|
score: score[6].score,
|
||||||
missed_score: 0,
|
missed_score: 0,
|
||||||
|
level: score[6].level || null,
|
||||||
|
knowledge_codes: score[6].knowledge_codes || '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setSpinInit(false);
|
setSpinInit(false);
|
||||||
setInit(false);
|
setInit(false);
|
||||||
|
|
||||||
|
// 预加载各题型的知识点选项(用于回显)
|
||||||
|
if (arr.length > 0) {
|
||||||
|
const categoryIds = arr.join(',');
|
||||||
|
[1, 2, 3, 4, 5, 6].forEach((type) => {
|
||||||
|
const typeScore = score[type];
|
||||||
|
if (typeScore && typeScore.knowledge_codes) {
|
||||||
|
question
|
||||||
|
.getKnowledgeCodes(categoryIds, type, typeScore.level || null)
|
||||||
|
.then((res: any) => {
|
||||||
|
const items = res.data || [];
|
||||||
|
const options = items.map((item: { code: string; name: string }) => ({
|
||||||
|
value: item.code,
|
||||||
|
label: item.name,
|
||||||
|
}));
|
||||||
|
setKnowledgeOptions((prev) => ({ ...prev, [type]: options }));
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -609,6 +688,42 @@ export const RendomPaper: React.FC<PropInterface> = ({ type }) => {
|
|||||||
></InputNumber>
|
></InputNumber>
|
||||||
{t('exam.paper.compose.text4')}
|
{t('exam.paper.compose.text4')}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="d-flex ml-30">
|
||||||
|
难度
|
||||||
|
<Select
|
||||||
|
style={{ width: 100, marginLeft: 8 }}
|
||||||
|
size="large"
|
||||||
|
options={levelOptions}
|
||||||
|
value={choice.level}
|
||||||
|
onChange={(value) => {
|
||||||
|
const obj = { ...choice };
|
||||||
|
obj.level = value;
|
||||||
|
obj.knowledge_codes = '';
|
||||||
|
setChoice(obj);
|
||||||
|
loadKnowledgeCodes(1, value);
|
||||||
|
}}
|
||||||
|
allowClear
|
||||||
|
placeholder="不限"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="d-flex ml-30">
|
||||||
|
知识点
|
||||||
|
<Select
|
||||||
|
style={{ width: 200, marginLeft: 8 }}
|
||||||
|
size="large"
|
||||||
|
mode="multiple"
|
||||||
|
options={knowledgeOptions[1]}
|
||||||
|
value={choice.knowledge_codes ? choice.knowledge_codes.split(',') : []}
|
||||||
|
onChange={(values: string[]) => {
|
||||||
|
const obj = { ...choice };
|
||||||
|
obj.knowledge_codes = values.join(',');
|
||||||
|
setChoice(obj);
|
||||||
|
}}
|
||||||
|
maxTagCount="responsive"
|
||||||
|
placeholder="不限"
|
||||||
|
onFocus={() => loadKnowledgeCodes(1, choice.level)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
{t('exam.paper.compose.text1')}
|
{t('exam.paper.compose.text1')}
|
||||||
@ -619,7 +734,7 @@ export const RendomPaper: React.FC<PropInterface> = ({ type }) => {
|
|||||||
)}
|
)}
|
||||||
{q2 > 0 && (
|
{q2 > 0 && (
|
||||||
<div className={styles['config-item']}>
|
<div className={styles['config-item']}>
|
||||||
<div className="d-flex">
|
<div className="d-flex" style={{ flexWrap: 'wrap', gap: '8px 0' }}>
|
||||||
<div className={styles['label']}>
|
<div className={styles['label']}>
|
||||||
<span className="c-red">*</span>
|
<span className="c-red">*</span>
|
||||||
{t('exam.question.select.label')}({t('exam.paper.compose.text1')}
|
{t('exam.question.select.label')}({t('exam.paper.compose.text1')}
|
||||||
@ -679,6 +794,42 @@ export const RendomPaper: React.FC<PropInterface> = ({ type }) => {
|
|||||||
></InputNumber>
|
></InputNumber>
|
||||||
{t('exam.paper.compose.text5')}
|
{t('exam.paper.compose.text5')}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="d-flex ml-30">
|
||||||
|
难度
|
||||||
|
<Select
|
||||||
|
style={{ width: 100, marginLeft: 8 }}
|
||||||
|
size="large"
|
||||||
|
options={levelOptions}
|
||||||
|
value={select.level}
|
||||||
|
onChange={(value) => {
|
||||||
|
const obj = { ...select };
|
||||||
|
obj.level = value;
|
||||||
|
obj.knowledge_codes = '';
|
||||||
|
setSelect(obj);
|
||||||
|
loadKnowledgeCodes(2, value);
|
||||||
|
}}
|
||||||
|
allowClear
|
||||||
|
placeholder="不限"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="d-flex ml-30">
|
||||||
|
知识点
|
||||||
|
<Select
|
||||||
|
style={{ width: 200, marginLeft: 8 }}
|
||||||
|
size="large"
|
||||||
|
mode="multiple"
|
||||||
|
options={knowledgeOptions[2]}
|
||||||
|
value={select.knowledge_codes ? select.knowledge_codes.split(',') : []}
|
||||||
|
onChange={(values: string[]) => {
|
||||||
|
const obj = { ...select };
|
||||||
|
obj.knowledge_codes = values.join(',');
|
||||||
|
setSelect(obj);
|
||||||
|
}}
|
||||||
|
maxTagCount="responsive"
|
||||||
|
placeholder="不限"
|
||||||
|
onFocus={() => loadKnowledgeCodes(2, select.level)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
{t('exam.paper.compose.text1')}
|
{t('exam.paper.compose.text1')}
|
||||||
@ -689,7 +840,7 @@ export const RendomPaper: React.FC<PropInterface> = ({ type }) => {
|
|||||||
)}
|
)}
|
||||||
{q3 > 0 && (
|
{q3 > 0 && (
|
||||||
<div className={styles['config-item']}>
|
<div className={styles['config-item']}>
|
||||||
<div className="d-flex">
|
<div className="d-flex" style={{ flexWrap: 'wrap', gap: '8px 0' }}>
|
||||||
<div className={styles['label']}>
|
<div className={styles['label']}>
|
||||||
<span className="c-red">*</span>
|
<span className="c-red">*</span>
|
||||||
{t('exam.question.input.label')}({t('exam.paper.compose.text1')}
|
{t('exam.question.input.label')}({t('exam.paper.compose.text1')}
|
||||||
@ -730,6 +881,42 @@ export const RendomPaper: React.FC<PropInterface> = ({ type }) => {
|
|||||||
></InputNumber>
|
></InputNumber>
|
||||||
{t('exam.paper.compose.text4')}
|
{t('exam.paper.compose.text4')}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="d-flex ml-30">
|
||||||
|
难度
|
||||||
|
<Select
|
||||||
|
style={{ width: 100, marginLeft: 8 }}
|
||||||
|
size="large"
|
||||||
|
options={levelOptions}
|
||||||
|
value={input.level}
|
||||||
|
onChange={(value) => {
|
||||||
|
const obj = { ...input };
|
||||||
|
obj.level = value;
|
||||||
|
obj.knowledge_codes = '';
|
||||||
|
setInput(obj);
|
||||||
|
loadKnowledgeCodes(3, value);
|
||||||
|
}}
|
||||||
|
allowClear
|
||||||
|
placeholder="不限"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="d-flex ml-30">
|
||||||
|
知识点
|
||||||
|
<Select
|
||||||
|
style={{ width: 200, marginLeft: 8 }}
|
||||||
|
size="large"
|
||||||
|
mode="multiple"
|
||||||
|
options={knowledgeOptions[3]}
|
||||||
|
value={input.knowledge_codes ? input.knowledge_codes.split(',') : []}
|
||||||
|
onChange={(values: string[]) => {
|
||||||
|
const obj = { ...input };
|
||||||
|
obj.knowledge_codes = values.join(',');
|
||||||
|
setInput(obj);
|
||||||
|
}}
|
||||||
|
maxTagCount="responsive"
|
||||||
|
placeholder="不限"
|
||||||
|
onFocus={() => loadKnowledgeCodes(3, input.level)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
{t('exam.paper.compose.text1')}
|
{t('exam.paper.compose.text1')}
|
||||||
@ -740,7 +927,7 @@ export const RendomPaper: React.FC<PropInterface> = ({ type }) => {
|
|||||||
)}
|
)}
|
||||||
{q4 > 0 && (
|
{q4 > 0 && (
|
||||||
<div className={styles['config-item']}>
|
<div className={styles['config-item']}>
|
||||||
<div className="d-flex">
|
<div className="d-flex" style={{ flexWrap: 'wrap', gap: '8px 0' }}>
|
||||||
<div className={styles['label']}>
|
<div className={styles['label']}>
|
||||||
<span className="c-red">*</span>
|
<span className="c-red">*</span>
|
||||||
{t('exam.question.judge.label')}({t('exam.paper.compose.text1')}
|
{t('exam.question.judge.label')}({t('exam.paper.compose.text1')}
|
||||||
@ -781,6 +968,42 @@ export const RendomPaper: React.FC<PropInterface> = ({ type }) => {
|
|||||||
></InputNumber>
|
></InputNumber>
|
||||||
{t('exam.paper.compose.text4')}
|
{t('exam.paper.compose.text4')}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="d-flex ml-30">
|
||||||
|
难度
|
||||||
|
<Select
|
||||||
|
style={{ width: 100, marginLeft: 8 }}
|
||||||
|
size="large"
|
||||||
|
options={levelOptions}
|
||||||
|
value={judge.level}
|
||||||
|
onChange={(value) => {
|
||||||
|
const obj = { ...judge };
|
||||||
|
obj.level = value;
|
||||||
|
obj.knowledge_codes = '';
|
||||||
|
setJudge(obj);
|
||||||
|
loadKnowledgeCodes(4, value);
|
||||||
|
}}
|
||||||
|
allowClear
|
||||||
|
placeholder="不限"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="d-flex ml-30">
|
||||||
|
知识点
|
||||||
|
<Select
|
||||||
|
style={{ width: 200, marginLeft: 8 }}
|
||||||
|
size="large"
|
||||||
|
mode="multiple"
|
||||||
|
options={knowledgeOptions[4]}
|
||||||
|
value={judge.knowledge_codes ? judge.knowledge_codes.split(',') : []}
|
||||||
|
onChange={(values: string[]) => {
|
||||||
|
const obj = { ...judge };
|
||||||
|
obj.knowledge_codes = values.join(',');
|
||||||
|
setJudge(obj);
|
||||||
|
}}
|
||||||
|
maxTagCount="responsive"
|
||||||
|
placeholder="不限"
|
||||||
|
onFocus={() => loadKnowledgeCodes(4, judge.level)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
{t('exam.paper.compose.text1')}
|
{t('exam.paper.compose.text1')}
|
||||||
@ -791,7 +1014,7 @@ export const RendomPaper: React.FC<PropInterface> = ({ type }) => {
|
|||||||
)}
|
)}
|
||||||
{q5 > 0 && (
|
{q5 > 0 && (
|
||||||
<div className={styles['config-item']}>
|
<div className={styles['config-item']}>
|
||||||
<div className="d-flex">
|
<div className="d-flex" style={{ flexWrap: 'wrap', gap: '8px 0' }}>
|
||||||
<div className={styles['label']}>
|
<div className={styles['label']}>
|
||||||
<span className="c-red">*</span>
|
<span className="c-red">*</span>
|
||||||
{t('exam.question.qa.label')}({t('exam.paper.compose.text1')}
|
{t('exam.question.qa.label')}({t('exam.paper.compose.text1')}
|
||||||
@ -832,6 +1055,42 @@ export const RendomPaper: React.FC<PropInterface> = ({ type }) => {
|
|||||||
></InputNumber>
|
></InputNumber>
|
||||||
{t('exam.paper.compose.text4')}
|
{t('exam.paper.compose.text4')}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="d-flex ml-30">
|
||||||
|
难度
|
||||||
|
<Select
|
||||||
|
style={{ width: 100, marginLeft: 8 }}
|
||||||
|
size="large"
|
||||||
|
options={levelOptions}
|
||||||
|
value={qa.level}
|
||||||
|
onChange={(value) => {
|
||||||
|
const obj = { ...qa };
|
||||||
|
obj.level = value;
|
||||||
|
obj.knowledge_codes = '';
|
||||||
|
setQa(obj);
|
||||||
|
loadKnowledgeCodes(5, value);
|
||||||
|
}}
|
||||||
|
allowClear
|
||||||
|
placeholder="不限"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="d-flex ml-30">
|
||||||
|
知识点
|
||||||
|
<Select
|
||||||
|
style={{ width: 200, marginLeft: 8 }}
|
||||||
|
size="large"
|
||||||
|
mode="multiple"
|
||||||
|
options={knowledgeOptions[5]}
|
||||||
|
value={qa.knowledge_codes ? qa.knowledge_codes.split(',') : []}
|
||||||
|
onChange={(values: string[]) => {
|
||||||
|
const obj = { ...qa };
|
||||||
|
obj.knowledge_codes = values.join(',');
|
||||||
|
setQa(obj);
|
||||||
|
}}
|
||||||
|
maxTagCount="responsive"
|
||||||
|
placeholder="不限"
|
||||||
|
onFocus={() => loadKnowledgeCodes(5, qa.level)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
{t('exam.paper.compose.text1')}
|
{t('exam.paper.compose.text1')}
|
||||||
@ -842,7 +1101,7 @@ export const RendomPaper: React.FC<PropInterface> = ({ type }) => {
|
|||||||
)}
|
)}
|
||||||
{q6 > 0 && (
|
{q6 > 0 && (
|
||||||
<div className={styles['config-item']}>
|
<div className={styles['config-item']}>
|
||||||
<div className="d-flex">
|
<div className="d-flex" style={{ flexWrap: 'wrap', gap: '8px 0' }}>
|
||||||
<div className={styles['label']}>
|
<div className={styles['label']}>
|
||||||
<span className="c-red">*</span>
|
<span className="c-red">*</span>
|
||||||
{t('exam.question.cap.label')}({t('exam.paper.compose.text1')}
|
{t('exam.question.cap.label')}({t('exam.paper.compose.text1')}
|
||||||
@ -883,6 +1142,42 @@ export const RendomPaper: React.FC<PropInterface> = ({ type }) => {
|
|||||||
></InputNumber>
|
></InputNumber>
|
||||||
{t('exam.paper.compose.text4')}
|
{t('exam.paper.compose.text4')}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="d-flex ml-30">
|
||||||
|
难度
|
||||||
|
<Select
|
||||||
|
style={{ width: 100, marginLeft: 8 }}
|
||||||
|
size="large"
|
||||||
|
options={levelOptions}
|
||||||
|
value={cap.level}
|
||||||
|
onChange={(value) => {
|
||||||
|
const obj = { ...cap };
|
||||||
|
obj.level = value;
|
||||||
|
obj.knowledge_codes = '';
|
||||||
|
setCap(obj);
|
||||||
|
loadKnowledgeCodes(6, value);
|
||||||
|
}}
|
||||||
|
allowClear
|
||||||
|
placeholder="不限"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="d-flex ml-30">
|
||||||
|
知识点
|
||||||
|
<Select
|
||||||
|
style={{ width: 200, marginLeft: 8 }}
|
||||||
|
size="large"
|
||||||
|
mode="multiple"
|
||||||
|
options={knowledgeOptions[6]}
|
||||||
|
value={cap.knowledge_codes ? cap.knowledge_codes.split(',') : []}
|
||||||
|
onChange={(values: string[]) => {
|
||||||
|
const obj = { ...cap };
|
||||||
|
obj.knowledge_codes = values.join(',');
|
||||||
|
setCap(obj);
|
||||||
|
}}
|
||||||
|
maxTagCount="responsive"
|
||||||
|
placeholder="不限"
|
||||||
|
onFocus={() => loadKnowledgeCodes(6, cap.level)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
{t('exam.paper.compose.text1')}
|
{t('exam.paper.compose.text1')}
|
||||||
|
|||||||
@ -78,14 +78,18 @@ export const QuestionsDetailUpdate: React.FC<PropInterface> = ({ id, qid, open,
|
|||||||
try {
|
try {
|
||||||
// 获取知识点详情(包含bookId)
|
// 获取知识点详情(包含bookId)
|
||||||
const knowledgeRes: any = await textbook.getKnowledgeByCodesApi(data.knowledge_code);
|
const knowledgeRes: any = await textbook.getKnowledgeByCodesApi(data.knowledge_code);
|
||||||
if (knowledgeRes.data && Array.isArray(knowledgeRes.data) && knowledgeRes.data.length > 0) {
|
if (
|
||||||
|
knowledgeRes.data &&
|
||||||
|
Array.isArray(knowledgeRes.data) &&
|
||||||
|
knowledgeRes.data.length > 0
|
||||||
|
) {
|
||||||
// 获取所有需要预加载的教材ID(去重)
|
// 获取所有需要预加载的教材ID(去重)
|
||||||
const bookIds = [...new Set(knowledgeRes.data.map((k: any) => k.bookId))];
|
const bookIds = [...new Set(knowledgeRes.data.map((k: any) => k.bookId))];
|
||||||
|
|
||||||
// 预加载每个教材的知识点列表到级联选项中
|
// 预加载每个教材的知识点列表到级联选项中
|
||||||
for (const bookId of bookIds) {
|
for (const bookId of bookIds) {
|
||||||
const knowledgeListRes: any = await textbook.getKnowledgeListApi(bookId as number);
|
const knowledgeListRes: any = await textbook.getKnowledgeListApi(bookId as number);
|
||||||
const targetOption = options.find(opt => opt.value === bookId);
|
const targetOption = options.find((opt) => opt.value === bookId);
|
||||||
if (targetOption && knowledgeListRes.data && Array.isArray(knowledgeListRes.data)) {
|
if (targetOption && knowledgeListRes.data && Array.isArray(knowledgeListRes.data)) {
|
||||||
targetOption.children = knowledgeListRes.data.map((item: any) => ({
|
targetOption.children = knowledgeListRes.data.map((item: any) => ({
|
||||||
value: item.knowledgeCode || item.knowledge_code,
|
value: item.knowledgeCode || item.knowledge_code,
|
||||||
@ -97,7 +101,10 @@ export const QuestionsDetailUpdate: React.FC<PropInterface> = ({ id, qid, open,
|
|||||||
setCascaderOptions([...options]);
|
setCascaderOptions([...options]);
|
||||||
|
|
||||||
// 构建级联选择器的值 [[bookId, knowledgeCode], ...]
|
// 构建级联选择器的值 [[bookId, knowledgeCode], ...]
|
||||||
cascaderValue = knowledgeRes.data.map((k: any) => [k.bookId, k.knowledgeCode || k.knowledge_code]);
|
cascaderValue = knowledgeRes.data.map((k: any) => [
|
||||||
|
k.bookId,
|
||||||
|
k.knowledgeCode || k.knowledge_code,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('加载知识点详情失败:', err);
|
console.error('加载知识点详情失败:', err);
|
||||||
@ -124,24 +131,27 @@ export const QuestionsDetailUpdate: React.FC<PropInterface> = ({ id, qid, open,
|
|||||||
const targetOption = selectedOptions[selectedOptions.length - 1];
|
const targetOption = selectedOptions[selectedOptions.length - 1];
|
||||||
targetOption.loading = true;
|
targetOption.loading = true;
|
||||||
|
|
||||||
textbook.getKnowledgeListApi(targetOption.value as number).then((res: any) => {
|
textbook
|
||||||
targetOption.loading = false;
|
.getKnowledgeListApi(targetOption.value as number)
|
||||||
if (res.data && Array.isArray(res.data)) {
|
.then((res: any) => {
|
||||||
targetOption.children = res.data.map((item: any) => ({
|
targetOption.loading = false;
|
||||||
value: item.knowledgeCode || item.knowledge_code,
|
if (res.data && Array.isArray(res.data)) {
|
||||||
label: item.name,
|
targetOption.children = res.data.map((item: any) => ({
|
||||||
isLeaf: true,
|
value: item.knowledgeCode || item.knowledge_code,
|
||||||
}));
|
label: item.name,
|
||||||
} else {
|
isLeaf: true,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
targetOption.children = [];
|
||||||
|
}
|
||||||
|
setCascaderOptions([...cascaderOptions]);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error('加载知识点失败:', err);
|
||||||
|
targetOption.loading = false;
|
||||||
targetOption.children = [];
|
targetOption.children = [];
|
||||||
}
|
setCascaderOptions([...cascaderOptions]);
|
||||||
setCascaderOptions([...cascaderOptions]);
|
});
|
||||||
}).catch((err) => {
|
|
||||||
console.error('加载知识点失败:', err);
|
|
||||||
targetOption.loading = false;
|
|
||||||
targetOption.children = [];
|
|
||||||
setCascaderOptions([...cascaderOptions]);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onFinish = (values: any) => {
|
const onFinish = (values: any) => {
|
||||||
@ -366,11 +376,13 @@ export const QuestionsDetailUpdate: React.FC<PropInterface> = ({ id, qid, open,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
question.questionUpdate(qid, id, params, values.level, Number(type), knowledgeCode).then((res: any) => {
|
question
|
||||||
setLoading(false);
|
.questionUpdate(qid, id, params, values.level, Number(type), knowledgeCode)
|
||||||
message.success(t('commen.saveSuccess'));
|
.then((res: any) => {
|
||||||
onCancel();
|
setLoading(false);
|
||||||
});
|
message.success(t('commen.saveSuccess'));
|
||||||
|
onCancel();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onFinishFailed = (errorInfo: any) => {
|
const onFinishFailed = (errorInfo: any) => {
|
||||||
@ -535,10 +547,7 @@ export const QuestionsDetailUpdate: React.FC<PropInterface> = ({ id, qid, open,
|
|||||||
</Radio>
|
</Radio>
|
||||||
</Radio.Group>
|
</Radio.Group>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item label="关联知识点" name="knowledge_cascader">
|
||||||
label="关联知识点"
|
|
||||||
name="knowledge_cascader"
|
|
||||||
>
|
|
||||||
<Cascader
|
<Cascader
|
||||||
options={cascaderOptions}
|
options={cascaderOptions}
|
||||||
loadData={loadKnowledgeData as CascaderProps<CascaderOption>['loadData']}
|
loadData={loadKnowledgeData as CascaderProps<CascaderOption>['loadData']}
|
||||||
@ -551,7 +560,9 @@ export const QuestionsDetailUpdate: React.FC<PropInterface> = ({ id, qid, open,
|
|||||||
filter: (inputValue: string, path: CascaderOption[]) =>
|
filter: (inputValue: string, path: CascaderOption[]) =>
|
||||||
path.some(
|
path.some(
|
||||||
(option) =>
|
(option) =>
|
||||||
(option.label as string).toLowerCase().indexOf(inputValue.toLowerCase()) > -1
|
(option.label as string)
|
||||||
|
.toLowerCase()
|
||||||
|
.indexOf(inputValue.toLowerCase()) > -1
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user