ソースを参照

个人画像-添加全部知识点

吴朋磊 3 ヶ月 前
コミット
6f89db85bd

+ 3 - 0
src/components/FiltersItem_ruoyan.vue

@@ -24,6 +24,9 @@
       return {
       }
     },
+    mounted() {
+      console.log('filtersData:', this.filtersData);
+    },
     methods:{
 
       //筛选点击事件

+ 1 - 1
src/components/PaperImage.vue

@@ -1316,7 +1316,7 @@ export default {
                 text = item.displayName.toString();
                 textWidth = ctx.measureText(text).width;
               }else{
-                textWidth = 2 * fontSize*this.zoomRate*this.scale; ;
+                textWidth = 2 * fontSize*this.zoomRate*this.scale; 
               }
               const underlineY = obj.y + 50*this.zoomRate * this.scale ; // 可根据需要调整下划线位置
               const startX = obj.x +35*this.zoomRate*this.scale;

+ 1 - 1
src/plugins/lib-flexible/flexible.js

@@ -1,4 +1,4 @@
-;(function(win, lib) {
+(function(win, lib) {
     var doc = win.document;
     var docEl = doc.documentElement;
     var metaEl = doc.querySelector('meta[name="viewport"]');

+ 1 - 1
src/utils/scan.js

@@ -93,7 +93,7 @@ var wsc2={
                     on_offline();//处理离线逻辑方法
                 },self.interval_close);
             },self.interval_ping);//每隔 self.interval_ping 毫秒
-        };
+        }
         //离线处理逻辑  
         function on_offline(){
             

+ 66 - 37
src/views/analysisReport/personalProfile/HistoricalChangeChart.vue

@@ -54,7 +54,7 @@ export default {
         // 判断是否有数据
         hasData() {
             return this.personalList && this.personalList.length > 0 &&
-                    ((this.classList && this.classList.length > 0) ||
+                ((this.classList && this.classList.length > 0) ||
                     (this.gradeList && this.gradeList.length > 0) ||
                     (this.examName && this.examName.length > 0));
         },
@@ -160,19 +160,25 @@ export default {
             const series = [];
             const legendData = [];
             // 判断个人得分率数据是否有效(非空且包含有效值)
-            const hasPersonalData = personalList && personalList.length > 0 && personalList.some(data => data !== null && data !== undefined);
+            const hasPersonalData = personalList && personalList.length > 0 && personalList.some(data => data !== null && data !== undefined && data !== '');
             // 判断班级数据是否有效(非空且包含有效值)
-            const hasClassData = classList && classList.length > 0 && classList.some(score => score !== null && score !== undefined);
+            const hasClassData = classList && classList.length > 0 && classList.some(score => score !== null && score !== undefined && score !== '');
             // 判断年级数据是否有效(非空且包含有效值)
-            const hasGradeData = gradeList && gradeList.length > 0 && gradeList.some(data => data !== null && data !== undefined);
+            const hasGradeData = gradeList && gradeList.length > 0 && gradeList.some(data => data !== null && data !== undefined && data !== '');
 
 
             // 如果有个人得分率数据,添加个人得分率系列(使用柱状图)
             if (hasPersonalData) {
+                // 将personalList转换为数字数组,确保echarts能正确显示
+                const numericPersonalList = personalList.map(val => {
+                    const num = parseFloat(val);
+                    return isNaN(num) ? null : num;
+                });
+                
                 series.push({
                     name: '个人',
                     type: 'bar',
-                    data: personalList,
+                    data: numericPersonalList,
                     barWidth: 60,
                     itemStyle: {
                         color: '#5470C6'
@@ -190,10 +196,16 @@ export default {
 
             // 如果有班级数据,添加班级系列(折线图)
             if (hasClassData) {
+                // 将classList转换为数字数组,确保echarts能正确显示
+                const numericClassList = classList.map(val => {
+                    const num = parseFloat(val);
+                    return isNaN(num) ? null : num;
+                });
+                
                 series.push({
                     name: '班级',
                     type: 'line',
-                    data: classList,
+                    data: numericClassList,
                     itemStyle: {
                         color: '#ffffff',
                         borderColor: '#91CC75',
@@ -223,10 +235,16 @@ export default {
 
             // 如果有年级数据,添加年级系列
             if (hasGradeData) {
+                // 将gradeList转换为数字数组,确保echarts能正确显示
+                const numericGradeList = gradeList.map(val => {
+                    const num = parseFloat(val);
+                    return isNaN(num) ? null : num;
+                });
+                
                 series.push({
                     name: '年级',
                     type: 'line',
-                    data: personalList,
+                    data: numericGradeList,
                     // smooth: true,
                     itemStyle: {
                         color: '#ffffff',
@@ -264,43 +282,54 @@ export default {
                     },
                     formatter: function (params) {
                         if (params.length === 0) return '';
-                        let result = params[0].name + ':<br/>';
+                        let result = `<span style="font-size:14px; font-weight:bold;">${params[0].name}</span>` + ':<br/>';
                         params.forEach(item => {
                             let span = '';
-                            if(item.seriesName === '年级'){
-                                span = `<span 
-                                        style="width:10px;
-                                        height:10px;
-                                        display:inline-block;
+                            if (item.seriesName === '年级') {
+                                span = `<span         
+                                        style="
                                         background-color:#EE6666;
-                                        border-radius:50%;
-                                        margin-left:5px;
-                                        margin-right:10px;
+                                        width:15px;
+                                        height:2px;
+                                        display:block;
+                                        float:left;
+                                        border-radius:10px;
+                                        position:absolute;
+                                        top:10px;
+                                        left:0;
                                         "></span>`;
-                            }else if(item.seriesName === '班级'){
+                            } else if (item.seriesName === '班级') {
                                 span = `<span 
-                                        style="width:10px;
-                                        height:10px;
-                                        display:inline-block;
-                                        background-color:#91CC75;
-                                        border-radius:50%;
-                                        margin-left:5px;
-                                        margin-right:10px;
+                                        style="background-color:#91CC75;
+                                        width:15px;
+                                        height:2px;
+                                        display:block;
+                                        float:left;
+                                        border-radius:10px;
+                                        position:absolute;
+                                        top:10px;
+                                        left:0;
                                         "></span>`;
-                            }else{
+                            } else {
                                 span = `<span 
-                                        style="width:20px;
-                                        height:10px;
-                                        display:inline-block;
+                                        style="
                                         background-color:#5470C6;
-                                        border-radius:2px;
-                                        margin-right:5px;
+                                        width:10px;
+                                        height:10px;
+                                        display:block;
+                                        float:left;
+                                        position:absolute;
+                                        top:7px;
+                                        left:2px;
                                         "></span>`;
                             }
                             if (item.value !== null && item.value !== undefined && item.value !== '') {
-                                result += `${span}${item.seriesName}得分率: ${item.value}%<br/>`;
+                                result += `<div style="width:100%; margin:0; padding:0; position:relative; padding-left:20px;">${span} <span style="font-size:12px;">${item.seriesName}得分率: ${item.value}%</span></div>`;
                             } else {
-                                result += `${span}${item.seriesName}得分率: 暂无数据<br/>`;
+                                result += `<div style="width:100%; margin:0; padding:0; position:relative; padding-left:20px;">
+                                    ${span} <span style="font-size:12px;">${item.seriesName}得分率:暂无数据</span>
+                                </div>`;
+                             
                             }
                         });
                         return result;
@@ -308,7 +337,7 @@ export default {
                 },
                 legend: {
                     data: legendData,
-                    left: 80,
+                    left: 90,
                     top: -2,
                     orient: 'horizontal',
                     itemGap: 20,
@@ -464,7 +493,7 @@ export default {
     .checkbox {
         position: absolute;
         left: 0;
-        top: 0;
+        top: -2px;
         color: #333;
 
         // margin-top: 4px;
@@ -482,10 +511,10 @@ export default {
         font-size: 14px;
         color: #999999;
         border-radius: 4px;
-        span{
-          margin-top: 11%;
+
+        span {
+            margin-top: 11%;
         }
     }
 }
 </style>
-

+ 12 - 0
src/views/analysisReport/personalProfile/index.vue

@@ -355,6 +355,12 @@ export default {
                         this.highVulnerability = []; // 高频错题知识点数据
                         // 首次加载默认获取知识点
                         this.zeroloading = false;
+                        // 接口调用成功后,刷新图表
+                        this.$nextTick(() => {
+                            if (this.$refs.knowledgeGraphRef) {
+                                this.$refs.knowledgeGraphRef.updateChart();
+                            }
+                        });
                     }
                 }else{
                     this.treeData = [];
@@ -363,6 +369,12 @@ export default {
                     this.highVulnerability = []; // 高频错题知识点数据
                     // 加载状态
                     this.zeroloading = false;
+                    // 接口调用成功后,刷新图表
+                    this.$nextTick(() => {
+                        if (this.$refs.knowledgeGraphRef) {
+                            this.$refs.knowledgeGraphRef.updateChart();
+                        }
+                    });
                 }
             })
         },

+ 407 - 289
src/views/analysisReport/personalProfile/zeroScoreKnowledge.vue

@@ -1,216 +1,216 @@
 <template>
   <div class="knowledge_graph">
-    <!-- 暂无数据 -->
-    <div class="noData" 
-          v-show="this.tableData.length === 0 && 
-            this.subjectScoreRate === 0 && 
-            this.fatalVulnerability.length === 0 && 
-            this.highVulnerability.length === 0">
-      <div class="module_chart no_content_data no_data" element-loading-background="#ffffff">
-        <span>暂无数据</span>
+    <!-- 顶部切换按钮 -->
+    <div class="graph_header" v-if="mode === 'student' && activeView === 'graph'">
+      <!-- 学生模式:显示对比选择器和视图切换按钮 -->
+      <div class="comparison_selector">
+        <el-button-group>
+          <el-button v-for="option in comparisonOptions" :key="option.key"
+            :type="activeComparison === option.key ? 'primary' : 'default'" @click="activeComparison = option.key">
+            {{ option.label }}
+          </el-button>
+        </el-button-group>
+        <!-- <div class="student_position">
+          朱睿涵位置:{{ studentPosition }}
+        </div> -->
       </div>
     </div>
-    <!-- 内容区域 -->
-    <div class="graphBox" v-show="this.tableData.length !== 0 || 
-            this.subjectScoreRate !== 0 || 
-            this.fatalVulnerability.length !== 0 || 
-            this.highVulnerability.length !== 0">
-      <div class="graph_header" v-if="activeView === 'list'">
-        <!-- 自定义图例 -->
-        <div class="legend">
-          <div class="legend_item" :class="{ 'selected': selectedLegend.weak }">
-            <span class="legend_dot weak"></span>
-            <span class="legend_text">{{ '薄弱(0%≤得分率<60%)' }}</span>
-          </div>
-          <div class="legend_item" :class="{ 'selected': selectedLegend.good }">
-            <span class="legend_dot good"></span>
-            <span class="legend_text">{{ '良好(60%≤得分率<85%)' }}</span>
-          </div>
-          <div class="legend_item" :class="{ 'selected': selectedLegend.excellent }">
-            <span class="legend_dot excellent"></span>
-            <span class="legend_text">{{ '优秀(85%≤得分率)' }}</span>
-          </div>
+
+    <div class="graph_header" v-if="activeView === 'list'">
+      <!-- 自定义图例 -->
+      <div class="legend">
+        <div class="legend_item" :class="{ 'selected': selectedLegend.weak }">
+          <span class="legend_dot weak"></span>
+          <span class="legend_text">${{ '薄弱(0%≤得分率<60%)' }}</span>
         </div>
-        <!-- 视图切换按钮 - 移到knowledge_graph容器下 -->
-        <div class="view_switcher_container">
-          <el-button type="text" @click="switchView('graph')" class="switch_btn list_btn">
-            <i class="icon_switch_graph">
-              <img src="../../../assets/studentAnalysis/circleGrey.svg" alt="按图形查看">
-            </i>
-            按图形查看
-          </el-button>
-          <el-button type="graph" class="switch_btn graph_btn" @click="switchView('list')">
-            <i class="icon_switch_list">
-              <img src="../../../assets/studentAnalysis/iconBlue.svg" alt="按列表查看">
-            </i>
-            按列表查看
-          </el-button>
+        <div class="legend_item" :class="{ 'selected': selectedLegend.good }">
+          <span class="legend_dot good"></span>
+          <span class="legend_text">${{ '良好(60%≤得分率<85%)' }}</span>
+        </div>
+        <div class="legend_item" :class="{ 'selected': selectedLegend.excellent }">
+          <span class="legend_dot excellent"></span>
+          <span class="legend_text">${{ '优秀(85%≤得分率)' }}</span>
         </div>
       </div>
+      <!-- 视图切换按钮 - 移到knowledge_graph容器下 -->
+      <div class="view_switcher_container">
+        <el-button type="text" @click="switchView('graph')" class="switch_btn list_btn">
+          <i class="icon_switch_graph">
+            <img src="../../../assets/studentAnalysis/circleGrey.svg" alt="按图形查看">
+          </i>
+          按图形查看
+        </el-button>
+        <el-button type="graph" class="switch_btn graph_btn" @click="switchView('list')">
+          <i class="icon_switch_list">
+            <img src="../../../assets/studentAnalysis/iconBlue.svg" alt="按列表查看">
+          </i>
+          按列表查看
+        </el-button>
+      </div>
+    </div>
 
-      <!-- 图形查看容器 -->
-      <div v-if="activeView === 'graph'" class="graph_content">
-        <!-- 树图表 -->
-        <div class="chart_container" >
-          <!-- 顶部容器:图例和视图切换按钮 -->
-          <div class="top_container">
-            <!-- 自定义图例 -->
-            <div class="legend">
-              <!-- @click="toggleLegend('weak')"  @click="toggleLegend('good')" @click="toggleLegend('excellent')" -->
-              <div class="legend_item" :class="{ 'selected': selectedLegend.weak }">
-                <span class="legend_dot weak"></span>
-                <span class="legend_text">{{ '薄弱(0%≤得分率<60%)' }}</span>
-              </div>
-              <div class="legend_item" :class="{ 'selected': selectedLegend.good }">
-                <span class="legend_dot good"></span>
-                <span class="legend_text">{{ '良好(60%≤得分率<85%)' }}</span>
-              </div>
-              <div class="legend_item" :class="{ 'selected': selectedLegend.excellent }">
-                <span class="legend_dot excellent"></span>
-                <span class="legend_text">{{ '优秀(85%≤得分率)' }}</span>
-              </div>
+
+    <!-- 图形查看容器 -->
+    <div v-if="activeView === 'graph'" class="graph_content">
+      <!-- 左侧图表 -->
+      <div class="chart_container">
+        <!-- 顶部容器:图例和视图切换按钮 -->
+        <div class="top_container">
+          <!-- 自定义图例 -->
+          <div class="legend">
+            <!-- @click="toggleLegend('weak')"  @click="toggleLegend('good')" @click="toggleLegend('excellent')" -->
+            <div class="legend_item" :class="{ 'selected': selectedLegend.weak }">
+              <span class="legend_dot weak"></span>
+              <span class="legend_text">${{ '薄弱(0%≤得分率<60%)' }}</span>
+            </div>
+            <div class="legend_item" :class="{ 'selected': selectedLegend.good }">
+              <span class="legend_dot good"></span>
+              <span class="legend_text">${{ '良好(60%≤得分率<85%)' }}</span>
             </div>
-            <!-- 视图切换按钮  -->
-            <div class="view_switcher_container" v-if="activeView === 'graph'" :class="activeView">
-              <el-button type="graph" @click="switchView('graph')" class="switch_btn graph_btn">
-                <i class="icon_switch_graph">
-                  <img src="../../../assets/studentAnalysis/circleBlue.svg" alt="按图形查看">
-                </i>
-                按图形查看
-              </el-button>
-              <el-button type="text" class="switch_btn list_btn" @click="switchView('list')">
-                <i class="icon_switch_list"><img src="../../../assets/studentAnalysis/iconGrey.svg" alt="按列表查看"></i>
-                按列表查看
-              </el-button>
+            <div class="legend_item" :class="{ 'selected': selectedLegend.excellent }">
+              <span class="legend_dot excellent"></span>
+              <span class="legend_text">${{ '优秀(85%≤得分率)' }}</span>
             </div>
           </div>
-          <div ref="chart" class="chart" style="width: 100%;" v-show="this.tableData.length !== 0"></div>
-          
-          <!-- 暂无数据提示 -->
-          <div class="module_chart no_content_data no_data" element-loading-background="#ffffff"
-            v-if="!this.tableData.length || this.tableData.length == 0">
-            <span>暂无数据</span>
+          <!-- 视图切换按钮  -->
+          <div class="view_switcher_container" v-if="activeView === 'graph'" :class="activeView">
+            <el-button type="graph" @click="switchView('graph')" class="switch_btn graph_btn">
+              <i class="icon_switch_graph">
+                <img src="../../../assets/studentAnalysis/circleBlue.svg" alt="按图形查看">
+              </i>
+              按图形查看
+            </el-button>
+            <el-button type="text" class="switch_btn list_btn" @click="switchView('list')">
+              <i class="icon_switch_list"><img src="../../../assets/studentAnalysis/iconGrey.svg" alt="按列表查看"></i>
+              按列表查看
+            </el-button>
           </div>
-
         </div>
+        <div ref="chart" class="chart" style="width: 100%;"></div>
+      </div>
 
-        <!-- 右侧容器,零分知识点、高频知识点列表 -->
-        <div class="knowledge_right_container">
-          <!-- Tab切换 -->
-          <div class="knowledge_tab">
-            <div class="tab_item" :class="{ active: activeTab === 'zero' }" @click="activeTab = 'zero'">
-              零分知识点
-            </div>
-            <div class="tab_item" :class="{ active: activeTab === 'highFreq' }" @click="activeTab = 'highFreq'">
-              高频错题知识点
-            </div>
+      <!-- 右侧容器,包含tab和知识点列表 -->
+      <div class="knowledge_right_container">
+        <!-- Tab切换 -->
+        <div class="knowledge_tab">
+          <div class="tab_item" :class="{ active: activeTab === 'zero' }" @click="activeTab = 'zero'">
+            零分知识点
           </div>
-
-          <!-- 右侧知识点列表 -->
-          <div class="knowledge_list">
-            <!-- 暂无数据提示 -->
-            <div class="module_chart no_content_data no_data" element-loading-background="#ffffff"
-              v-if="!knowledgeItems || knowledgeItems.length === 0">
-              <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"></span>
-                <el-tooltip :content="item.knowledgeName" placement="top" effect="light"
-                  :disabled="item.knowledgeName.length < 15">
-                  <span class="item_title">{{ item.knowledgeName }}</span>
-                </el-tooltip>
-                <span class="item_tag" v-if="item.scoreRateDiff">{{ item.scoreRateDiff }} %</span>
-              </div>
-              <div class="item_info">
-                <!-- 班级/学生得分率 -->
-                <span class="item_score">
-                  <span class="score_label">得分率:</span>
-                  <span class="score_first" v-if="item.personalScoreRate">{{ item.personalScoreRate }}%</span>
-                  <span class="score_separator" v-if="item.personalScoreRate && item.classScoreRate">|</span>
-                  <span v-if="item.classScoreRate"
-                    :class="item.classScoreRate !== null ? 'score_second' : 'score_first'">{{ item.classScoreRate }}%</span>
-                </span>
-                <span class="item_exam_count">考试数: <span class="exam_count">{{ item.examNum || 0 }}</span></span>
-              </div>
-            </div>
+          <div class="tab_item" :class="{ active: activeTab === 'highFreq' }" @click="activeTab = 'highFreq'">
+            高频错题知识点
+          </div>
+          <div class="tab_item" :class="{ active: activeTab === 'all' }" @click="activeTab = 'all'">
+            全部知识点
           </div>
         </div>
 
-      </div>
-
-      <!-- 列表查看容器 -->
-      <div v-if="activeView === 'list'" class="list_content">
-
-        <vxe-table :data="tableData" style="width: 100%;" maxHeight="480px" border show-header
-          :tree-config="{ childrenField: 'children', hasChildField: 'hasChildren', showIcon: true, iconOpen: 'el-icon-remove', iconClose: 'el-icon-circle-plus' }"
-          :scroll-y="{ enabled: true, gt: 480 }" fixed-header :header-cell-style="{ color: '#333' }"
-          v-if="tableData.length !== 0">
-          <vxe-table-column type="tree" tree-node prop="title" :title="subjectName">
-            <template #default="{ row }">
-              <div class="knowledge_item">
-                <span class="knowledge_title">{{ row.knowledgeName }}</span>
-              </div>
-            </template>
-          </vxe-table-column>
-
-          <!-- 是否良好,优秀 -->
-          <vxe-table-column prop="gradeScoreRate" title="知识点状态" width="120" align="center">
-            <template #default="{ row }">
-              <div class="status_info" v-if="row.gradeScoreRate !== null">
-                <span class="status_value"
-                  :style="{ color: getRateName(row.gradeScoreRate) === '优秀' ? '#3BA272' : getRateName(row.gradeScoreRate) === '良好' ? '#FAC858' : '#EE6666' }">
-                  {{ getRateName(row.gradeScoreRate) }}
-                </span>
-              </div>
-            </template>
-          </vxe-table-column>
-
-          <!-- 只有当班级名称不是年级且不为空时,才显示班级得分率列 -->
-          <vxe-table-column prop="personalScoreRate" title="个人得分率" width="180" align="center">
-            <template #default="{ row }">
-              <div class="rate_info" v-if="row.personalScoreRate !== null">
-                <span class="rate_dot" :class="getRateClass(row.personalScoreRate)"></span>
-                <span class="rate_value">{{ row.personalScoreRate }}%</span>
-              </div>
-            </template>
-          </vxe-table-column>
-
-
-
-          <vxe-table-column prop="classScoreRate" title="班级得分率" width="180" align="center">
-            <template #default="{ row }">
-              <div class="rate_info">
-                <span class="rate_dot" :class="getRateClass(row.classScoreRate)"></span>
-                <span class="rate_value">{{ row.classScoreRate }}%</span>
-              </div>
-            </template>
-          </vxe-table-column>
-
-          <!-- 只有当班级名称不是年级且不为空时,才显示得分率差列 -->
-          <vxe-table-column prop="diff" title="得分率差" width="150" align="center">
-            <template #default="{ row }">
-              <div class="rate_info" v-if="row.scoreRateDiff !== null">
-                <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 }}%
-                </span>
-              </div>
-            </template>
-          </vxe-table-column>
-        </vxe-table>
-        <!-- 暂无数据 -->
-        <div v-if="!tableData.length || tableData.length == 0" class="noData">
-          <div class="module_chart no_content_data no_data" element-loading-background="#ffffff">
-            暂无数据
+        <!-- 右侧知识点列表 -->
+        <div class="knowledge_list">
+          <!-- 暂无数据提示 -->
+          <div class="module_chart no_content_data no_data" style="height: 240px; " element-loading-background="#ffffff"
+            v-if="!knowledgeItems || knowledgeItems.length === 0">
+            <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"></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>
+            </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_exam_count">考试数: <span class="exam_count">{{ item.examNum || 0 }}</span></span>
+            </div>
           </div>
         </div>
       </div>
     </div>
 
+    <!-- 列表查看容器 -->
+    <div v-if="activeView === 'list'" class="list_content">
+
+      <vxe-table ref="treeTable" :data="tableData" style="width: 100%;" maxHeight="480px" border show-header
+        :tree-config="{ childrenField: 'children', hasChildField: 'hasChildren', showIcon: true, iconOpen: 'el-icon-remove', iconClose: 'el-icon-circle-plus' }"
+        :scroll-y="{ enabled: true, gt: 480 }" fixed-header :header-cell-style="{ color: '#333' }"
+        @row-click="handleRowClick" @cell-click="handleCellClick" :row-class-name="rowClassName" >
+
+        <vxe-table-column type="tree" tree-node prop="title" :title="subjectName">
+          <template #default="{ row }">
+            <div class="knowledge_item">
+              <span class="knowledge_title">{{ row.knowledgeName }}</span>
+            </div>
+          </template>
+        </vxe-table-column>
+
+        <!-- 个人得分率 -->
+        <vxe-table-column  prop="personalScoreRate" title="个人得分率" width="180" align="center">
+          <template #default="{ row }">
+            <div class="rate_info" v-if="row.personalScoreRate !== null">
+              <span class="rate_dot" :class="getRateClass(row.personalScoreRate)"></span>
+              <span class="rate_value">{{ row.personalScoreRate }}%</span>
+            </div>
+          </template>
+        </vxe-table-column>
+        <!-- 个人知识点状态 -->
+        <vxe-table-column prop="personalScoreRate" title="个人知识点状态" width="120" align="center">
+          <template #default="{ row }">
+            <div class="status_info" v-if="row.personalScoreRate !== null">
+              <span class="status_value"
+                :style="{ color: getRateName(row.personalScoreRate) === '优秀' ? '#3BA272' : getRateName(row.personalScoreRate) === '良好' ? '#FAC858' : '#EE6666' }">
+                {{ getRateName(row.personalScoreRate) }}
+              </span>
+            </div>
+          </template>
+        </vxe-table-column>
+
+        <!-- 班级得分率 -->
+        <vxe-table-column  prop="classScoreRate" title="班级得分率" width="180" align="center">
+          <template #default="{ row }">
+            <div class="rate_info" v-if="row.classScoreRate !== null">
+              <span class="rate_dot" :class="getRateClass(row.classScoreRate)"></span>
+              <span class="rate_value">{{ row.classScoreRate }}%</span>
+            </div>
+          </template>
+        </vxe-table-column>
+        <!-- 班级知识点状态 -->
+        <vxe-table-column prop="classScoreRate" title="班级知识点状态" width="120" align="center">
+          <template #default="{ row }">
+            <div class="status_info" v-if="row.classScoreRate !== null">
+              <span class="status_value"
+                :style="{ color: getRateName(row.classScoreRate) === '优秀' ? '#3BA272' : getRateName(row.classScoreRate) === '良好' ? '#FAC858' : '#EE6666' }">
+                {{ getRateName(row.classScoreRate) }}
+              </span>
+            </div>
+          </template>
+        </vxe-table-column>
+
+        <!-- 只有当班级名称不是年级且不为空时,才显示得分率差列 -->
+        <vxe-table-column  prop="diff" title="得分率差" width="150"
+          align="center">
+          <template #default="{ row }">
+            <div class="rate_info" v-if="row.scoreRateDiff !== null">
+              <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 }}%
+              </span>
+            </div>
+          </template>
+        </vxe-table-column>
+      </vxe-table>
+    </div>
   </div>
 </template>
 
@@ -225,7 +225,7 @@ export default {
       type: String,
       default: 'class',
       validator: (value) => {
-        return ['class', 'grade'].includes(value);
+        return ['class', 'student'].includes(value);
       }
     },
     activeView: { // 当前视图,graph或list 年级画像还是学生画像
@@ -247,6 +247,10 @@ export default {
       type: Array,
       default: () => []
     },
+    classGroupName: { // 班级名称
+      type: String,
+      default: ''
+    },
     fatalVulnerability: { // 零分知识点数据
       type: Array,
       default: () => []
@@ -254,12 +258,23 @@ export default {
     highVulnerability: { // 高频错题知识点数据
       type: Array,
       default: () => []
+    },
+    allKnowledgeList: { // 全部知识点数据
+      type: Array,
+      default: () => []
     }
   },
   computed: {
     // 根据当前tab返回对应的数据
     knowledgeItems() {
-      return this.activeTab === 'zero' ? this.fatalVulnerability : this.highVulnerability;
+      if (this.activeTab === 'zero') {
+        return this.fatalVulnerability || [];
+      } else if (this.activeTab === 'highFreq') {
+        return this.highVulnerability || [];
+      } else if (this.activeTab === 'all') {
+        return this.allKnowledgeList || [];
+      }
+      return [];
     }
   },
   data() {
@@ -269,8 +284,17 @@ export default {
       _isMounted: false,
       // Tab切换状态
       activeTab: 'zero',
-
-
+      // 对比选择器数据
+      activeComparison: 'grade', // 默认对比年级
+      studentPosition: 'C层', // 学生位置
+      comparisonOptions: [
+        // { key: 'grade', label: '对比年级' },
+        // { key: 'a', label: '对比A层' },
+        // { key: 'b', label: '对比B层' },
+        // { key: 'c', label: '对比C层' },
+        // { key: 'd', label: '对比D层' },
+        // { key: 'e', label: '对比E层' }
+      ],
       // 图例选中状态
       selectedLegend: {
         weak: true,
@@ -278,7 +302,9 @@ export default {
         excellent: true
       },
       // 选中的知识点索引
-      selectedIndex: 0
+      selectedIndex: 0,
+      // 当前选中的行数据
+      selectedRow: null
     };
   },
   mounted() {
@@ -291,9 +317,6 @@ export default {
         this.initChart();
       });
     }
-
-    // 实现默认选中逻辑
-    // this.setDefaultSelection();
   },
   beforeDestroy() {
     // 设置组件未挂载状态
@@ -321,11 +344,12 @@ export default {
     // 监听tab切换,更新选中索引并通知父组件
     activeTab(newTab, oldTab) {
       this.selectedIndex = 0;
+
       // 只有当组件已挂载且tab值实际发生变化,并且不是初始化时,才向父组件发送事件
       // 这是为了避免在视图切换时重复调用接口
       if (this._isMounted && newTab !== oldTab) {
         // 向父组件发送tab切换事件
-        this.$emit('activeTabChange', newTab);
+        this.$emit('active-tab-change', newTab);
       }
     },
     // 监听数据变化,重新设置默认选中项
@@ -346,6 +370,15 @@ export default {
           this.setDefaultSelection();
         }
       }
+    },
+    allKnowledgeList: {
+      deep: true,
+      handler() {
+        // 仅当当前视图是图形视图且数据发生变化时才更新默认选中
+        if (this.activeView === 'graph') {
+          this.setDefaultSelection();
+        }
+      }
     }
   },
   methods: {
@@ -353,17 +386,25 @@ export default {
     setDefaultSelection() {
       // 保存当前tab值,用于比较是否需要修改
       const oldActiveTab = this.activeTab;
-      // 如果零分知识点没有数据,切换到高频错题知识点并选中第一条
-      if (this.fatalVulnerability.length === 0 && this.highVulnerability.length > 0) {
+
+      // 优先级:零分知识点 > 高频错题知识点 > 全部知识点
+      if (this.fatalVulnerability && this.fatalVulnerability.length > 0) {
+        // 如果零分知识点有数据,切换到零分知识点并选中第一条
+        if (this.activeTab !== 'zero') {
+          this.activeTab = 'zero';
+        }
+        this.selectedIndex = 0;
+      } else if (this.highVulnerability && this.highVulnerability.length > 0) {
+        // 如果零分知识点没有数据但高频错题知识点有数据,切换到高频错题知识点并选中第一条
         if (this.activeTab !== 'highFreq') {
           this.activeTab = 'highFreq';
         }
         this.selectedIndex = 0;
-      } else {
-        if (this.activeTab !== 'zero') {
-          this.activeTab = 'zero';
+      } else if (this.allKnowledgeList && this.allKnowledgeList.length > 0) {
+        // 如果前两者都没有数据但全部知识点有数据,切换到全部知识点并选中第一条
+        if (this.activeTab !== 'all') {
+          this.activeTab = 'all';
         }
-        // 否则默认选中零分知识点的第一条
         this.selectedIndex = 0;
       }
 
@@ -382,7 +423,6 @@ export default {
 
     // 根据得分率获取对应的颜色
     getNodeColor(scoreRate) {
-
       const rate = parseFloat(scoreRate);
       if (rate >= 85) {
         return '#3BA272'; // 优秀 - 绿色
@@ -475,17 +515,16 @@ export default {
             // 根据节点是否为最外围节点设置大小
             // 最外围节点(没有子节点的叶子节点)直径为32像素,其他节点直径为18像素
             const isOutermost = !item.children || item.children.length === 0;
-            const symbolSize = isOutermost ? 32 : 18;
-            const scoreRate = item.personalScoreRate;
-            // 根据班级名称选择使用的得分率字段 
+            const symbolSize = isOutermost ? 16 : 10;
+
             const node = {
               name: item.knowledgeName,
               symbolSize: symbolSize,
-              // 使用选择的得分率
-              value: scoreRate,
+              // 根据班级名称是否为年级来选择使用gradeScoreRate或classScoreRate
+              value: vm.classGroupName === '年级' ? item.gradeScoreRate : item.classScoreRate,
               itemStyle: {
-                // 确保getNodeColor接收的是正确的得分率
-                color: vm.getNodeColor(scoreRate)
+                // 根据班级名称是否为年级来选择使用gradeScoreRate或classScoreRate
+                color: vm.getNodeColor(vm.classGroupName === '年级' ? item.gradeScoreRate : item.classScoreRate)
               }
             };
 
@@ -540,7 +579,7 @@ export default {
               data: [
                 {
                   name: this.subjectName,
-                  symbolSize: 18,
+                  symbolSize: 10,
                   value: this.subjectScoreRate, // 添加value属性,用于显示得分率
                   itemStyle: {
                     // 根据得分率动态设置颜色
@@ -625,18 +664,41 @@ export default {
       }
     },
 
-    // 行样式类名方法
+    // 行样式类名方法 - vxe-table 3.7.5版本兼容
     rowClassName({ row }) {
+      // 使用selectedRow引用比较,避免修改数据
+      if (this.selectedRow && this.selectedRow.knowledgeId === row.knowledgeId) {
+        return 'selected-row';
+      }
       return row.highlight ? 'row_highlight' : '';
     },
 
     // 处理知识点列表项点击事件
-    // 向父组件发送事件,传递点击的知识点数据
     handleItemClick(item, index) {
       // 更新选中索引
       this.selectedIndex = index;
       // 向父组件发送事件,传递点击的知识点数据
       this.$emit('knowledge-item-click', { item, index });
+    },
+
+    // 处理树形表格行点击事件
+    handleRowClick(row) {
+      // 检查是否为最后一级节点(没有children或children为空)
+      const isLastLevel = !row.children || row.children.length === 0;
+
+      if (isLastLevel) {
+        // 设置当前行为选中行,不修改数据,只更新引用
+        this.selectedRow = row;
+
+        // 调用handleItemClick方法处理点击事件
+        this.handleItemClick(row, -1);
+      }
+    },
+
+    // 处理单元格点击事件
+    handleCellClick({ row }) {
+      // 调用行点击事件处理逻辑
+      this.handleRowClick(row);
     }
   }
 };
@@ -650,6 +712,23 @@ export default {
   margin-bottom: 10px;
   position: relative;
 
+  /* 选中行样式 - 确保所有情况下都能正确应用 */
+  :deep(.vxe-body--row.selected-row),
+  :deep(.selected-row) {
+    background-color: rgba(46, 100, 250, 0.1) !important;
+    color: #2E64FA !important;
+
+    /* 确保所有子元素也继承文字颜色 */
+    * {
+      color: #2E64FA !important;
+    }
+
+    /* 特殊处理评分点样式 */
+    .rate_dot {
+      border-color: rgba(46, 100, 250, 0.1) !important;
+    }
+  }
+
   .graph_header {
     margin-bottom: 20px;
     position: relative;
@@ -732,6 +811,13 @@ export default {
     justify-content: flex-end;
     font-size: 14px;
 
+    // 学生模式(学生提升)时,绝对定位在右上角
+    &.student_mode {
+      // position: absolute;
+      // top: 0;
+      // right: 25px;
+    }
+
     .switch_btn {
       display: flex;
       align-items: center;
@@ -773,6 +859,48 @@ export default {
     }
   }
 
+  // 对比选择器样式
+  .comparison_selector {
+    display: flex;
+    align-items: center;
+    gap: 10px;
+
+    .student_position {
+      font-size: 14px;
+      color: #999999;
+    }
+
+    // 对比选择器按钮样式
+    :deep(.el-button-group) {
+      .el-button {
+        padding: 7px 10px;
+        // border-radius: 4px;
+
+        &:not(.el-button--primary) {
+          color: #999999;
+          background-color: #FFFFFF;
+          border-color: #DCDFE6;
+
+          &:hover {
+            color: #2E64FA;
+            border-color: #C6E2FF;
+          }
+        }
+
+        &.el-button--primary {
+          background-color: #2E64FA;
+          color: #FFFFFF;
+          border-color: #2E64FA;
+
+          &:hover {
+            background-color: #409EFF;
+            border-color: #409EFF;
+          }
+        }
+      }
+    }
+  }
+
   .graph_content {
     display: flex;
     gap: 20px;
@@ -811,11 +939,23 @@ export default {
         &:hover {
           color: #2E64FA;
         }
+
+        // 零分知识点:根据文字宽度自适应,不需要内间距
+        // &:first-child {
+        //   padding: 0;
+        //   white-space: nowrap;
+        // }
+
+        // // 高频错题知识点:占满剩余宽度
+        // &:last-child {
+        //   padding: 0;
+        //   white-space: nowrap;
+        // }
       }
     }
 
     .knowledge_right_container {
-      width: 265px;
+      width: 310px;
       height: 630px;
       display: flex;
       flex-direction: column;
@@ -846,7 +986,7 @@ export default {
         flex-wrap: wrap; // 小屏幕下自动换行
         flex: 1;
         min-width: 0;
-        z-index: 99;
+
         .legend_item {
           display: flex;
           align-items: center;
@@ -925,31 +1065,10 @@ export default {
         font-size: 14px;
         white-space: nowrap; // 防止按钮换行
       }
-
-      /* 暂无数据样式 */
-      .no_data {
-        position: absolute;
-        top: 50%;
-        left: 50%;
-        transform: translate(-50%, -50%);
-        font-size: 16px;
-        color: #909399;
-        text-align: center;
-        width: 100%;
-        height: 220px;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        border-radius: 8px;
-        span{
-          margin-top:16%;
-        }
-      }
-
     }
 
     .knowledge_list {
-      width: 265px;
+      width: 100%;
       height: 630px;
       overflow-y: auto;
       padding-right: 10px;
@@ -984,13 +1103,14 @@ export default {
         color: #909399;
         text-align: center;
         width: 100%;
-        height: 240px;
+        height: 100%;
         display: flex;
         align-items: center;
         justify-content: center;
         border-radius: 8px;
-        span{
-          margin-top: 35%;
+
+        span {
+          margin-top: 40%;
         }
       }
 
@@ -1088,25 +1208,14 @@ export default {
     }
   }
 
+  // vxe表格样式
   .list_content {
     border-radius: 10px 10px 10px 10px;
     max-height: 480px;
-    // border: 1px solid #E9EBEF;
-    // padding: 16px;
 
     .vxe-table {
-      border-radius: 8px;
-      overflow-y: auto;
-      overflow-x: hidden;
-
-      .vxe-table--header {
-        display: table-header-group !important;
-        visibility: visible !important;
-        opacity: 1 !important;
-        height: auto !important;
-
-      }
 
+      // 展开收起的样式
       :deep(.el-icon-circle-plus) {
         color: #DCDFE6 !important;
         font-size: 16px !important;
@@ -1116,24 +1225,51 @@ export default {
         color: #2E64FA !important;
         font-size: 16px !important;
       }
+    }
+
+    /* 设置表格行高度和光标样式 */
+    :deep {
+      .vxe-table {
+        border-radius: 8px;
+        overflow-y: auto;
+        overflow-x: hidden;
+
+        // 表头样式
+        .vxe-table--header {
+          display: table-header-group !important;
+          visibility: visible !important;
+          opacity: 1 !important;
+          height: 52px !important;
+        }
+
+        /* 设置表头高度 */
+        .vxe-table--header {
+          .vxe-header--row {
+            height: 52px !important;
+          }
+        }
 
-      .vxe-table--body {
+        // 行样式
         .vxe-body--row {
-          height: 52px;
           border-bottom: 1px solid #F0F2F5;
+          cursor: pointer !important;
+          height: 52px !important;
 
           &:hover {
-            background-color: #f5f7fa;
+            background-color: #f5f7fa !important;
           }
 
           &.row_highlight {
             background-color: rgba(195, 219, 255, 0.2);
+            cursor: pointer !important;
           }
-        }
-      }
 
-      /* 树形表格样式 */
-      .vxe-table {
+          &.selected-row {
+            background-color: rgba(195, 219, 255, 0.2) !important;
+            color: #2E64FA !important;
+            cursor: pointer !important;
+          }
+        }
 
         /* 自定义树形图标样式 */
         .vxe-tree-icon {
@@ -1160,9 +1296,17 @@ export default {
           background-color: #C0C4CC;
           color: white;
         }
+
+        // 斑马纹
+        .vxe-table--body {
+          tr:nth-child(even) {
+            background-color: #F5F7FA;
+          }
+        }
       }
     }
 
+
     .knowledge_item {
       display: flex;
       align-items: center;
@@ -1213,31 +1357,5 @@ export default {
       }
     }
   }
-
-  // 暂无数据样式
-  .noData {
-    position: relative;
-    height: 220px;
-
-    /* 暂无数据样式 */
-    .no_data {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-      font-size: 16px;
-      color: #909399;
-      text-align: center;
-      width: 100%;
-      height: 100%;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      border-radius: 8px;
-      span{
-        margin-top: 11%;
-      }
-    }
-  }
 }
 </style>

+ 5 - 5
src/views/login/login.vue

@@ -59,9 +59,9 @@ export default {
     //获取用户信息
     GetUserInfo() {
       const messageHandler = event => {
-        console.log('打印父窗口传来的数据', event.data)
-        console.log('打印父窗口的源', event.origin)
-        console.log('打印当前窗口的源', window.location.origin)
+        // console.log('打印父窗口传来的数据', event.data)
+        // console.log('打印父窗口的源', event.origin)
+        // console.log('打印当前窗口的源', window.location.origin)
         const schoolType = sessionStorage.getItem('schoolType');
         //安全检查 验证消息来源
         if (event.origin !== window.location.origin) {
@@ -75,7 +75,7 @@ export default {
         }
         if (event.data.type === 'STU_TOKEN') {
           const userInfo = event.data.userInfo
-          console.log('打印用户信息', userInfo)
+          // console.log('打印用户信息', userInfo)
           this.$store.dispatch('user/CLEAR_LOCAL_STORAGE') //清空本地存储
           setToken(userInfo.tokenValue)
           sessionStorage.setItem('schoolType', schoolType || 2) //1:单校 2:联校// 暂时写死单校   线上还是单校的
@@ -84,7 +84,7 @@ export default {
           this.$store.dispatch('user/SET_SCHOOL_WEB_SITE_ID', userInfo.cloudMonitorSiteId || '') //设置学校网站id
 
           this.$store.dispatch('user/getUserInfoByToken').then(user => {
-            console.log('获取用户信息成功', this.$store.state.user.userInfo)
+            // console.log('获取用户信息成功', this.$store.state.user.userInfo)
             let userInfos = this.$store.state.user.userInfo
             // 移除事件监听器,避免重复处理
             window.removeEventListener('message', messageHandler)