فهرست منبع

优化:selectEnrollPlanProfessionByCode

jinxia.mo 1 ماه پیش
والد
کامیت
6cefd0a9f8

+ 9 - 0
back-ui/src/api/learn/knowledgeTree.js

@@ -25,6 +25,15 @@ export function getKnowledgeTree(id) {
   })
 }
 
+// 批量查询知识点树详细
+export function getKnowledgeTreeBatch(ids) {
+  return request({
+    url: '/learn/knowledgeTree/getKnowledgeByIds',
+    method: 'post',
+    data: ids
+  })
+}
+
 // 新增知识点树
 export function addKnowledgeTree(data) {
   return request({

+ 25 - 13
back-ui/src/views/dz/papers/components/paper-knowledge-hand.vue

@@ -1,6 +1,9 @@
 <template>
     <el-row :gutter="20">
-        <el-col :span="24">
+        <el-col :span="6">
+            <knowledge-tree allow-multiple/>
+        </el-col>
+        <el-col :span="18">
             <el-form label-width="68px">
 <!--                <BatchYearSelect v-model:batch-id="batchId" :batch-list="batchList" />-->
                 <el-row :gutter="20">
@@ -34,16 +37,10 @@
                     </el-col>
                 </el-row>
             </el-form>
-        </el-col>
-        
-    </el-row>
-    <el-divider />
-    <el-row v-if="!hasBuiltPaper" :gutter="20">
-        <el-col :span="6">
-            <knowledge-tree allow-multiple/>
-        </el-col>
-        <el-col :span="18">
-            <question-intelligent @submit="handleSubmit" />
+            <el-divider v-if="!hasBuiltPaper" />
+            <div v-if="!hasBuiltPaper">
+                <question-intelligent @submit="handleSubmit" />
+            </div>
         </el-col>
     </el-row>
     <built-paper-list ref="built" @send="handleSubmit" />
@@ -130,6 +127,7 @@ const handleSubmit = async (qTypes) => {
 }
 
 let statArg = null
+let previousSubjectId = null // 保存上一次的科目ID
 const _loadClassStatistic = async () => {
     selectedClasses.value = []
     classList.value = []
@@ -138,8 +136,22 @@ const _loadClassStatistic = async () => {
 }
 onConditionReady(async (payload) => {
     statArg = payload
-    knowledgeNode.value = null
-    knowledgeCheckNodes.value = []
+    
+    // 判断是否是科目切换(只有 subjectId 变化,其他条件不变)
+    const isSubjectChange = previousSubjectId !== null && 
+                           previousSubjectId !== payload.subjectId &&
+                           statArg && 
+                           statArg.batchId === payload.batchId &&
+                           statArg.examType === payload.examType
+    
+    // 如果不是科目切换,或者是首次加载,则清空知识点
+    if (!isSubjectChange) {
+        knowledgeNode.value = null
+        knowledgeCheckNodes.value = []
+    }
+    
+    // 保存当前的科目ID
+    previousSubjectId = payload.subjectId
 
     await loadKnowledge(payload)
     await _loadClassStatistic()

+ 41 - 1
back-ui/src/views/dz/papers/components/plugs/knowledge-tree.vue

@@ -26,7 +26,47 @@ const handleNodeClick = function (data) {
 
 const handleNodeCheck = function (data, {checkedNodes}) {
     // 多选时,只保留叶子节点
-    knowledgeCheckNodes.value = checkedNodes.filter(k => !!!k.children?.length)
+    const newCheckedLeafNodes = checkedNodes.filter(k => !!!k.children?.length)
+    
+    // 获取当前已选知识点的ID集合
+    const existingIds = new Set(knowledgeCheckNodes.value.map(k => k.id))
+    
+    // 获取新树中被选中的节点ID集合
+    const newCheckedIds = new Set(newCheckedLeafNodes.map(k => k.id))
+    
+    // 找出新增的节点(在新选中但不在已选中)
+    const addedNodes = newCheckedLeafNodes.filter(k => !existingIds.has(k.id))
+    
+    // 找出需要保留的旧节点(在新树中仍然存在的节点,或者不在新树中但用户没有取消选择)
+    // 由于 el-tree 的 checkedNodes 只包含当前树中的节点,我们需要保留那些不在新树中的旧节点
+    // 但移除那些在新树中被取消选择的节点
+    const currentTreeIds = new Set(knowledges.value.flatMap(k => getAllNodeIds(k)))
+    const removedFromTree = knowledgeCheckNodes.value.filter(k => !currentTreeIds.has(k.id))
+    
+    // 合并:保留不在当前树中的旧节点(跨科目的节点)+ 新选中的节点
+    // 但需要移除在当前树中被取消选择的节点
+    const keptOldNodes = knowledgeCheckNodes.value.filter(k => {
+        // 如果节点不在当前树中,保留它(跨科目节点)
+        if (!currentTreeIds.has(k.id)) {
+            return true
+        }
+        // 如果节点在当前树中,且在新选中,保留它
+        return newCheckedIds.has(k.id)
+    })
+    
+    // 合并保留的旧节点和新添加的节点
+    knowledgeCheckNodes.value = [...keptOldNodes, ...addedNodes]
+}
+
+// 辅助函数:获取树中所有节点的ID(包括子节点)
+function getAllNodeIds(node) {
+    const ids = [node.id]
+    if (node.children && node.children.length > 0) {
+        node.children.forEach(child => {
+            ids.push(...getAllNodeIds(child))
+        })
+    }
+    return ids
 }
 
 watch(keyword, val => {

+ 82 - 4
back-ui/src/views/dz/papers/components/plugs/question-intelligent.vue

@@ -1,10 +1,37 @@
 <template>
-    <div class="text-main mb-3 flex items-center">
-        <div :class="seqClass">1</div>选择知识点(从左侧勾选)
+    <div class="text-main mb-3 flex items-center justify-between">
+        <div class="flex items-center">
+            <div :class="seqClass">1</div>选择知识点(从左侧勾选)
+        </div>
+        <div class="flex items-center gap-2">
+            <el-button size="small" @click="handleBatchAdd">批量添加知识点</el-button>
+            <el-button size="small" type="danger" @click="handleClearAll">一键清空</el-button>
+        </div>
     </div>
     <div class="flex flex-row flex-wrap gap-3">
         <el-tag v-for="k in knowledgeCheckNodes" type="primary" round closable @close="removeKnowledge(k)">{{k.name}}</el-tag>
     </div>
+    
+    <!-- 批量添加知识点弹窗 -->
+    <el-dialog v-model="showBatchDialog" title="批量添加知识点" width="500px">
+        <el-form :model="batchForm" label-width="100px">
+            <el-form-item label="知识点ID">
+                <el-input 
+                    v-model="batchForm.knowledgeIds" 
+                    type="textarea" 
+                    :rows="4"
+                    placeholder="请输入知识点ID,多个ID用逗号分隔,例如:1,2,3,4"
+                />
+                <div class="text-gray-500 text-xs mt-1">支持逗号、空格、换行分隔</div>
+            </el-form-item>
+        </el-form>
+        <template #footer>
+            <div class="dialog-footer">
+                <el-button @click="showBatchDialog = false">取消</el-button>
+                <el-button type="primary" @click="handleBatchSave" :loading="batchLoading">保存</el-button>
+            </div>
+        </template>
+    </el-dialog>
     <div class="text-main mt-10 mb-3 flex items-center">
         <div :class="seqClass">2</div>试题设置
     </div>
@@ -21,9 +48,10 @@
 </template>
 
 <script setup name="QuestionIntelligent">
-
+import {ref} from 'vue'
 import {useProvidePaperQuestionCondition} from "@/views/dz/papers/hooks/usePaperQuestionCondition.js";
 import {useInjectPaperKnowledgeCondition} from "@/views/dz/papers/hooks/usePaperKnowledgeCondition.js";
+import {ElMessageBox} from "element-plus";
 
 const props = defineProps({
     exactMode: Boolean
@@ -31,9 +59,59 @@ const props = defineProps({
 const emits = defineEmits(['submit'])
 
 const seqClass = 'inline-block rounded-full bg-blue-100 w-5 h-5 text-center mr-2'
-const {knowledgeCheckNodes, removeKnowledge} = useInjectPaperKnowledgeCondition()
+const {knowledgeCheckNodes, removeKnowledge, clearAllKnowledge, batchAddKnowledge} = useInjectPaperKnowledgeCondition()
 const {qTypes} = useProvidePaperQuestionCondition(props.exactMode, false)
 
+const showBatchDialog = ref(false)
+const batchLoading = ref(false)
+const batchForm = ref({
+    knowledgeIds: ''
+})
+
+const handleClearAll = async () => {
+    if (knowledgeCheckNodes.value.length === 0) {
+        ElMessage.warning('当前没有已选知识点')
+        return
+    }
+    
+    try {
+        await ElMessageBox.confirm(
+            `确定要清空所有 ${knowledgeCheckNodes.value.length} 个已选知识点吗?`,
+            '提示',
+            {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }
+        )
+        clearAllKnowledge()
+        ElMessage.success('已清空所有知识点')
+    } catch {
+        // 用户取消
+    }
+}
+
+const handleBatchAdd = () => {
+    batchForm.value.knowledgeIds = ''
+    showBatchDialog.value = true
+}
+
+const handleBatchSave = async () => {
+    if (!batchForm.value.knowledgeIds || !batchForm.value.knowledgeIds.trim()) {
+        ElMessage.warning('请输入知识点ID')
+        return
+    }
+    
+    batchLoading.value = true
+    try {
+        await batchAddKnowledge(batchForm.value.knowledgeIds)
+        showBatchDialog.value = false
+        batchForm.value.knowledgeIds = ''
+    } finally {
+        batchLoading.value = false
+    }
+}
+
 const buildPaper = function () {
     emits('submit', qTypes)
 }

+ 71 - 1
back-ui/src/views/dz/papers/hooks/usePaperKnowledgeCondition.js

@@ -1,6 +1,8 @@
 import {createEventHook, injectLocal, provideLocal} from "@vueuse/core";
 import {getPaperKnowledges} from "@/api/dz/papers.js";
+import {getKnowledgeTree, getKnowledgeTreeBatch} from "@/api/learn/knowledgeTree.js";
 import {useInjectGlobalLoading} from "@/views/hooks/useGlobalLoading.js";
+import {ElMessage} from "element-plus";
 
 const key = Symbol('PaperKnowledgeCondition')
 export const useProvidePaperKnowledgeCondition = function () {
@@ -20,6 +22,73 @@ export const useProvidePaperKnowledgeCondition = function () {
         }
     }
 
+    // 一键清空所有知识点
+    const clearAllKnowledge = () => {
+        knowledgeCheckNodes.value = []
+        knowledgeNode.value = null
+    }
+
+    // 批量添加知识点
+    const batchAddKnowledge = async (knowledgeIdsStr) => {
+        if (!knowledgeIdsStr || !knowledgeIdsStr.trim()) {
+            ElMessage.warning('请输入知识点ID')
+            return
+        }
+
+        // 解析知识点ID字符串,支持逗号、空格、换行等分隔符
+        const ids = knowledgeIdsStr
+            .split(/[,,\s\n]+/)
+            .map(id => id.trim())
+            .filter(id => id && !isNaN(Number(id)))
+            .map(id => Number(id))
+
+        if (ids.length === 0) {
+            ElMessage.warning('请输入有效的知识点ID')
+            return
+        }
+
+        loading.value = true
+        try {
+            // 获取已存在的知识点ID集合,避免重复添加
+            const existingIds = new Set(knowledgeCheckNodes.value.map(k => k.id))
+            
+            // 过滤掉已存在的ID
+            const newIds = ids.filter(id => !existingIds.has(id))
+            
+            if (newIds.length === 0) {
+                ElMessage.warning('所有知识点已存在')
+                return
+            }
+
+            // 批量查询知识点详情
+            const res = await getKnowledgeTreeBatch(newIds)
+            
+            if (!res.data || res.data.length === 0) {
+                ElMessage.warning('没有找到新的知识点,可能ID无效')
+                return
+            }
+
+            // 过滤掉非叶子节点(有子节点的节点)
+            const newKnowledgeNodes = res.data.filter(node => {
+                return !node.children || node.children.length === 0
+            })
+
+            if (newKnowledgeNodes.length === 0) {
+                ElMessage.warning('没有找到叶子节点知识点')
+                return
+            }
+
+            // 追加到现有列表
+            knowledgeCheckNodes.value = [...knowledgeCheckNodes.value, ...newKnowledgeNodes]
+            ElMessage.success(`成功添加 ${newKnowledgeNodes.length} 个知识点`)
+        } catch (error) {
+            console.error('批量添加知识点失败:', error)
+            ElMessage.error('批量添加知识点失败')
+        } finally {
+            loading.value = false
+        }
+    }
+
     const loadKnowledge = async function(payload) {
         loading.value = true
         try {
@@ -32,7 +101,8 @@ export const useProvidePaperKnowledgeCondition = function () {
 
     const payload = {
         knowledges, knowledgeNode, knowledgeId, knowledgeCheckNodes, knowledgeIds,
-        removeKnowledge, loadKnowledge, onKnowledgeRemove: knowledgeRemoveEvent.on
+        removeKnowledge, loadKnowledge, clearAllKnowledge, batchAddKnowledge,
+        onKnowledgeRemove: knowledgeRemoveEvent.on
     }
     provideLocal(key, payload)
     return payload

+ 26 - 6
back-ui/src/views/dz/papers/hooks/usePaperQuestionCondition.js

@@ -79,10 +79,6 @@ export const useProvidePaperQuestionCondition = function (exactMode, handMode) {
     // hooks - 监听知识点变化,重新获取题型数据
     // 监听单个知识点节点和多选知识点节点的变化
     watch([() => conditionArgs.value.subjectId, knowledgeNode, knowledgeCheckNodes], async ([subjectId, knowledgeNodeVal, knowledgeCheckNodesVal]) => {
-        // clean
-        qtpye.value = ''
-        qTypes.value = []
-
         // 获取知识点ID:优先使用单个知识点,否则使用多选知识点
         const currentKnowledgeId = knowledgeNodeVal?.id
         const currentKnowledgeIds = knowledgeCheckNodesVal?.map(k => k.id) || []
@@ -109,10 +105,34 @@ export const useProvidePaperQuestionCondition = function (exactMode, handMode) {
         
         try {
             const res = await getPaperQuestionTypes(query)
-            qTypes.value = res.data || []
+            const newQTypes = res.data || []
+            
+            // 保留已有的题型设置(特别是 count 值),只更新题型列表
+            // 如果已有题型设置,则合并新旧题型,保留已有的 count
+            if (qTypes.value.length > 0) {
+                const existingTypesMap = new Map()
+                qTypes.value.forEach(qt => {
+                    existingTypesMap.set(qt.dictValue, qt.count || 0)
+                })
+                
+                // 合并新题型和已有设置
+                qTypes.value = newQTypes.map(newQt => {
+                    const existingCount = existingTypesMap.get(newQt.dictValue)
+                    return {
+                        ...newQt,
+                        count: existingCount !== undefined ? existingCount : (newQt.count || 0)
+                    }
+                })
+            } else {
+                // 如果没有已有设置,直接使用新的题型列表
+                qTypes.value = newQTypes
+            }
+            
+            // 清空选中的题型
+            qtpye.value = ''
         } catch (error) {
             console.error('获取题型列表失败:', error)
-            qTypes.value = []
+            // 出错时不清空已有设置
         }
     }, { deep: true, immediate: false })
 

+ 15 - 0
ie-admin/src/main/java/com/ruoyi/web/controller/learn/LearnKnowledgeTreeController.java

@@ -79,6 +79,21 @@ public class LearnKnowledgeTreeController extends BaseController
         return success(learnKnowledgeTreeService.selectLearnKnowledgeTreeById(id));
     }
 
+    /**
+     * 批量获取知识点树详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('learn:knowledgeTree:query')")
+    @PostMapping("/getKnowledgeByIds")
+    @ApiOperation("批量获取知识点树详细信息")
+    public AjaxResult getKnowledgeByIds(@RequestBody List<Long> ids)
+    {
+        if (ids == null || ids.isEmpty()) {
+            return AjaxResult.error("知识点ID列表不能为空");
+        }
+        List<LearnKnowledgeTree> result = learnKnowledgeTreeService.selectLearnKnowledgeTreeByIds(ids);
+        return AjaxResult.success(result);
+    }
+
     /**
      * 新增知识点树
      */

+ 9 - 1
ie-admin/src/main/java/com/ruoyi/web/controller/learn/LearnTeacherController.java

@@ -235,7 +235,15 @@ public class LearnTeacherController extends BaseController {
             public int compare(Dict o1, Dict o2) {
                 return o1.getInt("dictValue").compareTo(o2.getInt("dictValue"));
             }
-        }).collect(Collectors.toList());
+        }).collect(Collectors.collectingAndThen(
+            Collectors.toMap(
+                dict -> dict.getInt("dictValue"),
+                dict -> dict,
+                (existing, replacement) -> existing,
+                LinkedHashMap::new
+            ),
+            map -> new ArrayList<>(map.values())
+        ));
         return AjaxResult.success(dictList);
     }
 

+ 8 - 0
ie-system/src/main/java/com/ruoyi/learn/mapper/LearnKnowledgeTreeMapper.java

@@ -23,6 +23,14 @@ public interface LearnKnowledgeTreeMapper
     public LearnKnowledgeTree selectLearnKnowledgeTreeById(Long id);
     public List<LearnKnowledgeTree> selectLearnKnowledgeTreeByParentIds(@Param("ids") Collection<Long> ids);
 
+    /**
+     * 批量查询知识点树
+     * 
+     * @param ids 知识点树主键列表
+     * @return 知识点树集合
+     */
+    public List<LearnKnowledgeTree> selectLearnKnowledgeTreeByIds(@Param("ids") List<Long> ids);
+
 
     /**
      * 查询知识点树列表

+ 8 - 0
ie-system/src/main/java/com/ruoyi/learn/service/ILearnKnowledgeTreeService.java

@@ -20,6 +20,14 @@ public interface ILearnKnowledgeTreeService
      */
     public LearnKnowledgeTree selectLearnKnowledgeTreeById(Long id);
 
+    /**
+     * 批量查询知识点树
+     * 
+     * @param ids 知识点树主键列表
+     * @return 知识点树集合
+     */
+    public List<LearnKnowledgeTree> selectLearnKnowledgeTreeByIds(List<Long> ids);
+
     /**
      * 查询知识点树列表
      * 

+ 12 - 0
ie-system/src/main/java/com/ruoyi/learn/service/impl/LearnKnowledgeTreeServiceImpl.java

@@ -32,6 +32,18 @@ public class LearnKnowledgeTreeServiceImpl implements ILearnKnowledgeTreeService
         return learnKnowledgeTreeMapper.selectLearnKnowledgeTreeById(id);
     }
 
+    /**
+     * 批量查询知识点树
+     * 
+     * @param ids 知识点树主键列表
+     * @return 知识点树集合
+     */
+    @Override
+    public List<LearnKnowledgeTree> selectLearnKnowledgeTreeByIds(List<Long> ids)
+    {
+        return learnKnowledgeTreeMapper.selectLearnKnowledgeTreeByIds(ids);
+    }
+
     /**
      * 查询知识点树列表
      * 

+ 5 - 0
ie-system/src/main/resources/mapper/learn/LearnKnowledgeTreeMapper.xml

@@ -62,6 +62,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         where pid in <foreach item="id" collection="ids" open="(" separator="," close=")">#{id}</foreach>
     </select>
 
+    <select id="selectLearnKnowledgeTreeByIds" resultMap="LearnKnowledgeTreeResult">
+        <include refid="selectLearnKnowledgeTreeVo"/>
+        where id in <foreach item="id" collection="ids" open="(" separator="," close=")">#{id}</foreach>
+    </select>
+
     <insert id="insertLearnKnowledgeTree" parameterType="LearnKnowledgeTree" useGeneratedKeys="true" keyProperty="id">
         insert into learn_knowledge_tree
         <trim prefix="(" suffix=")" suffixOverrides=",">

+ 10 - 7
ie-system/src/main/resources/mapper/syzy/BBusiWishUniversitiesProfessionMapper.xml

@@ -64,20 +64,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </select>
 
     <select id="selectEnrollPlanProfessionByCode" parameterType="map" resultMap="BBusiWishUniversitiesProfessionResult">
-        SELECT distinct up.id, up.collegeCode, up.type, up.code, up.name, up.professionType, up.hot, up.ranking, up.createTime, up.updateTime, mp.`id` enrollCode, mp.`majorDirection` majorDirection  FROM `a_marjor_plan` mp
-        JOIN `b_busi_wish_universities_profession` up ON mp.`universityId` = up.`collegeCode` AND up.`name` = mp.`majorName`
+        SELECT distinct up.id, up.collegeCode, up.type, up.code, up.name, up.professionType, up.hot, up.ranking, up.createTime, up.updateTime, mp.`id` enrollCode, mp.`majorDirection` majorDirection  
+        FROM `a_marjor_plan` mp
+        INNER JOIN `b_busi_wish_universities_profession` up 
+            ON mp.`universityId` = up.`collegeCode` 
+            AND up.`name` = mp.`majorName`
+            <if test="examType != null and examType != ''"> AND up.`examType` = #{examType}</if>
         <where>
-            <if test="collegeCode != null  and collegeCode != ''"> and  mp.`universityId` = #{collegeCode}</if>
+            <if test="collegeCode != null  and collegeCode != ''"> and mp.`universityId` = #{collegeCode}</if>
             <if test="year != null"> and mp.`year` = #{year}</if>
+            <if test="examineeType != null  and examineeType != ''"> and mp.`examineeType` = #{examineeType}</if>
             <if test="code != null  and code != ''"> and up.code = #{code}</if>
             <if test="name != null  and name != ''"> and up.name like concat('%', #{name}, '%')</if>
             <if test="professionType != null  and professionType != ''"> and up.professionType = #{professionType}</if>
-            <if test="examineeType != null  and examineeType != ''"> and mp.`examineeType` = #{examineeType}</if>
-            <if test="examType != null  and examType != ''"> and up.`examType` = #{examType}</if>
-            <if test="examMajor != null  and examMajor != ''"> and examMajor = #{examMajor}</if>
+            <if test="examMajor != null  and examMajor != ''"> and up.examMajor = #{examMajor}</if>
             <if test="hot != null"> and up.hot = #{hot}</if>
         </where>
-        order by up.`ranking` asc, up.id
+        ORDER BY up.`ranking` ASC, up.id
     </select>
 
     <select id="selectBBusiWishUniversitiesProfessionById" parameterType="String" resultMap="BBusiWishUniversitiesProfessionResult">