Jelajahi Sumber

个人画像-知识点图谱样式效果更改

吴朋磊 2 bulan lalu
induk
melakukan
3713e91ec3

+ 1 - 6
src/views/analysisReport/personalProfile/index.vue

@@ -79,12 +79,6 @@ export default {
         knowledgeStats() {
             const stats = [];
             const prefix = ''; // 定义prefix变量
-            // 当konwLenght不等于0时,显示第一行文本
-            // if (this.knowledgeMapData.konwLenght > 0) {
-            //     stats.push(
-            //         `共包含<span style='color:#2E64FA'>${this.knowledgeMapData.konwLenght}</span>个知识点,分别为<span style='color:#333333;font-weight:600;'>${this.knowledgeMapData.knowledgeList}</span>`
-            //     ); //包含知识点
-            // }
             // 当repeatKnowledgeNum不等于0时,显示第二行文本
             if (this.knowledgeMapData.repeatKnowledgeNum > 0) {
                 // 获取得分率变化的文本部分
@@ -184,6 +178,7 @@ export default {
     methods: {
         // 我的历次成绩---年级画像
         MyGradeHistoryData() {
+            console.log(this.portraitData);
             // 加载状态-清空数据
             this.historyloading = true;
             // 历次考试知识点追踪-加载状态

+ 479 - 90
src/views/analysisReport/personalProfile/zeroScoreKnowledge.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="knowledge_graph">
     <!-- 顶部切换按钮 -->
-    <div class="graph_header" v-if="mode === 'student' && activeView === 'graph'">
+    <div class="graph_header" >
       <!-- 学生模式:显示对比选择器和视图切换按钮 -->
       <div class="comparison_selector">
         <el-button-group>
@@ -10,9 +10,6 @@
             {{ option.label }}
           </el-button>
         </el-button-group>
-        <!-- <div class="student_position">
-          朱睿涵位置:{{ studentPosition }}
-        </div> -->
       </div>
     </div>
 
@@ -112,37 +109,36 @@
             <span>暂无数据</span>
           </div>
           <!-- 正常列表数据 -->
+           
           <div class="list_item" v-for="(item, index) in knowledgeItems" :key="index"
             @click="handleItemClick(item, index)" :class="{ active: selectedIndex === index }" v-else>
-
             <div class="item_header">
               <span class="item_dot" :style="{ backgroundColor: getDotColor(item) }"></span>
               <el-tooltip :content="item.knowledgeName" placement="top" effect="light"
                 :disabled="!item.knowledgeName || item.knowledgeName.length < 15">
                 <span class="item_title">{{ item.knowledgeName }}</span>
               </el-tooltip>
-              <span class="item_tag" v-if="item.scoreRateDiff">{{ item.scoreRateDiff }} %</span>
+              <span class="item_tag" 
+                v-if="item.scoreRateDiff" :style="{ backgroundColor: parseFloat(item.scoreRateDiff) > 0 ? '#3BA272' : '#F56C6C' }">
+                {{ item.scoreRateDiff }}%
+              </span>
             </div>
 
             <div class="item_info">
-              <!-- 班级/学生得分率 -->
-              <!-- <span class="item_score">
-                <span class="score_label">得分率:</span>
-                <span class="score_first" v-if="item.classScoreRate">{{ item.classScoreRate }}%</span>
-                <span class="score_separator" v-if="item.classScoreRate">|</span>
-                <span v-if="item.gradeScoreRate"
-                  :class="item.classScoreRate !== null ? 'score_second' : 'score_first'">{{ item.gradeScoreRate }}%</span>
-              </span> -->
-
               <!-- 班级/学生得分率 -->
               <span class="item_score">
-                <span class="score_label">得分率:</span>
+                <span class="score_label" v-if="item.personalScoreRate">个人得分率:</span>
                 <span :style="{color: getDotColor(item)}" v-if="item.personalScoreRate">{{ item.personalScoreRate }}%</span>
-                <span class="score_separator" v-if="item.personalScoreRate">|</span>
-                <span v-if="item.personalScoreRate" class="score_second">{{ item.classScoreRate }}%</span>
-                <span v-if="!item.personalScoreRate" :style="{color: getDotColor(item)}">{{ item.classScoreRate }}%</span>
+                <span class="score_separator" v-if="item.classScoreRate">|</span>
+                <span class="score_label">班级得分率:</span>
+                <span v-if="item.classScoreRate" class="score_second">{{ item.classScoreRate }}%</span>
+                <span v-if="!item.classScoreRate" :style="{color: getDotColor(item)}">{{ item.classScoreRate }}%</span>
               </span>
 
+              <!-- 班级/学生得分率 -->
+              
+
+
               <span class="item_exam_count">考试数: <span class="exam_count">{{ item.examNum || 0 }}</span></span>
             </div>
           </div>
@@ -216,7 +212,7 @@
               <span class="rate_dot" :class="getRateClass(row.scoreRateDiff)"></span>
               <span class="diff_value"
                 :class="{ 'diff_negative': row.scoreRateDiff !== null && row.scoreRateDiff < 0 }">
-                {{ row.scoreRateDiff !== null && row.scoreRateDiff >= 0 ? '+' : '' }}{{ row.scoreRateDiff }}%
+                {{ row.scoreRateDiff !== null && row.scoreRateDiff > 0 ? '+' : '' }}{{ row.scoreRateDiff }}%
               </span>
             </div>
           </template>
@@ -233,13 +229,6 @@ import * as echarts from 'echarts';
 export default {
   name: 'KnowledgeGraph',
   props: {
-    mode: {
-      type: String,
-      default: 'class',
-      validator: (value) => {
-        return ['class', 'student'].includes(value);
-      }
-    },
     activeView: { // 当前视图,graph或list 年级画像还是学生画像
       type: String,
       default: 'graph',
@@ -259,10 +248,6 @@ export default {
       type: Array,
       default: () => []
     },
-    classGroupName: { // 班级名称
-      type: String,
-      default: ''
-    },
     fatalVulnerability: { // 零分知识点数据
       type: Array,
       default: () => []
@@ -274,7 +259,19 @@ export default {
     allKnowledgeList: { // 全部知识点数据
       type: Array,
       default: () => []
-    }
+    },
+    classLevel: { // 判断是否是组合还是班级,还是年级
+      type: [String, Number],
+      default: ''
+    },
+    classGroupName: { // 班级组合名称
+      type: String,
+      default: ''
+    },
+    currentKnowledgeId: { // 当前选中的知识点ID
+      type: [String, Number],
+      default: ''
+    },
   },
   computed: {
     // 根据当前tab返回对应的数据
@@ -294,6 +291,8 @@ export default {
       chart: null, // echarts实例
       // 组件挂载状态标记
       _isMounted: false,
+      // 首次渲染标志,用于控制初始化时的透明度逻辑
+      _isFirstRender: true,
       // Tab切换状态
       activeTab: 'all',
       // 对比选择器数据
@@ -316,7 +315,11 @@ export default {
       // 选中的知识点索引
       selectedIndex: 0,
       // 当前选中的行数据
-      selectedRow: null
+      selectedRow: null,
+      // 当前选中的知识点ID,用于控制节点透明度
+      selectedKnowledgeId: '',
+      // 树形表格展开行keys
+      expandRowKeys: []
     };
   },
   mounted() {
@@ -339,8 +342,23 @@ export default {
     }
   },
   watch: {
+    // 监听classLevel变化,当对比选择器数据变化时更新对比选项
+    classLevel: {
+      handler(newVal, oldVal) {
+        
+      },
+      immediate: true
+    },
+    // 监听classGroupName变化,当班级组合名称变化时更新对比选项
+    classGroupName: {
+      handler(newVal, oldVal) {
+        if (newVal) {
+        }
+      },
+      immediate: true
+    },
     // 监听activeView变化,当切换到图形视图时重新初始化图表,切换到列表视图时销毁图表
-    activeView(newView) {
+    activeView(newView, oldView) {
       if (newView === 'graph') {
         this.$nextTick(() => {
           this.initChart();
@@ -351,17 +369,35 @@ export default {
           this.chart.dispose();
           this.chart = null;
         }
+        // 切换到列表视图时,展开所有树形节点
+        this.$nextTick(() => {
+          this.expandAllNodes();
+        });
       }
     },
     // 监听tab切换,更新选中索引并通知父组件
     activeTab(newTab, oldTab) {
       this.selectedIndex = 0;
+      
+      // 根据当前tab获取对应的数据列表
+      const currentList = this.knowledgeItems;
+      // 如果有数据,更新selectedKnowledgeId为第一条数据的ID
+      if (currentList && currentList.length > 0) {
+        this.selectedKnowledgeId = currentList[0].knowledgeId;
+        // 如果当前是图形视图且图表已初始化,更新图表选中状态
+        if (this.activeView === 'graph' && this.chart) {
+          this.updateChart();
+        }
+      } else {
+        // 如果没有数据,清空selectedKnowledgeId
+        this.selectedKnowledgeId = '';
+      }
 
       // 只有当组件已挂载且tab值实际发生变化,并且不是初始化时,才向父组件发送事件
       // 这是为了避免在视图切换时重复调用接口
       if (this._isMounted && newTab !== oldTab) {
         // 向父组件发送tab切换事件
-        this.$emit('activeTabChange', newTab);
+        this.$emit('active-tab-change', newTab);
       }
     },
     // 监听数据变化,重新设置默认选中项
@@ -391,6 +427,34 @@ export default {
           this.setDefaultSelection();
         }
       }
+    },
+    // 监听tableData变化,更新展开行
+    tableData: {
+      deep: true,
+      handler() {
+        // 当tableData变化时,重新展开所有节点
+        if (this.activeView === 'list') {
+          this.$nextTick(() => {
+            this.expandAllNodes();
+          });
+        }
+        if (this.chart) {
+          this.$nextTick(() => {
+            this.updateChart();
+          });
+        }
+      }
+    },
+    // 监听当前选中的知识点ID变化,更新图表高亮状态
+    currentKnowledgeId: {
+      handler(newVal) {
+        // 更新selectedKnowledgeId,确保图表使用正确的高亮状态
+        this.selectedKnowledgeId = newVal;
+        // 仅当当前视图是图形视图且图表已初始化时才更新
+        if (this.activeView === 'graph' && this.chart) {
+          this.updateChart();
+        }
+      }
     }
   },
   methods: {
@@ -398,27 +462,41 @@ export default {
     setDefaultSelection() {
       // 保存当前tab值,用于比较是否需要修改
       const oldActiveTab = this.activeTab;
+      let selectedItem = null;
 
-      // 优先级:全部知识点 > 高频错题知识点 > 零分知识点
+      // 优先级:全部知识点> 高频错题知识点 > 零分知识点 
       if (this.allKnowledgeList && this.allKnowledgeList.length > 0) {
         // 如果前两者都没有数据但全部知识点有数据,切换到全部知识点并选中第一条
         if (this.activeTab !== 'all') {
           this.activeTab = 'all';
         }
         this.selectedIndex = 0;
-      }else if (this.highVulnerability && this.highVulnerability.length > 0) {
-        // 如果零分知识点没有数据但高频错题知识点有数据,切换到高频错题知识点并选中第一条
+        selectedItem = this.allKnowledgeList[0];
+      } else if (this.highVulnerability && this.highVulnerability.length > 0) {
+        // 如果全部知识点没有数据但高频错题知识点有数据,切换到高频错题知识点并选中第一条
         if (this.activeTab !== 'highFreq') {
           this.activeTab = 'highFreq';
         }
         this.selectedIndex = 0;
-      }else if (this.fatalVulnerability && this.fatalVulnerability.length > 0) {
+        selectedItem = this.highVulnerability[0];
+      } else if (this.fatalVulnerability && this.fatalVulnerability.length > 0) {
         // 如果零分知识点有数据,切换到零分知识点并选中第一条
         if (this.activeTab !== 'zero') {
           this.activeTab = 'zero';
         }
         this.selectedIndex = 0;
-      }  
+        selectedItem = this.fatalVulnerability[0];
+      } 
+      
+      // 如果有选中项,更新selectedKnowledgeId,实现初始化默认选择
+      if (selectedItem) {
+        this.selectedKnowledgeId = selectedItem.knowledgeId;
+        // 如果当前是图形视图且图表已初始化,更新图表选中状态
+        if (this.activeView === 'graph' && this.chart) {
+          this.updateChart();
+        }
+      }
+      
       // 只有当tab值实际发生变化时,才触发选中事件
       // 初始化时不触发,避免重复调用接口
       if (this._isMounted && oldActiveTab !== this.activeTab) {
@@ -426,12 +504,77 @@ export default {
       }
     },
 
+    // 递归收集所有节点的key,用于展开所有树形节点
+    collectAllRowKeys() {
+      const keys = [];
+      
+      // 递归函数,收集所有有children的节点key
+      const collectKeys = (data) => {
+        if (!data || !Array.isArray(data)) return;
+        
+        data.forEach(item => {
+          // 如果节点有children,将其key添加到keys数组中
+          if (item.children && item.children.length > 0) {
+            // 使用knowledgeId作为key
+            keys.push(item.knowledgeId);
+            // 递归处理children
+            collectKeys(item.children);
+          }
+        });
+      };
+      
+      // 调用递归函数
+      collectKeys(this.tableData);
+      
+      // 更新expandRowKeys数组
+      this.expandRowKeys = keys;
+    },
+
+    // 使用vxe-table API展开所有树形节点
+    expandAllNodes() {
+      // 获取vxe-table实例
+      const treeTable = this.$refs.treeTable;
+      if (treeTable) {
+        // 递归收集所有需要展开的节点
+        const expandNodes = [];
+        const collectExpandNodes = (data) => {
+          if (!data || !Array.isArray(data)) return;
+          data.forEach(item => {
+            if (item.children && item.children.length > 0) {
+              expandNodes.push(item);
+              collectExpandNodes(item.children);
+            }
+          });
+        };
+        collectExpandNodes(this.tableData);
+        
+        // 使用vxe-table 3.7.5兼容的API展开所有节点
+        if (treeTable.setExpandRows) {
+          // 如果支持setExpandRows方法
+          treeTable.setExpandRows(expandNodes);
+        } else if (treeTable.setTreeExpand) {
+          // 如果支持setTreeExpand方法
+          expandNodes.forEach(node => {
+            treeTable.setTreeExpand(node, true);
+          });
+        }
+      }
+    },
+
     // 切换查看方式
     switchView(view) {
       // 通过emit事件通知父组件更新activeView
       this.$emit('view-change', view);
     },
 
+    // 年级、班级组合判断
+    classIsGroup() {
+      if (this.classLevel == 1) {
+        return true;
+      }else
+      return false;
+    },
+
     // 根据得分率获取对应的颜色
     getNodeColor(scoreRate) {
       const rate = parseFloat(scoreRate);
@@ -516,31 +659,51 @@ export default {
         };
 
         // 基于表格数据生成树形图数据
-        const generateTreeData = (data, level = 0) => {
+        // 添加一个可选参数overrideKnowledgeId,用于临时覆盖当前的currentKnowledgeId
+        const generateTreeData = (data, level = 0, overrideKnowledgeId = null) => {
+          // 使用传入的overrideKnowledgeId或默认使用vm.selectedKnowledgeId
+          const currentKnowledgeId = overrideKnowledgeId !== null ? overrideKnowledgeId : vm.selectedKnowledgeId;
+          
           return data.filter(item => {
             // 获取节点级别
-            const nodeLevel = getNodeLevel(item.gradeScoreRate);
+            const nodeLevel = vm.classLevel === 0 ? getNodeLevel(item.gradeScoreRate) : getNodeLevel(item.classScoreRate);
             // 根据图例选中状态决定是否显示节点
             return vm.selectedLegend[nodeLevel];
           }).map(item => {
-            // 根据节点是否为最外围节点设置大小
-            // 最外围节点(没有子节点的叶子节点)直径为32像素,其他节点直径为18像素
+            // 根据节点是否为最外围节点
             const isOutermost = !item.children || item.children.length === 0;
-            const symbolSize = isOutermost ? 16 : 10;
-
+            
+            // 检查当前节点是否匹配选中的知识点ID
+            const isMatched = isOutermost && String(item.knowledgeId) === String(currentKnowledgeId);
+            
+            // 设置节点大小:选中状态16px,未选中状态10px,非叶子节点5px
+            let symbolSize = 5;
+            if (isOutermost) {
+              symbolSize = isMatched ? 15 : 10;
+            }
+            
+            // 计算节点样式
+            const itemColor = isOutermost ? 
+              vm.getNodeColor(vm.classLevel === 0 || vm.classLevel === 1 ? item.gradeScoreRate : item.classScoreRate) : 
+              '#BFC1C7';
+            
             const node = {
               name: item.knowledgeName,
               symbolSize: symbolSize,
               // 根据班级名称是否为年级来选择使用gradeScoreRate或classScoreRate
-              value: vm.classGroupName === '年级' ? item.gradeScoreRate : item.classScoreRate,
+              value: vm.classLevel === 0 || vm.classLevel === 1 ? item.gradeScoreRate : item.classScoreRate,
               itemStyle: {
-                // 根据班级名称是否为年级来选择使用gradeScoreRate或classScoreRate
-                color: vm.getNodeColor(vm.classGroupName === '年级' ? item.gradeScoreRate : item.classScoreRate)
-              }
+                color: itemColor,
+                opacity: 1
+              },
+              // 添加isOutermost属性用于tooltip判断
+              isOutermost: isOutermost,
+              // 保存知识ID用于匹配
+              knowledgeId: item.knowledgeId
             };
 
             if (item.children && item.children.length > 0) {
-              node.children = generateTreeData(item.children, level + 1);
+              node.children = generateTreeData(item.children, level + 1, overrideKnowledgeId);
             }
 
             return node;
@@ -551,12 +714,23 @@ export default {
         const option = {
           tooltip: {
             formatter: (params) => {
-              // 只有根节点(treePathInfo存在且长度为1)使用组件的subjectScoreRate作为得分率
+              // 检查是否为根节点
               if (params.treePathInfo && params.treePathInfo.length === 1) {
-                return `${params.name}<br/>得分率: ${this.subjectScoreRate}%`;
+                // 根节点也属于非最外层节点,只显示名称
+                return params.name;
+              }
+              
+              // 检查节点是否为最外层节点(叶子节点)
+              // 使用isOutermost属性或检查data.isOutermost
+              const isOutermost = params.data && params.data.isOutermost;
+              
+              if (isOutermost) {
+                // 最外层节点显示完整信息(名称+得分率)
+                return `${params.name}<br/>得分率: ${params.value !== null && params.value !== undefined ? params.value : '0'}%`;
+              } else {
+                // 非最外层节点只显示名称
+                return params.name;
               }
-              // 其他节点(包括treePathInfo不存在的情况)都使用value作为得分率
-              return `${params.name}<br/>得分率: ${params.value !== null && params.value !== undefined ? params.value : '0'}%`;
             }
           },
           animationDurationUpdate: 1500,
@@ -573,7 +747,7 @@ export default {
               roam: true,
 
               // 使用基础配置实现居中
-              center: ['10%', '8%'],
+              center: ['10%', '7%'],
 
               zoom: this.getZoomValue(),
               scaleLimit: {
@@ -590,14 +764,14 @@ export default {
               data: [
                 {
                   name: this.subjectName,
-                  symbolSize: 10,
+                  symbolSize: 5,
                   value: this.subjectScoreRate, // 添加value属性,用于显示得分率
                   itemStyle: {
-                    // 根据得分率动态设置颜色
-                    color: this.subjectScoreRate >= 85 ? '#3BA272' : // 优秀 - 绿色
-                      this.subjectScoreRate >= 60 ? '#FAC858' : // 良好 - 黄色
-                        '#EE6666' // 薄弱 - 红色
+                    // 根节点为非最外层节点,使用#EBEEF5颜色
+                    color: '#BFC1C7'
                   },
+                  // 标记为非最外层节点
+                  isOutermost: false,
                   children: generateTreeData(this.tableData)
                 }
               ],
@@ -606,13 +780,13 @@ export default {
               },
               lineStyle: {
                 color: '#ECEEF3',
-                width: 2,
+                width: 1,
                 type: 'solid'
               },
               emphasis: {
                 focus: 'adjacency',
                 lineStyle: {
-                  width: 2
+                  width: 1
                 }
               }
             }
@@ -624,6 +798,195 @@ export default {
 
         // 初始化后调用resize确保图表正确显示
         this.chart.resize();
+        
+        // 添加图表点击事件监听
+        this.chart.on('click', (params) => {
+          // 明确识别点击类型
+          const isOuterNodeClick = params.data && params.data.isOutermost;
+          const isEmptyAreaClick = !params.data || !params.componentType || params.componentType === '';
+          const isNonOuterNodeClick = params.data && !params.data.isOutermost;
+          
+          if (isOuterNodeClick) {
+            // 处理最外层节点点击
+            // 确定当前显示的列表数据
+            let targetList = [];
+            if (this.activeTab === 'zero') {
+              targetList = this.fatalVulnerability;
+            } else if (this.activeTab === 'highFreq') {
+              targetList = this.highVulnerability;
+            } else {
+              targetList = this.allKnowledgeList;
+            }
+            
+            // 1. 首先尝试直接从当前列表中根据名称查找
+            let knowledgeItem = targetList.find(item => item.knowledgeName === params.name);
+            let targetIndex = -1;
+            
+            if (knowledgeItem) {
+              targetIndex = targetList.findIndex(item => item.knowledgeId === knowledgeItem.knowledgeId);
+            } 
+            
+            // 2. 如果在当前列表中找不到,尝试从其他列表中查找
+            if (!knowledgeItem) {
+              const allLists = [this.allKnowledgeList, this.highVulnerability, this.fatalVulnerability];
+              for (const list of allLists) {
+                knowledgeItem = list.find(item => item.knowledgeName === params.name);
+                if (knowledgeItem) {
+                  // 找到后切换到对应tab
+                  if (list === this.highVulnerability) {
+                    this.activeTab = 'highFreq';
+                    targetList = this.highVulnerability;
+                  } else if (list === this.fatalVulnerability) {
+                    this.activeTab = 'zero';
+                    targetList = this.fatalVulnerability;
+                  } else {
+                    this.activeTab = 'all';
+                    targetList = this.allKnowledgeList;
+                  }
+                  targetIndex = targetList.findIndex(item => item.knowledgeId === knowledgeItem.knowledgeId);
+                  break;
+                }
+              }
+            }
+            
+            // 3. 如果还是找不到,尝试从tableData中查找
+            if (!knowledgeItem) {
+              const findKnowledgeItem = (data) => {
+                for (let item of data) {
+                  if (item.knowledgeName === params.name) {
+                    return item;
+                  }
+                  if (item.children && item.children.length > 0) {
+                    const found = findKnowledgeItem(item.children);
+                    if (found) return found;
+                  }
+                }
+                return null;
+              };
+              
+              knowledgeItem = findKnowledgeItem(this.tableData);
+              
+              if (knowledgeItem) {
+                // 在所有列表中查找匹配的knowledgeId
+                const allLists = [this.allKnowledgeList, this.highVulnerability, this.fatalVulnerability];
+                for (const list of allLists) {
+                  targetIndex = list.findIndex(item => {
+                    // 考虑类型转换,确保比较准确
+                    return String(item.knowledgeId) === String(knowledgeItem.knowledgeId);
+                  });
+                  if (targetIndex !== -1) {
+                    // 切换到对应tab
+                    if (list === this.highVulnerability) {
+                      this.activeTab = 'highFreq';
+                      targetList = this.highVulnerability;
+                    } else if (list === this.fatalVulnerability) {
+                      this.activeTab = 'zero';
+                      targetList = this.fatalVulnerability;
+                    } else {
+                      this.activeTab = 'all';
+                      targetList = this.allKnowledgeList;
+                    }
+                    break;
+                  }
+                }
+              }
+            }
+            
+            if (knowledgeItem && targetIndex !== -1) {
+              // 向父组件发送事件
+              this.$emit('knowledge-item-click', { 
+                item: knowledgeItem,
+                index: targetIndex 
+              });
+              
+              // 更新选中索引和选中知识点ID
+              this.selectedIndex = targetIndex;
+              this.selectedKnowledgeId = knowledgeItem.knowledgeId;
+              
+              // 将首次渲染标志设为false,后续点击操作将应用透明度降低逻辑
+              // this._isFirstRender = false;
+              
+              // 重新生成图表数据,确保只有当前选中节点高亮
+              this.chart.setOption({
+                series: [{
+                  data: [
+                    {
+                      name: this.subjectName,
+                      symbolSize: 5,
+                      value: this.subjectScoreRate,
+                      itemStyle: {
+                        color: '#BFC1C7',
+                        opacity: 1
+                      },
+                      isOutermost: false,
+                      children: generateTreeData(this.tableData, 0, this.selectedKnowledgeId)
+                    }
+                  ]
+                }]
+              }, {
+                animation: true,
+                animationDuration: 300
+              });
+              
+              // 滚动到选中项
+              this.$nextTick(() => {
+                const listContainer = this.$el.querySelector('.knowledge_list');
+                const listItems = listContainer.querySelectorAll('.list_item');
+                if (listItems[targetIndex]) {
+                  listContainer.scrollTop = listItems[targetIndex].offsetTop - 100;
+                }
+              });
+            } else {
+              // 打印详细调试信息
+              console.log('点击处理调试信息:', {
+                paramsName: params.name,
+                params: params,
+                knowledgeItem: knowledgeItem,
+                targetIndex: targetIndex,
+                activeTab: this.activeTab,
+                targetListLength: targetList.length,
+                targetListSample: targetList.slice(0, 3),
+                tableDataSample: this.tableData.slice(0, 1)
+              });
+            }
+          }
+          else if (isEmptyAreaClick || isNonOuterNodeClick) {
+            // 点击空白区域或非最外层节点,恢复所有节点透明度
+            // 清空selectedKnowledgeId,确保所有节点都高亮
+            this.selectedKnowledgeId = '';
+            
+            // 调用generateTreeData时传入overrideKnowledgeId为空字符串,确保所有节点都不透明
+            const updatedOption = {
+              series: [{
+                data: [
+                  {
+                    name: this.subjectName,
+                    symbolSize: 5,
+                    value: this.subjectScoreRate,
+                    itemStyle: {
+                      color: '#BFC1C7',
+                      opacity: 1
+                    },
+                    isOutermost: false,
+                    // 传入overrideKnowledgeId为空字符串,确保生成的所有节点都不透明
+                    children: generateTreeData(this.tableData, 0, '')
+                  }
+                ]
+              }]
+            };
+            // 更新图表,使用动画实现平滑过渡
+            this.chart.setOption(updatedOption, {
+              animation: true,
+              animationDuration: 300
+            });
+            
+            // 向父组件发送事件,清除选中的知识点ID
+            this.$emit('knowledge-item-click', { 
+              item: null,
+              index: -1 
+            });
+          }
+        });
 
         // 监听窗口大小变化
         window.addEventListener('resize', () => {
@@ -688,8 +1051,15 @@ export default {
     handleItemClick(item, index) {
       // 更新选中索引
       this.selectedIndex = index;
+      // 更新选中知识点ID,实现反向选择功能
+      this.selectedKnowledgeId = item.knowledgeId;
       // 向父组件发送事件,传递点击的知识点数据
       this.$emit('knowledge-item-click', { item, index });
+      
+      // 如果当前是图形视图且图表已初始化,更新图表选中状态
+      if (this.activeView === 'graph' && this.chart) {
+        this.updateChart();
+      }
     },
 
     // 处理树形表格行点击事件
@@ -836,13 +1206,6 @@ export default {
     justify-content: flex-end;
     font-size: 14px;
 
-    // 学生模式(学生提升)时,绝对定位在右上角
-    &.student_mode {
-      // position: absolute;
-      // top: 0;
-      // right: 25px;
-    }
-
     .switch_btn {
       display: flex;
       align-items: center;
@@ -1135,7 +1498,7 @@ export default {
         border-radius: 8px;
 
         span {
-          margin-top: 40%;
+          margin-top: 23%;
         }
       }
 
@@ -1182,7 +1545,7 @@ export default {
           }
 
           .item_tag {
-            background: #F56C6C;
+            
             color: #FFFFFF;
             font-size: 12px;
             padding: 4px 6px;
@@ -1236,7 +1599,7 @@ export default {
   // vxe表格样式
   .list_content {
     border-radius: 10px 10px 10px 10px;
-    max-height: 480px;
+
 
     .vxe-table {
 
@@ -1258,6 +1621,9 @@ export default {
         border-radius: 8px;
         overflow-y: auto;
         overflow-x: hidden;
+        
+        // 使用CSS变量设置全局行高
+        --vxe-ui-table-row-height-default: 20px !important;
 
         // 表头样式
         .vxe-table--header {
@@ -1274,27 +1640,50 @@ export default {
           }
         }
 
-        // 行样式
-        .vxe-body--row {
-          border-bottom: 1px solid #F0F2F5;
-          cursor: pointer !important;
-          height: 52px !important;
-
-          &:hover {
-            background-color: #f5f7fa !important;
-          }
+        // 行样式 - 使用更具体的选择器
+        .vxe-table--body {
+          // 表格行样式
+          tbody {
+            tr {
+              border-bottom: 1px solid #F0F2F5;
+              cursor: pointer !important;
+              height: 20px !important;
+              line-height: 20px !important;
+              min-height: 20px !important;
+              max-height: 20px !important;
+
+              &:hover {
+                background-color: #f5f7fa !important;
+              }
 
-          &.row_highlight {
-            background-color: rgba(195, 219, 255, 0.2);
-            cursor: pointer !important;
-          }
+              &.row_highlight {
+                background-color: rgba(195, 219, 255, 0.2);
+                cursor: pointer !important;
+              }
 
-          &.selected-row {
-            background-color: rgba(195, 219, 255, 0.2) !important;
-            color: #2E64FA !important;
-            cursor: pointer !important;
+              &.selected-row {
+                background-color: rgba(195, 219, 255, 0.2) !important;
+                color: #2E64FA !important;
+                cursor: pointer !important;
+              }
+            }
           }
         }
+        
+        // 单元格样式,设置内边距为0,确保行高由行高属性控制
+        .vxe-body--column,
+        .vxe-header--column {
+          padding: 0 !important;
+          height: 20px !important;
+          line-height: 20px !important;
+          min-height: 20px !important;
+          max-height: 20px !important;
+        }
+        
+        // 表格主体最小高度设置
+        .vxe-table--body {
+          min-height: 0 !important;
+        }
 
         /* 自定义树形图标样式 */
         .vxe-tree-icon {

+ 1 - 1
version.json

@@ -1,5 +1,5 @@
 {
-  "version": "0.1.3_2026_1_12_0",
+  "version": "0.1.3_2026_3_26_0",
   "content": [
     {
       "time": "2024-11-1",