<template>
  <div class="circularDiffusion">
    <!-- 教师端样式 -->
    <div class="knowledge_class">
      <!-- 图形查看容器 -->
      <div  class="graph_content">
        <!-- 左侧图表 -->
        <div class="chart_container">
          <!-- 顶部容器：图例和视图切换按钮 -->
          <div class="top_container">
            <!-- 自定义图例 -->
            <div class="legend">
              <div class="legend_item" :class="{ 'selected': selectedLegend.weak }" @click="toggleLegend('weak')">
                <span class="legend_dot weak"></span>
                <span class="legend_text">{{ mode === 'student' && activeComparison ? '薄弱(得分率差值<-10%)'
                  : '薄弱(0%≤得分率<60%)' }}</span>
              </div>
              <div class="legend_item" :class="{ 'selected': selectedLegend.good }" @click="toggleLegend('good')">
                <span class="legend_dot good"></span>
                <span class="legend_text">
                  {{ mode === 'student' && activeComparison ? '持平(-10%≤得分率差值≤10%)' : '良好(60%≤得分率< 85 %) ' }}</span>
              </div>
              <div class="legend_item" :class="{ ' selected': selectedLegend.excellent }"
                    @click="toggleLegend('excellent')">
                    <span class="legend_dot excellent"></span>
                    <span class="legend_text">{{ mode === 'student' && activeComparison ? '优势(得分率差值>10%)' :
                      '优秀(85%≤得分率)'
                      }}</span>
              </div>
            </div>
          </div>
          <div ref="chart" class="chart" style="width: 100%;"></div>
        </div>

        <!-- 右侧容器 -->
        <div class="knowledge_right_container">
          <!-- 折线图 -->
          <div class="line_chart_container">
            <!-- 标题栏（放到折线图上方）：左右布局，左侧上下结构 -->
            <div class="chart_title" v-if="selectedKnowledgeName">
              <div class="chart_title_left">
                <span class="title_text" :title="selectedKnowledgeName">{{ selectedKnowledgeName }}</span>
                <span class="exam_freq">考频：{{ filteredTableData.length }}</span>
              </div>
              <el-button
                class="add_practice_btn"
                :class="addPracticeBtnThemeClass"
                :disabled="selectedIsJoin"
                @click="handleAddPractice">
                {{ selectedIsJoin ? '已加入练习' : '加入练习' }}
              </el-button>
            </div>
            <div ref="lineChart" class="line_chart"></div>
          </div>

          <!-- Tab切换和表格 -->
          <div class="table_container">
            <el-radio-group v-model="activeTableTab" class="table_tabs_radio">
              <el-radio-button label="all">全部</el-radio-button>
              <el-radio-button label="exam">考试</el-radio-button>
              <el-radio-button label="practice">练习</el-radio-button>
            </el-radio-group>

            <el-table :data="filteredTableData" border stripe class="data_table">
              <el-table-column prop="index" label="序号" width="60" align="center">
                <template slot-scope="scope">
                  {{ scope.$index + 1 }}
                </template>
              </el-table-column>
              <el-table-column prop="examName" label="考试名称" show-overflow-tooltip align="center"></el-table-column>
              <el-table-column prop="examType" label="类型" width="100" align="center"></el-table-column>
              <el-table-column prop="scoreRate" label="得分率" width="100" align="center">
                <template slot-scope="scope">
                  <span :style="{ color: getCurrentColor() }">{{ scope.row.scoreRate }}%</span>
                </template>
              </el-table-column>
            </el-table>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>

// 引入echarts
import * as echarts from 'echarts';

export default {
  name: 'CircularDiffusion',
  props: {
    studentName: {
      type: String,
      default: ''
    },
    mode: {
      type: String,
      default: 'class',
      validator: (value) => {
        return ['class', 'student'].includes(value);
      }
    },
    subjectName: {
      type: String,
      default: '知识点'
    },
    subjectScoreRate: {
      type: Number,
      default: 0
    },
    tableData: {
      type: Array,
      default: () => []
    },
    fatalVulnerability: {
      type: Array,
      default: () => []
    },
    highVulnerability: {
      type: Array,
      default: () => []
    },
    allKnowledgeList: {
      type: Array,
      default: () => []
    },
    isRecordKnowledge: {
      type: Boolean,
      default: false
    },
    studentKnowledgeLayering: {
      type: String,
      default: ''
    },
    classLevel: {
      type: [String, Number],
      default: ''
    },
    classGroupName: {
      type: String,
      default: ''
    },
    historicalChangeData: {
      type: Object,
      default: () => ({
        examNames: [],
        scores: [],
        trendData: [],
        personalScoreRate: []
      })
    },
    historicalChangeLoading: {
      type: Boolean,
      default: false
    },
    currentKnowledgeId: {
      type: [String, Number],
      default: ''
    },
    scoreRateTypes: {
      type: Array,
      default: () => []
    },
    comparisonOptions: {
      type: Array,
      default: null
    },
    isPush: {
      type: Boolean,
      default: false
    },
    // 父级已成功"加入练习"的知识点ID映射：{ [knowledgeId]: true }
    addedKnowledgeIds: {
      type: Object,
      default: () => ({})
    },
  },
  computed: {
    // 从扁平 allKnowledgeList（或tableData递归兜底）获取当前选中节点的完整对象
    currentKnowledgeItem() {
      if (!this.selectedKnowledgeId) return null;
      const sid = String(this.selectedKnowledgeId);
      if (Array.isArray(this.allKnowledgeList) && this.allKnowledgeList.length) {
        const hit = this.allKnowledgeList.find(item => String(item.knowledgeId) === sid);
        if (hit) return hit;
      }
      // 再从树形 tableData 递归找一次兜底（扁平列表可能为空但树形有数据）
      const findInTree = (arr) => {
        for (const item of arr) {
          if (String(item.knowledgeId) === sid) return item;
          if (Array.isArray(item.children) && item.children.length) {
            const found = findInTree(item.children);
            if (found) return found;
          }
        }
        return null;
      };
      return findInTree(this.tableData || []);
    },
    // 选中的知识点名称
    selectedKnowledgeName() {
      const item = this.currentKnowledgeItem;
      return item ? (item.knowledgeName ?? '') : '';
    },
    // 当前选中知识点是否"已加入练习"
    // 优先级：1) 父级 addedKnowledgeIds（当前会话刚点按钮成功，立即生效）→ 2) 节点自身 isJoin===1（接口返回的静态字段）
    selectedIsJoin() {
      if (this.selectedKnowledgeId
        && this.addedKnowledgeIds
        && Object.prototype.hasOwnProperty.call(this.addedKnowledgeIds, String(this.selectedKnowledgeId))) {
        return true;
      }
      const item = this.currentKnowledgeItem;
      return item ? (Number(item.isJoin) === 1) : false;
    },
    // 加入练习按钮主题 class（按当前选中圆点的类型匹配颜色）
    //   与 getCurrentColor() / getNodeColor() 严格对齐：
    //     knowledgeType=3 优秀/扎实巩固 → #3BA272
    //     knowledgeType=2 一般/能力提升 → #FAC858
    //     knowledgeType=1 薄弱          → #EE6666
    addPracticeBtnThemeClass() {
      const t = Number(this.selectedKnowledgeType);
      if (t === 3) return 'btn_theme_excellent';
      if (t === 2) return 'btn_theme_good';
      return 'btn_theme_weak';
    },
    // 选中知识点的考频
    selectedExamFreq() {
      const item = this.currentKnowledgeItem;
      if (item) {
        // 兼容常见考频字段名
        return item.examFrequency ?? item.examFreq ?? item.freq ?? item.frequency ?? item.count ?? '--';
      }
      return '--';
    },
    // 当前选中节点的考试/练习明细数组（来自后端 examList 字段）
    // 每个元素结构：{ dataName, dataType, dataRate }
    selectedExamList() {
      const item = this.currentKnowledgeItem;
      if (item && Array.isArray(item.examList)) {
        return item.examList.filter(e => e); // 过滤空项
      }
      return [];
    },
    // 筛选表格数据：全部 / 考试(dataType=1) / 练习(dataType=2)
    // 并将 examList 字段映射为表格列需要的字段：
    //   dataName -> examName, dataType -> examType/type, dataRate -> scoreRate
    filteredTableData() {
      const list = (this.selectedExamList || []).map(item => {
        const dataTypeNumber = Number(item.dataType);
        const examTypeLabel = dataTypeNumber === 1 ? '考试' : (dataTypeNumber === 2 ? '练习' : '');
        const typeKey = dataTypeNumber === 1 ? 'exam' : 'practice';
        return {
          examName: item.dataName ?? '',
          examType: item.examType ?? examTypeLabel,
          scoreRate: item.dataRate ?? 0,
          type: typeKey,
          dataType: dataTypeNumber,
          dataRate: item.dataRate
        };
      });
      if (this.activeTableTab === 'all') {
        return list;
      }
      if (this.activeTableTab === 'exam') {
        return list.filter(row => row.dataType === 1 || row.type === 'exam');
      }
      return list.filter(row => row.dataType === 2 || row.type === 'practice');
    }
  },
  data() {
    return {
      chart: null,
      lineChart: null,
      _isMounted: false,
      _isFirstRender: true,
      activeComparison: null,
      selectedLegend: {
        weak: true,
        good: true,
        excellent: true
      },
      legendNumbers: [1, 2, 3],
      selectedIndex: 0,
      selectedKnowledgeId: '',
      selectedKnowledgeType: 3,
      chartRefreshKey: 0,
      showKnowledgePrompt: this.isRecordKnowledge,
      activeTableTab: 'all'
    };
  },
  mounted() {
    this._isMounted = true;
    this.$nextTick(() => {
      this.initChart();
      this.initLineChart();
    });
  },
  beforeDestroy() {
    this._isMounted = false;
    if (this.chart) {
      this.chart.dispose();
    }
    if (this.lineChart) {
      this.lineChart.dispose();
    }
  },
  watch: {
    // 当知识点列表数据变化时，更新选中项的类型与折线图/表格
    allKnowledgeList: {
      handler(newVal) {
        if (newVal && newVal.length > 0 && this.selectedKnowledgeId) {
          const item = newVal.find(item => String(item.knowledgeId) === String(this.selectedKnowledgeId));
          if (item) {
            this.selectedKnowledgeType = item.knowledgeType || 3;
          }
          if (this.lineChart) this.updateLineChart();
        }
      },
      deep: true
    },
    tableData: {
      deep: true,
      handler(newVal) {
        if (this.chart) {
          this.$nextTick(() => {
            this.updateChart();
            // 数据刷新后，如果父组件没传 currentKnowledgeId，则重新走一次默认选中
            if (!this.currentKnowledgeId) {
              this.setDefaultSelection();
            }
            if (this.lineChart) this.updateLineChart();
          });
        }
      }
    },
    currentKnowledgeId: {
      handler(newVal) {
        if (newVal === undefined || newVal === null) {
          this.selectedKnowledgeId = '';
          return;
        }
        this.selectedKnowledgeId = newVal;
        // 更新知识点类型：先在 allKnowledgeList 扁平表中找
        let item = (this.allKnowledgeList || []).find(i => String(i.knowledgeId) === String(newVal));
        // 兜底：在 tableData 整棵树里按 knowledgeId 查找（保证 region 点任何行都能拿到 knowledgeType）
        if (!item && Array.isArray(this.tableData)) {
          const stack = [...this.tableData];
          while (stack.length && !item) {
            const n = stack.pop();
            if (n && String(n.knowledgeId) === String(newVal)) {
              item = n;
              break;
            }
            if (n && Array.isArray(n.children) && n.children.length) {
              for (let i = 0; i < n.children.length; i++) stack.push(n.children[i]);
            }
          }
        }
        if (item) {
          this.selectedKnowledgeType = item.knowledgeType || 3;
        }
        if (this.chart) this.updateChart();
        // 更新折线图和表格（依赖 examList 驱动，computed 会同步）
        if (this.lineChart) this.updateLineChart();
      },
      immediate: true
    },
    // 考试/练习 tab 切换时重新计算表格数据（computed.filteredTableData 会自动响应）
    activeTableTab() {
      // 预留：切换 tab 后若需做高亮/滚动控制可补
    },
    isRecordKnowledge: {
      handler(newVal) {
        this.showKnowledgePrompt = newVal;
      }
    },
    scoreRateTypes: {
      deep: true,
      handler(newVal) {
        const val = Array.isArray(newVal) ? newVal : [];
        this.selectedLegend.weak = val.length === 0 || val.includes(1);
        this.selectedLegend.good = val.length === 0 || val.includes(2);
        this.selectedLegend.excellent = val.length === 0 || val.includes(3);
        this.legendNumbers = val.length === 0 ? [1, 2, 3] : [...val].sort((a, b) => a - b);
        if (this.chart) this.updateChart();
      }
    },
  },
  methods: {
    // 获取当前颜色（根据选中知识点类型）
    getCurrentColor() {
      if (this.selectedKnowledgeType === 3) {
        return '#3BA272';
      } else if (this.selectedKnowledgeType === 2) {
        return '#FAC858';
      } else {
        return '#EE6666';
      }
    },
    // 初始化折线图
    initLineChart() {
      if (!this.$refs.lineChart) return;
      if (this.lineChart) {
        this.lineChart.dispose();
      }
      this.lineChart = echarts.init(this.$refs.lineChart);
      this.updateLineChart();
    },
    // 更新折线图：数据源为当前选中知识点节点上的 examList 数组
    //   xAxis = examList[i].dataName
    //   yAxis series.data = examList[i].dataRate （0~100百分比）
    updateLineChart() {
      const LOG_TAG = '%c[Diffusion.updateLineChart]';
      const LOG_STYLE = 'background:#3BA272;color:#fff;padding:2px 8px;border-radius:4px;font-weight:500;';
      const callerStack = (new Error()).stack
        ?.split('\n')
        ?.slice(1, 5)
        ?.map(s => s.trim())
        ?.join(' | ') || '';

      const snapshotBefore = {
        hasLineChartInstance: !!this.lineChart,
        hasLineChartRef: !!this.$refs.lineChart,
        selectedKnowledgeId: this.selectedKnowledgeId ?? '(empty)',
        selectedKnowledgeType: this.selectedKnowledgeType,
        selectedExamListLength: (this.selectedExamList || []).length,
        filteredTableDataLength: (this.filteredTableData || []).length,
        activeTableTab: this.activeTableTab,
        caller: callerStack
      };

      if (!this.lineChart) {
        console.warn(LOG_TAG + ' 提前跳过：lineChart 实例不存在（initLineChart 还未执行 / 已被 dispose）', LOG_STYLE, snapshotBefore);
        return;
      }

      const color = this.getCurrentColor();
      const examList = this.selectedExamList || [];
      const xAxisData = examList.map(item => item.dataName ?? '');
      const seriesData = examList.map(item => (item.dataRate === null || item.dataRate === undefined ? null : Number(item.dataRate)));

      const option = {
        tooltip: {
          trigger: 'axis',
          formatter: (params) => {
            const p = Array.isArray(params) ? params[0] : params;
            if (!p || p.value === null || p.value === undefined) return `${p.name ?? ''}`;
            return `${p.name}: ${p.value}%`;
          }
        },
        grid: {
          left: '5%',
          right: '5%',
          top: '10%',
          bottom: '10%'
        },
        xAxis: {
          show: false,
          type: 'category',
          data: xAxisData,
          axisLabel: {
            show: false,
            fontSize: 10,
            color: '#999',
            rotate: 30
          },
          axisLine: {
            show: false,
            lineStyle: { color: '#ECEEF3' }
          },
          axisTick: {
            show: false
          }
        },
        yAxis: {
          show: false,
          type: 'value',
          min: 0,
          max: 100,
          axisLabel: {
            show: false,
            fontSize: 10,
            color: '#999',
            formatter: '{value}%'
          },
          splitLine: {
            show: false,
            lineStyle: {
              color: '#f5f5f5',
              type: 'dashed'
            }
          }
        },
        series: [{
          data: seriesData,
          type: 'line',
          smooth: true,
          symbol: 'circle',
          symbolSize: 8,
          connectNulls: false,
          itemStyle: {
            color: '#fff',
            borderColor: color,
            borderWidth: 2
          },
          lineStyle: {
            color: color,
            width: 2
          },
          areaStyle: {
            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
              { offset: 0, color: color + '40' },
              { offset: 1, color: color + '05' }
            ])
          },
          // 确保圆点显示在折线上方，遮挡线条
          z: 10
        }]
      };

      const t0 = performance.now();
      this.lineChart.setOption(option, true); // notMerge=true，防止上一个节点残留x轴/点
      this.lineChart.resize();
      const t1 = performance.now();
    },
    // 处理加入练习：把当前选中节点的完整字段 emit 给父组件，父组件调加入练习接口
    handleAddPractice() {
      const item = this.currentKnowledgeItem;
      const knowledgeId = this.selectedKnowledgeId ?? item?.knowledgeId ?? null;
      const knowledgeName = this.selectedKnowledgeName ?? item?.knowledgeName ?? '';
      if (!knowledgeId) {
        this.$message && this.$message.warning && this.$message.warning('请先选择一个知识点');
        return;
      }

      // 得分率：personalScoreRate → gradeScoreRate → classScoreRate
      let personalScoreRate = item?.personalScoreRate;
      if (personalScoreRate === null || personalScoreRate === undefined || personalScoreRate === '') {
        personalScoreRate = item?.gradeScoreRate ?? item?.classScoreRate;
      }
      if (personalScoreRate === null || personalScoreRate === undefined || personalScoreRate === '') {
        personalScoreRate = 0;
      } else {
        personalScoreRate = Number(personalScoreRate);
        if (isNaN(personalScoreRate)) personalScoreRate = 0;
      }

      // 知识点类型 1薄弱 2一般 3优秀
      let knowledgeType = Number(item?.knowledgeType ?? item?.type ?? 0);
      if (isNaN(knowledgeType)) knowledgeType = 0;

      this.$emit('add-practice', {
        knowledgeId,
        knowledgeName,
        personalScoreRate,
        knowledgeType,
        ...(item ? { ...item } : {})
      });
    },
    setDefaultSelection() {
      // 如果父组件已经传入 currentKnowledgeId，则使用父级指定的选中项，不再重选
      // ——但如果父级指定的节点被"图例过滤规则"判定为不可见/降级(hasData=false)，则需要重选
      if (this.currentKnowledgeId) {
        this.selectedKnowledgeId = this.currentKnowledgeId;
        // 先按当前图例规则重算一遍过滤树，判断父级指定的节点是否"最终可见且有数据"
        const filteredTree = this.calcFilteredTree(this.tableData);
        let matchNode = null;
        const findStack = [...filteredTree];
        while (findStack.length && !matchNode) {
          const n = findStack.pop();
          if (n && String(n.knowledgeId) === String(this.currentKnowledgeId)) {
            matchNode = n;  
            break;
          }
          if (n && Array.isArray(n.children) && n.children.length > 0) {
            for (let i = 0; i < n.children.length; i++) findStack.push(n.children[i]);
          }
        }

        const isFinalValid = matchNode && matchNode.__finalVisible && matchNode.__finalHasData;

        if (isFinalValid) {
          // 父级指定的节点可见且有效：直接沿用
          this.selectedKnowledgeType = matchNode.knowledgeType || 3;
          if (this.lineChart) this.updateLineChart();
          // 通知父级联动 state（保证首次进来默认选中时 region 卡片也高亮对应条目 + 居中滚动）
          this.$emit('knowledge-item-click', {
            knowledgeId: matchNode.knowledgeId,
            knowledgeName: matchNode.knowledgeName,
            personalScoreRate: matchNode.personalScoreRate != null ? matchNode.personalScoreRate : matchNode.scoreRate,
            knowledgeType: matchNode.knowledgeType,
            examList: Array.isArray(matchNode.examList) ? matchNode.examList : []
          });
          return;
        }
        // 否则：父级指定被图例隐藏或降级 → 走重选逻辑（下面的 legacyLists/树形）
      }

      // 1. 优先从扁平化 allKnowledgeList / highVulnerability / fatalVulnerability 取第一个
      //    ——仍然要过一遍"图例过滤"，只有最终可见+hasData 的才能选中
      let targetNode = null;
      const legacyLists = [this.allKnowledgeList, this.highVulnerability, this.fatalVulnerability];
      for (const lst of legacyLists) {
        if (!Array.isArray(lst) || lst.length === 0) continue;
        // 从 lst 中找第一个：经过 calcFilteredTree 判定后 __finalVisible=true && __finalHasData=true
        const quickTree = this.calcFilteredTree(lst.map(it => ({ ...it })));  // 扁平化结构当 root 层处理即可
        for (let i = 0; i < quickTree.length; i++) {
          const n = quickTree[i];
          if (n && n.__finalVisible && n.__finalHasData) {
            targetNode = n;
            break;
          }
        }
        if (targetNode) break;
      }

      // 2. 兜底：从 tableData（树形）取"第一棵节点树的最后一个可见且有效叶子"
      if (!targetNode) {
        targetNode = this.findFirstVisibleLeafNode(this.calcFilteredTree(this.tableData));
      }

      if (!targetNode) {
        // 没找到任何可见有效节点（例如全部图例都关闭）→ 清空选中
        this.selectedKnowledgeId = '';
        this.selectedKnowledgeType = 3;
        if (this.lineChart) this.updateLineChart();
        return;
      }

      this.selectedIndex = 0;
      this.selectedKnowledgeId = targetNode.knowledgeId;
      this.selectedKnowledgeType = targetNode.knowledgeType || 3;

      // 初始化折线图（确保颜色正确）
      if (this.lineChart) {
        this.updateLineChart();
      }

      // 统一：应用选中大小（放大/还原）
      this.applySelectedSizeToChart(this.selectedKnowledgeId);

      // 通知父级联动 state（保证首次进来默认选中 / 图例过滤重选后 region 卡片也高亮对应条目 + 居中滚动）
      this.$emit('knowledge-item-click', {
        knowledgeId: targetNode.knowledgeId,
        knowledgeName: targetNode.knowledgeName,
        personalScoreRate: targetNode.personalScoreRate != null ? targetNode.personalScoreRate : targetNode.scoreRate,
        knowledgeType: targetNode.knowledgeType,
        examList: Array.isArray(targetNode.examList) ? targetNode.examList : []
      });
    },
    switchView(view) {
      this.$emit('view-change', view);
    },
    classIsGroup() {
      if (this.classLevel == 1) {
        return true;
      } else
        return false;
    },
    getNodeColor(knowledgeType) {
      const type = knowledgeType;
      if (type == 3) {
        return '#3BA272';
      } else if (type == 2) {
        return '#FAC858';
      } else {
        return '#EE6666';
      }
    },

    // 工具：knowledgeType -> legend 级别
    getNodeLevel(knowledgeType) {
      if (knowledgeType == 3) return 'excellent';
      if (knowledgeType == 2) return 'good';
      return 'weak';
    },

    /**
     * 基于 selectedLegend 计算"过滤后"的树形展示状态（后序遍历：先处理 children 再决定当前节点）
     * 规则（与产品说明严格对齐）：
     *  1) 叶子节点：
     *     - 自己的类型被图例隐藏（selectedLegend[type]=false）→ 整节点隐藏（opacity=0, invisible=true）
     *     - 自己的类型显示 → 保持 hasData/颜色 原样，visible
     *  2) 非叶子节点（有 children）：
     *     a. 如果存在「可见且有数据」的子（不管子是哪一层，只要子树里存在）
     *        → 父节点必须可见（否则子画不出来）：
     *          - 若自己的类型被图例隐藏 → 降级为「灰色节点、hasData=false」（名称保留，不显示真实得分率/颜色，不参与点击）
     *          - 若自己的类型显示 → 保持原 hasData/颜色 原样
     *     b. 如果不存在「可见且有数据」的子（全子树都被隐藏或都无数据）
     *        → 父节点按自身规则走：
     *          - 自己类型被隐藏 → 隐藏
     *          - 自己类型显示 → 保持原 hasData/颜色 原样
     *  3) 返回的每一项新增字段（不修改原 props.tableData，做浅克隆）：
     *     __finalVisible : boolean  该节点最终是否可见
     *     __finalHasData : boolean  是否显示为"有数据节点"（false=灰色圆点，无得分率）
     *     __finalColor   : string   最终绘制颜色
     *     __scoreRate    : number|null  得分率（若 __finalHasData=false 则不再用）
     *     __rawHasData   : boolean  原始数据是否有 scoreRate（用于 applySelectedSizeToChart 重算）
     */
    calcFilteredTree(data) {
      const vm = this;
      if (!Array.isArray(data)) return [];
      return data.map(item => {
        const node = { ...(item || {}) };
        const scoreRate = vm.classLevel == 2 || vm.mode == 'student' ? node.classScoreRate : node.gradeScoreRate;
        const rawHasData = scoreRate !== null && scoreRate !== undefined;
        node.__scoreRate = rawHasData ? Number(scoreRate) : null;
        node.__rawHasData = rawHasData;
        const selfLevel = vm.getNodeLevel(node.knowledgeType);
        const selfTypeHidden = vm.selectedLegend[selfLevel] === false;

        const hasChildren = Array.isArray(node.children) && node.children.length > 0;

        if (hasChildren) {
          // 先递归处理 children
          const subChildren = vm.calcFilteredTree(node.children);
          node.children = subChildren;

          // 统计：子树中存在多少"最终可见且最终 hasData=true"的节点（任意深度）
          let subHasVisibleHasData = false;
          const stack = [...subChildren];
          while (stack.length && !subHasVisibleHasData) {
            const n = stack.pop();
            if (n && n.__finalVisible && n.__finalHasData) {
              subHasVisibleHasData = true;
              break;
            }
            if (n && Array.isArray(n.children) && n.children.length > 0) {
              for (let i = 0; i < n.children.length; i++) stack.push(n.children[i]);
            }
          }

          if (subHasVisibleHasData) {
            // 父必须可见
            node.__finalVisible = true;
            if (selfTypeHidden) {
              // 降级为灰色节点（hasData=false）
              node.__finalHasData = false;
              node.__finalColor = '#BFC1C7';
            } else {
              node.__finalHasData = rawHasData;
              node.__finalColor = rawHasData ? vm.getNodeColor(node.knowledgeType) : '#BFC1C7';
            }
          } else {
            // 子树没有任何有效可见节点 → 父按自身规则
            if (selfTypeHidden) {
              node.__finalVisible = false;
              node.__finalHasData = rawHasData;
              node.__finalColor = '#BFC1C7';
            } else {
              node.__finalVisible = true;
              node.__finalHasData = rawHasData;
              node.__finalColor = rawHasData ? vm.getNodeColor(node.knowledgeType) : '#BFC1C7';
            }
          }
        } else {
          // 叶子节点
          if (selfTypeHidden) {
            node.__finalVisible = false;
            node.__finalHasData = rawHasData;
            node.__finalColor = '#BFC1C7';
          } else {
            node.__finalVisible = true;
            node.__finalHasData = rawHasData;
            node.__finalColor = rawHasData ? vm.getNodeColor(node.knowledgeType) : '#BFC1C7';
          }
        }
        return node;
      });
    },

    /**
     * 从过滤后的树形数据中，找到"最终可见且有数据"的一个叶子节点（用于默认选中）
     *  - 优先在"第一棵子树的最后一个可见叶子"里找（和原 setDefaultSelection 风格一致）
     *  - 如果整棵树都没可见有效点，返回 null
     *  - 如果入参 excludeKnowledgeId 提供，则跳过该 knowledgeId（用于重选时跳过被隐藏的旧选中）
     */
    findFirstVisibleLeafNode(list, excludeKnowledgeId = null) {
      if (!Array.isArray(list) || list.length === 0) return null;
      // 遍历所有节点（深度优先），收集可见+hasData=true 的叶子
      const stack = [];
      // 从前往后 push，让最后一棵树最后处理；每次 pop 最后一个 children，以匹配"第一棵树最后一个叶子"
      for (let i = list.length - 1; i >= 0; i--) stack.push(list[i]);
      let best = null;
      while (stack.length) {
        const n = stack.pop();
        if (!n) continue;
        const isLeaf = !Array.isArray(n.children) || n.children.length === 0;
        const isValid = n.__finalVisible && n.__finalHasData &&
          (excludeKnowledgeId === null || String(n.knowledgeId) !== String(excludeKnowledgeId));
        if (isValid && isLeaf) {
          // 每找到一个就覆盖（因为从后入栈+pop，最后保留下的就是第一棵树最后一个叶子）
          best = n;
        }
        if (Array.isArray(n.children) && n.children.length > 0) {
          for (let i = n.children.length - 1; i >= 0; i--) stack.push(n.children[i]);
        }
      }
      return best;
    },

    /**
     * 统一生成 ECharts tree 结构 children
     *  ——基于 calcFilteredTree 输出的节点（已经带 __final* 字段）
     * @param {Array} data          calcFilteredTree 处理过的 tree（或子集 children）
     * @param {number} level        当前层级
     * @param {string|null} overrideKnowledgeId
     */
    generateTreeData(data, level = 0, overrideKnowledgeId = null) {
      const vm = this;
      const currentKnowledgeId = overrideKnowledgeId !== null ? overrideKnowledgeId : vm.selectedKnowledgeId;
      return (data || []).map(item => {
        const finalHasData = !!item.__finalHasData;
        const finalVisible = !!item.__finalVisible;
        const finalColor = item.__finalColor || '#BFC1C7';
        const finalScoreRate = item.__scoreRate;

        // 注意：节点被过滤（__finalVisible=false）时，即使 isMatched 也不显示放大——因为看不见
        const isMatched = finalHasData && finalVisible && (String(item.knowledgeId) === String(currentKnowledgeId));

        let symbolSize = 5;
        if (isMatched) {
          symbolSize = 25; // 选中：放大
        } else if (finalHasData) {
          symbolSize = 10; // 有效但未选中：默认
        }

        // 选中节点的绘制权重更高，防止被非选中圆点遮挡
        const drawZ       = isMatched ? 99  : 10;
        const drawZLevel  = isMatched ? 2   : 0;
        const node = {
          name: item.knowledgeName,
          symbolSize: symbolSize,
          value: finalScoreRate,
          itemStyle: {
            color: finalColor,
            opacity: finalVisible ? 1 : 0
          },
          lineStyle: {
            opacity: finalVisible ? 1 : 0
          },
          hasData: finalHasData,           // 用于 tooltip 显示/点击忽略（灰色不能点）
          knowledgeId: item.knowledgeId,
          knowledgeType: item.knowledgeType,
          z: drawZ,
          zlevel: drawZLevel
        };
        if (item.children && item.children.length > 0) {
          node.children = vm.generateTreeData(item.children, level + 1, overrideKnowledgeId);
        }
        return node;
      });
    },

    /**
     * 统一入口：将当前选中的 knowledgeId 对应的 symbolSize 应用到 ECharts 图表上
     * ——基于过滤后的 filteredTree（保证图例过滤后重新算节点大小和选中状态）
     */
    applySelectedSizeToChart(overrideKnowledgeId) {
      if (!this.chart) return;
      const selectedId = overrideKnowledgeId !== undefined ? overrideKnowledgeId : this.selectedKnowledgeId;
      const filteredTree = this.calcFilteredTree(this.tableData);
      this.chart.setOption({
        series: [{
          data: [
            {
              name: this.subjectName,
              symbolSize: 5,
              value: this.subjectScoreRate,
              itemStyle: {
                color: '#BFC1C7',
                opacity: 1
              },
              hasData: false,
              children: this.generateTreeData(filteredTree, 0, selectedId)
            }
          ]
        }]
      }, {
        animation: true,
        animationDuration: 300
      });
    },

    /**
     * 从树形 list 中取"第一棵节点树的最后一个叶子节点"
     * 规则：取 list[0]，沿 children[last] 一路下钻，直到没有 children
     */
    findFirstTreeLastLeafNode(list) {
      if (!Array.isArray(list) || list.length === 0) return null;
      let node = list[0];
      while (node && Array.isArray(node.children) && node.children.length > 0) {
        node = node.children[node.children.length - 1];
      }
      return node || null;
    },
    initChart() {
      try {
        if (!this.$refs.chart) {
          console.error('图表容器不存在');
          return;
        }
        this.createChart();
      }
      catch (error) {
        console.error('图表初始化失败:', error);
      }
    },
    createChart() {
      try {
        if (!this.$refs.chart) {
          console.error('图表容器不存在');
          return;
        }
        if (this.chart) {
          this.chart.dispose();
        }
        this.chart = echarts.init(this.$refs.chart);
        const vm = this;
        const countOutermostNodes = (data) => {
          let count = 0;
          const traverse = (nodes) => {
            nodes.forEach(node => {
              if (node.children && node.children.length > 0) {
                traverse(node.children);
              } else {
                count++;
              }
            });
          };
          traverse(data);
          return count;
        };
        const outermostCount = countOutermostNodes(this.tableData);
        const getOptimalZoom = () => {
          let zoom = 1.05;
          return zoom;
        };
        const getOptimalRadius = () => {
          if (outermostCount <= 5) {
            return ['20%', '65%'];
          }
          return ['12%', '75%'];
        };
        const option = {
          tooltip: {
            formatter: (params) => {
              if (params.treePathInfo && params.treePathInfo.length === 1) {
                return params.name;
              }
              const hasData = params.data && params.data.hasData;
              if (hasData) {
                return `${params.name}<br/>得分率: ${params.value !== null && params.value !== undefined ? params.value : '0'}%`;
              } else {
                return params.name;
              }
            }
          },
          animationDurationUpdate: 1500,
          animationEasingUpdate: 'quinticInOut',
          series: [
            {
              type: 'tree',
              layout: 'radial',
              symbol: 'circle',
              initialTreeDepth: 999,
              expandAndCollapse: false,
              orient: 'radial',
              roam: false,
              left: '8%',
              right: '8%',
              top: '8%',
              bottom: '8%',
              radius: getOptimalRadius(),
              nodeScaleRatio: 1,
              layerPadding: outermostCount <= 5 ? [20, 10] : [10, 5],
              data: [
                {
                  name: this.subjectName,
                  symbolSize: 5,
                  value: this.subjectScoreRate,
                  itemStyle: {
                    color: '#BFC1C7'
                  },
                  hasData: false,
                  children: this.generateTreeData(this.calcFilteredTree(this.tableData))
                }
              ],
              label: {
                show: false
              },
              lineStyle: {
                color: '#ECEEF3',
                width: 1,
                type: 'solid'
              },
              emphasis: {
                focus: 'adjacency',
                lineStyle: {
                  width: 1
                }
              }
            }
          ]
        };
        this.chart.setOption(option);
        this.chart.resize();

        // 设置默认选中项
        this.$nextTick(() => {
          this.setDefaultSelection();
        });

        this.chart.on('click', (params) => {
          const dataItem = params && params.data;

          // 1. 灰色圆点（无数据 hasData=false）或学科中心节点：点击忽略，不做任何处理
          const isSubjectCenter = params.treePathInfo && params.treePathInfo.length === 1;
          if (isSubjectCenter || !dataItem || !dataItem.hasData) {
            return;
          }

          // 2. 从点击节点本身的 knowledgeId 去查（点击 params.data.knowledgeId 与 原始tableData 匹配）
          const clickedKnowledgeId = dataItem.knowledgeId;
          const allLists = [this.allKnowledgeList, this.highVulnerability, this.fatalVulnerability];
          let knowledgeItem = null;
          let targetIndex = -1;

          // 先从扁平化列表里找（用于回传 index 给父级）
          if (clickedKnowledgeId !== undefined && clickedKnowledgeId !== null && clickedKnowledgeId !== '') {
            for (const list of allLists) {
              if (!Array.isArray(list)) continue;
              targetIndex = list.findIndex(item => String(item.knowledgeId) === String(clickedKnowledgeId));
              if (targetIndex !== -1) {
                knowledgeItem = list[targetIndex];
                break;
              }
            }
          }
          // 扁平化找不到，就从 tableData 递归查找，按 name 兜底
          if (!knowledgeItem && params.name) {
            const findByNodeName = (arr) => {
              for (const item of arr) {
                if (item.knowledgeName === params.name) return item;
                if (Array.isArray(item.children) && item.children.length) {
                  const f = findByNodeName(item.children);
                  if (f) return f;
                }
              }
              return null;
            };
            knowledgeItem = findByNodeName(this.tableData);
            // 再回填一次 index
            if (knowledgeItem && knowledgeItem.knowledgeId !== undefined) {
              for (const list of allLists) {
                if (!Array.isArray(list)) continue;
                targetIndex = list.findIndex(item =>
                  String(item.knowledgeId) === String(knowledgeItem.knowledgeId)
                );
                if (targetIndex !== -1) break;
              }
            }
          }

          // 3. 如果还是找不到知识点项，按点击的 data 做最小赋值（至少保证选中ID、类型对得上，有放大效果）
          if (!knowledgeItem) {
            knowledgeItem = {
              knowledgeId: clickedKnowledgeId ?? params.name,
              knowledgeName: params.name,
              knowledgeType: dataItem.knowledgeType || 3
            };
          }

          // 4. 回传事件：保持向后兼容 {item, index}，同时把核心字段打平在 payload 顶层
          //    ——父级联动（handleKnowledgeItemClick）会按顶层 knowledgeId 写入 currentLinkedKnowledgeId
          //    ——外层其他监听者也可继续用 item / index
          this.$emit('knowledge-item-click', {
            item: knowledgeItem,
            index: targetIndex,
            knowledgeId: knowledgeItem.knowledgeId,
            knowledgeName: knowledgeItem.knowledgeName,
            personalScoreRate: knowledgeItem.personalScoreRate != null
              ? knowledgeItem.personalScoreRate
              : knowledgeItem.scoreRate,
            knowledgeType: knowledgeItem.knowledgeType,
            examList: Array.isArray(knowledgeItem.examList) ? knowledgeItem.examList : []
          });

          // 5. 更新内部状态
          if (targetIndex !== -1) this.selectedIndex = targetIndex;
          this.selectedKnowledgeId = knowledgeItem.knowledgeId;
          this.selectedKnowledgeType = knowledgeItem.knowledgeType || 3;

          // 6. 折线图颜色同步
          if (this.lineChart) {
            this.updateLineChart();
          }

          // 7. 统一更新圆点大小：当前点放大(25)，上一个选中点自动恢复为10
          this.applySelectedSizeToChart(this.selectedKnowledgeId);
        });
        window.addEventListener('resize', () => {
          if (this.chart) {
            this.chart.resize();
          }
        });
      }
      catch (error) {
        console.error('图表创建失败:', error);
      }
    },
    updateChart() {
      try {
        const LOG_TAG = '%c[Diffusion.updateChart]';
        const LOG_STYLE = 'background:#2E64FA;color:#fff;padding:2px 8px;border-radius:4px;font-weight:500;';
        const callerStack = (new Error()).stack
          ?.split('\n')
          ?.slice(1, 5)
          ?.map(s => s.trim())
          ?.join(' | ') || '';

        if (!this.$refs.chart) {
          console.warn(LOG_TAG + ' 提前跳过：$refs.chart 不存在（模板 ref 未挂载 / 容器尚未渲染）', LOG_STYLE);
          return;
        }

        const t0 = performance.now();
        const oldChartExist = !!this.chart;
        this.createChart();
        const t1 = performance.now();
      }
      catch (error) {
        console.error('[Diffusion.updateChart] 图表更新失败:', error);
      }
    },
    reset() {
      this.selectedIndex = 0;
      this.updateChart();
    },
    toggleLegend(type) {
      const prevSelected = this.selectedLegend[type];
      this.selectedLegend[type] = !prevSelected;
      const typeMap = {
        weak: 1,
        good: 2,
        excellent: 3
      };
      const number = typeMap[type];
      if (!this.selectedLegend[type]) {
        const index = this.legendNumbers.indexOf(number);
        if (index > -1) {
          this.legendNumbers.splice(index, 1);
        }
      } else {
        if (!this.legendNumbers.includes(number)) {
          this.legendNumbers.push(number);
        }
        this.legendNumbers.sort((a, b) => a - b);
      }
      const hasAllNumbers = this.legendNumbers.includes(1) && this.legendNumbers.includes(2) && this.legendNumbers.includes(3);
      const dataToSend = hasAllNumbers ? null : this.legendNumbers;
      this.$emit('legend-numbers-change', dataToSend);

      // ---- 图例变化后：重算过滤 → 重绘扩散图 → 若选中被隐藏则重选可见节点 ----
      this.$nextTick(() => {
        // 1) 全量重绘（内部 createChart 已经是 calcFilteredTree(tableData) 再生成 children）
        if (this.chart) {
          this.updateChart();
        }

        // 2) 判断当前选中的节点在新的图例规则下是否"最终可见且有数据"
        const filteredTree = this.calcFilteredTree(this.tableData);
        let matchNode = null;
        const findStack = [...filteredTree];
        while (findStack.length && !matchNode) {
          const n = findStack.pop();
          if (n && this.selectedKnowledgeId &&
              String(n.knowledgeId) === String(this.selectedKnowledgeId)) {
            matchNode = n;
            break;
          }
          if (n && Array.isArray(n.children) && n.children.length > 0) {
            for (let i = 0; i < n.children.length; i++) findStack.push(n.children[i]);
          }
        }

        const stillValid = matchNode && matchNode.__finalVisible && matchNode.__finalHasData;
        if (!stillValid) {
          // 当前选中的点被隐藏/降级 → 重选一个可见有效叶子（排除当前，避免又选回来）
          const replacement = this.findFirstVisibleLeafNode(filteredTree, this.selectedKnowledgeId);
          if (replacement) {
            this.selectedKnowledgeId = replacement.knowledgeId;
            this.selectedKnowledgeType = replacement.knowledgeType || 3;
            // 重新应用圆点放大
            if (this.chart) this.applySelectedSizeToChart(replacement.knowledgeId);
            // 同步折线图/表格（computed 依赖 selectedKnowledgeId → selectedExamList 会变，折线这里手动触发）
            if (this.lineChart) this.updateLineChart();
            this.$emit('knowledge-item-click', {
              knowledgeId: replacement.knowledgeId,
              knowledgeName: replacement.knowledgeName
            });
          } else {
            // 全部关闭了 → 清空
            this.selectedKnowledgeId = '';
            this.selectedKnowledgeType = 3;
            if (this.chart) this.applySelectedSizeToChart('');
            if (this.lineChart) this.updateLineChart();
          }
        } else {
          // 当前选中仍然有效 → 仅重新"应用选中大小"（因为 createChart 后放大状态没保留）
          if (this.chart) this.applySelectedSizeToChart(this.selectedKnowledgeId);
        }
      });
    },
  }
};
</script>

<style lang="scss" scoped>
.circularDiffusion {
  position: relative;

}

.circularDiffusion,
.knowledge_graph_student {
  .knowledge_class {

    :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;
      display: flex;
      justify-content: space-between;
      align-items: center;

      .legend {
        display: flex;
        gap: 20px;

        .legend_item {
          display: flex;
          align-items: center;
          gap: 5px;
          cursor: pointer;
          font-weight: 500;

          &.selected {
            opacity: 1;
          }

          &:hover {
            opacity: 1;
          }

          .legend_dot {
            width: 20px;
            height: 10px;
            border-radius: 2px;
            transition: all 0.3s ease;

            &.weak {
              background: #ccc;
            }

            &.good {
              background: #cccccc;
            }

            &.excellent {
              background: #ccc;
            }
          }

          .legend_text {
            font-size: 12px;
            color: #999999;
            transition: all 0.3s ease;
          }

          &.selected {
            .legend_dot.weak {
              background: #EE6666;
            }

            .legend_dot.good {
              background: #FAC858;
            }

            .legend_dot.excellent {
              background: #3BA272;
            }

            .legend_text {
              color: #333;
            }
          }
        }
      }
    }

    .graph_content {
      display: flex;
      gap: 20px;
      position: relative;

      .knowledge_right_container {
        width: 500px;
        display: flex;
        flex-direction: column;
        gap: 10px;

        .line_chart_container {
          border: 1px solid #DCDFE6;
          border-radius: 6px;
          position: relative;
          display: flex;
          flex-direction: column;
          /* 标题栏：左右分布，放在折线图上方 */
          .chart_title {
            display: flex;
            justify-content: space-between;
            align-items: center;
            gap: 12px;
            padding: 10px 16px;
            border-bottom: 1px solid #DCDFE6;
            /* 左侧：上下结构（标题 + 考频） */
            .chart_title_left {
              flex: 1;
              min-width: 0;
              display: flex;
              flex-direction: column;
              justify-content: center;
              gap: 4px;
            }

            .title_text {
              font-weight: 500;
              font-size: 14px;
              color: #333333;
              overflow: hidden;
              text-overflow: ellipsis;
              white-space: nowrap;
              line-height: 1.2;
            }

            .exam_freq {
              font-weight: 400;
              font-size: 12px;
              color: #999999;
              line-height: 1.2;
            }

            /* 加入练习按钮：使用 Element UI el-button，描边风格跟随当前选中圆点的 knowledgeType 颜色 */
            .add_practice_btn {
              border-radius: 4px;
              font-weight: 500;
              font-size: 14px;
              background: #fff !important;
              flex-shrink: 0;
              padding: 0;
              width: 100px;
              height: 36px;
              line-height: 36px;
              text-align: center;

              /* 薄弱：knowledgeType=1 → 与选中圆点的 #EE6666 颜色一致 */
              &.btn_theme_weak {
                border: 1px solid #EE6666 !important;
                color: #EE6666 !important;
                &:hover, &:focus {
                  border: 1px solid #EE6666 !important;
                  color: #EE6666 !important;
                  background: #fff !important;
                }
              }
              /* 一般/能力提升：knowledgeType=2 → #FAC858 */
              &.btn_theme_good {
                border: 1px solid #FAC858 !important;
                color: #FAC858 !important;
                &:hover, &:focus {
                  border: 1px solid #FAC858 !important;
                  color: #FAC858 !important;
                  background: #fff !important;
                }
              }
              /* 优秀/扎实巩固：knowledgeType=3 → #3BA272 */
              &.btn_theme_excellent {
                border: 1px solid #3BA272 !important;
                color: #3BA272 !important;
                &:hover, &:focus {
                  border: 1px solid #3BA272 !important;
                  color: #3BA272 !important;
                  background: #fff !important;
                }
              }

              /* isJoin=1 的置灰状态：disabled 时边框和文字变浅灰，背景淡灰（优先级最高，覆盖主题色） */
              &.is-disabled,
              &[disabled] {
                border: 1px solid #DCDFE6 !important;
                color: #C0C4CC !important;
                background: #F5F7FA !important;
                cursor: not-allowed !important;
                &:hover,
                &:focus {
                  border: 1px solid #DCDFE6 !important;
                  color: #C0C4CC !important;
                  background: #F5F7FA !important;
                }
              }
            }
          }

          .line_chart {
            width: calc(100% - 30px);
            height: 180px;
            padding: 15px;
          }
        }

        .table_container {

          /* 按钮组 tabs：按设计图样式 */
          .table_tabs_radio {
            margin-bottom: 10px;
            display: inline-flex;
            align-items: center;
            line-height: 0;
            border-collapse: collapse;

            :deep(.el-radio-button) {
              /* 让选中的按钮在视觉上压过相邻按钮的边框 */
              &:first-child .el-radio-button__inner {
                border-radius: 4px 0 0 4px;
              }
              &:last-child .el-radio-button__inner {
                border-radius: 0 4px 4px 0;
              }
            }

            :deep(.el-radio-button__inner) {
              height: 36px;
              line-height: 36px;
              padding: 0 20px;
              font-size: 13px;
              color: #909399;          /* 未选中：灰字 */
              background: #FFFFFF;
              border: 1px solid #DCDFE6; /* 未选中：灰边框 */
              box-sizing: border-box;
              margin: 0;
              box-shadow: none !important;
              transition: none;
              position: relative;
              border-radius: 0;
            }

            /* 相邻按钮的重叠（Element 默认 margin-left:-1px，这里保持默认通过 border-color 变化即可，显式指定分隔线颜色） */
            :deep(.el-radio-button + .el-radio-button) {
              margin-left: -1px;
            }

            /* 选中态：蓝边框 + 蓝字 + 白底（完全匹配图片） */
            :deep(.el-radio-button__orig-radio:checked + .el-radio-button__inner) {
              color: #2E64FA;
              background-color: #FFFFFF;
              border-color: #2E64FA;
              box-shadow: none !important;
              z-index: 2; /* 把选中按钮抬高，其蓝色边框盖住相邻按钮的灰色边框 */
            }

            /* 未选中 hover：保持灰字灰边框，不变成蓝色 */
            :deep(.el-radio-button__orig-radio:not(:checked) + .el-radio-button__inner:hover) {
              color: #909399;
              border-color: #DCDFE6;
              background-color: #FFFFFF;
            }
            :deep(.el-radio-button__inner:hover) {
              color: #909399;
            }
          }

          .data_table {
            height: 330px;
            border-radius: 6px;
            /* 整表统一 cell 高度为 40px（Element UI 是通过 .cell 控制） */
            :deep(.el-table__header-wrapper th .cell),
            :deep(.el-table__body-wrapper td .cell) {
              height: 40px;
              line-height: 40px;
              padding-top: 0;
              padding-bottom: 0;
              box-sizing: border-box;
             
            }

            /* 表头样式 */
            :deep(.el-table__header th) {
              background: #F5F7FA;
              color: #333333;
              font-weight: 500;
              font-size: 14px;
              padding:0px !important;
            }

            /* 斑马纹：开启 stripe 后，Element UI 会给偶数行加 el-table__row--striped */
            :deep(.el-table__body tr.el-table__row--striped td) {
              background: #FAFBFC;
            }
            :deep(.el-table__body tr.el-table__row--striped td.el-table__cell) {
              background: #FAFBFC;
            }

            /* 表体样式 */
            :deep(.el-table__body td) {
              font-weight: 400;
              font-size: 14px;
              color: #666666;
               padding:0px !important;
            }

            /* hover 行时不改变斑马纹底色（仅轻微提亮边框感知） */
            :deep(.el-table__body tr:hover > td) {
              background-color: #EFF6FF;
            }
          }
        }
      }

      .item_chart_container {
        border: 1px solid #E9EBEF;
        border-radius: 10px;
        margin-bottom: 10px;
      }

      .chart_container {
        flex: 1;
        min-width: 0;
        border-radius: 10px;
        border: 1px solid #E9EBEF;
        position: relative;
        padding: 20px;
        overflow: hidden;

        .top_container {
          display: flex;
          justify-content: space-between;
          align-items: flex-start;
          flex-wrap: wrap;
          gap: 10px;
          margin-bottom: 10px;
        }

        .legend {
          display: flex;
          gap: 20px;
          flex-wrap: wrap;
          flex: 1;
          min-width: 0;

          .legend_item {
            display: flex;
            align-items: center;
            gap: 5px;
            cursor: pointer;
            font-weight: 500;

            &.selected {
              opacity: 1;
            }

            &:hover {
              opacity: 1;
            }

            .legend_dot {
              width: 20px;
              height: 10px;
              border-radius: 2px;
              transition: all 0.3s ease;

              &.weak {
                background: #ccc;
              }

              &.good {
                background: #ccc;
              }

              &.excellent {
                background: #ccc;
              }
            }

            .legend_text {
              font-size: 12px;
              color: #999999;
              transition: all 0.3s ease;
            }

            &.selected {
              .legend_dot.weak {
                background: #EE6666;
              }

              .legend_dot.good {
                background: #FAC858;
              }

              .legend_dot.excellent {
                background: #3BA272;
              }

              .legend_text {
                color: #333;
              }
            }
          }
        }

        .chart {
          display: flex;
          justify-content: center;
          align-items: center;
          min-height: 590px;
        }

      }

      .knowledge_list {
        width: 100%;
        height: 410px;
        overflow-y: auto;
        padding-right: 10px;
        position: relative;

        &::-webkit-scrollbar {
          width: 6px;
        }

        &::-webkit-scrollbar-track {
          background: #f1f1f1;
          border-radius: 3px;
        }

        &::-webkit-scrollbar-thumb {
          background: #c1c1c1;
          border-radius: 3px;
        }

        &::-webkit-scrollbar-thumb:hover {
          background: #a8a8a8;
        }
      }
    }
  }
}
</style>
