@ -1,702 +0,0 @@
<template>
  <div class="region_container">
    <!-- 薄弱补强区 -->
    <div class="region_section weak">
      <div class="region_header">
        <div class="region_header_main">
          <span class="region_icon">
            <img :src="require('@/assets/knowledgeGraph/buqiang.png')" alt="薄弱补强区" style="width: 32px; height: auto; display: block;" />
          </span>
          <span class="region_title">薄弱补强区</span>
        </div>
        <span class="region_desc">得分率＜60% ｜ 优先补齐短板</span>
      </div>
      <div class="knowledge_rows" ref="weakRowsBox">
        <div
          class="knowledge_row"
          v-for="(item, index) in weakArea"
          :key="'weak-' + index"
          :data-knowledge-id="item.knowledgeId"
          @click="handleRowClick(item)"
          :class="{ selected: isKnowledgeSelected(item), highlighted: isKnowledgeSelected(item) }"
        >
          <div class="row_left">
            <span class="kp_name" :title="item.knowledgeName">{{ item.knowledgeName }}</span>
            <div class="kp_meta">
              <span class="kp_score">得分率：{{ getScoreRate(item) }}%</span>
              <span class="kp_freq_tag">考频：{{ getExamFreq(item) }}</span>
            </div>
          </div>
          <div class="row_right">
            <el-button size="small" class="add_btn weak_btn"
              :disabled="isKnowledgeAdded(item)"
              @click="handleAddPractice(item)">
              {{ isKnowledgeAdded(item) ? '已加入练习' : '加入练习' }}
            </el-button>
          </div>
        </div>
        <div v-if="weakArea.length === 0" class="empty_tip no_content_data"><span>暂无数据</span></div>
      </div>
    </div>

    <!-- 能力提升区 -->
    <div class="region_section good">
      <div class="region_header">
        <div class="region_header_main">
          <span class="region_icon">
            <img :src="require('@/assets/knowledgeGraph/tisheng.png')" alt="能力提升区" style="width: 32px; height: auto; display: block;" />
          </span>
          <span class="region_title">能力提升区</span>
        </div>
        <span class="region_desc">60%≤得分率＜85% ｜ 强化练习提升</span>
      </div>
      <div class="knowledge_rows" ref="goodRowsBox">
        <div
          class="knowledge_row"
          v-for="(item, index) in goodArea"
          :key="'good-' + index"
          :data-knowledge-id="item.knowledgeId"
          @click="handleRowClick(item)"
          :class="{ selected: isKnowledgeSelected(item), highlighted: isKnowledgeSelected(item) }"
        >
          <div class="row_left">
            <span class="kp_name" :title="item.knowledgeName">{{ item.knowledgeName }}</span>
            <div class="kp_meta">
              <span class="kp_score">得分率：{{ getScoreRate(item) }}%</span>
              <span class="kp_freq_tag">考频：{{ getExamFreq(item) }}</span>
            </div>
          </div>
          <div class="row_right">
            <el-button size="small" class="add_btn good_btn"
              :disabled="isKnowledgeAdded(item)"
              @click="handleAddPractice(item)">
              {{ isKnowledgeAdded(item) ? '已加入练习' : '加入练习' }}
            </el-button>
          </div>
        </div>
        <div v-if="goodArea.length === 0" class="empty_tip no_content_data"><span>暂无数据</span></div>
      </div>
    </div>

    <!-- 扎实巩固区 -->
    <div class="region_section excellent">
      <div class="region_header">
        <div class="region_header_main">
          <span class="region_icon">
            <img :src="require('@/assets/knowledgeGraph/gonggu.png')" alt="扎实巩固区" style="width: 32px; height: auto; display: block;" />
          </span>
          <span class="region_title">扎实巩固区</span>
        </div>
        <span class="region_desc">得分率＞85% ｜ 定期练习巩固</span>
      </div>
      <div class="knowledge_rows" ref="excellentRowsBox">
        <div
          class="knowledge_row"
          v-for="(item, index) in excellentArea"
          :key="'excellent-' + index"
          :data-knowledge-id="item.knowledgeId"
          @click="handleRowClick(item)"
          :class="{ selected: isKnowledgeSelected(item), highlighted: isKnowledgeSelected(item) }"
        >
          <div class="row_left">
            <span class="kp_name" :title="item.knowledgeName">{{ item.knowledgeName }}</span>
            <div class="kp_meta">
              <span class="kp_score">得分率：{{ getScoreRate(item) }}%</span>
              <span class="kp_freq_tag">考频：{{ getExamFreq(item) }}</span>
            </div>
          </div>
          <div class="row_right">
            <el-button size="small" class="add_btn excellent_btn"
              :disabled="isKnowledgeAdded(item)"
              @click="handleAddPractice(item)">
              {{ isKnowledgeAdded(item) ? '已加入练习' : '加入练习' }}
            </el-button>
          </div>
        </div>
        <div v-if="excellentArea.length === 0" class="empty_tip no_content_data"><span>暂无数据</span></div>
      </div>
    </div>

    <!-- 冲刺进阶区（暂时隐藏） -->
    <div class="region_section advanced" >
      <div class="region_header">
        <div class="region_header_main">
          <span class="region_icon">
            <img :src="require('@/assets/knowledgeGraph/jinjie.png')" alt="冲刺进阶区" style="width: 32px; height: auto; display: block;" />
          </span>
          <span class="region_title">冲刺进阶区</span>
        </div>
        <span class="region_desc">优于同层级学生 ｜ 对标上游冲刺进阶</span>
      </div>
      <div class="knowledge_rows" ref="advancedRowsBox">
        <div
          class="knowledge_row"
          v-for="(item, index) in advancedArea"
          :key="'advanced-' + index"
          :data-knowledge-id="item.knowledgeId"
          @click="handleRowClick(item)"
          :class="{ selected: isKnowledgeSelected(item), highlighted: isKnowledgeSelected(item) }"
        >
          <div class="row_left">
            <span class="kp_name" :title="item.knowledgeName">{{ item.knowledgeName }}</span>
            <div class="kp_meta">
              <span class="kp_score">得分率：{{ getScoreRate(item) }}%</span>
              <span class="kp_freq_tag">考频：{{ getExamFreq(item) }}</span>
            </div>
          </div>
          <div class="row_right">
            <el-button size="small" class="add_btn advanced_btn"
              :disabled="isKnowledgeAdded(item)"
              @click="handleAddPractice(item)">
              {{ isKnowledgeAdded(item) ? '已加入练习' : '加入练习' }}
            </el-button>
          </div>
        </div>
        <div v-if="advancedArea.length === 0" class="empty_tip no_content_data"><span>暂无数据</span></div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Region',
  props: {
    // 分区数据（由父级通过 knowledge_partition 接口返回后传入）
    // conquerZone:    薄弱补强区
    // improveZone:    能力提升区
    // consolidateZone:扎实巩固区
    partitionData: {
      type: Object,
      default: () => ({
        conquerZone: [],
        improveZone: [],
        consolidateZone: []
      })
    },
    // 父级已成功"加入练习"的知识点ID映射：{ [knowledgeId]: true }
    addedKnowledgeIds: {
      type: Object,
      default: () => ({})
    },
    // 父级传入的"联动选中知识点ID"
    // - 环形扩散图选中的圆点，父级会把 knowledgeId 写到这里 → 本组件高亮对应行并滚动居中
    // - 点本组件任一行条目时，也通过 emit('item-click') 反向通知父级把 ID 同步到 circularDiffusion
    selectedKnowledgeId: {
      type: [String, Number],
      default: ''
    },
    // 图例可见状态联动：薄弱(1)/良好(2)/优秀(3) 的可见性
    // 当某类图例被隐藏时，对应区域返回空数组，显示"暂无数据"
    legendVisibility: {
      type: Object,
      default: () => ({ 1: true, 2: true, 3: true })
    }
  },
  data() {
    return {};
  },
  watch: {
    // 父级 selectedKnowledgeId 变化：联动高亮 + 滚动到对应区列表的可视中央
    selectedKnowledgeId: {
      immediate: true,
      handler(newVal) {
        if (!newVal) return;
        const sid = String(newVal);
        // 这里只负责发起"滚动尝试"，函数内部会处理 DOM/数据未就绪时的重试
        this.scheduleHighlightScroll(sid);
      }
    },
    // partitionData 变化：路由进入组件时，父级接口返回前 selectedKnowledgeId 可能已经写好了，
    // 但此时 conquerZone/improveZone/consolidateZone 都是空数组 → 上面的 watch 已经执行过（immediate）但没找到条目，不会再自动重跑。
    // 所以 partitionData 有内容变化后，只要还有选中 ID，就重新触发一次滚动。
    partitionData: {
      deep: true,
      handler(newVal, oldVal) {
        // 快速跳过空对象/完全一样的引用
        if (!this.selectedKnowledgeId) return;
        const lists = ['conquerZone', 'improveZone', 'consolidateZone'];
        let hasAnyData = false;
        if (newVal) {
          for (const k of lists) {
            if (Array.isArray(newVal[k]) && newVal[k].length > 0) { hasAnyData = true; break; }
          }
        }
        if (!hasAnyData) return;
        // 接口数据刚落地，Vue 下一 tick 才会 patch DOM 行节点；所以 $nextTick 再排
        const sid = String(this.selectedKnowledgeId);
        this.$nextTick(() => this.scheduleHighlightScroll(sid));
      }
    }
  },
  activated() {
    // keep-alive 缓存路由时，组件被切回来会触发 activated，但 watch 不会再立即执行
    // 只要有选中 ID 就重新尝试滚动居中（DOM 已就绪，命中会直接滚）
    if (this.selectedKnowledgeId) {
      const sid = String(this.selectedKnowledgeId);
      this.$nextTick(() => this.scheduleHighlightScroll(sid));
    }
  },
  computed: {
    // 薄弱补强区：直接用接口返回的 conquerZone
    // 图例联动：当薄弱图例被隐藏时（legendVisibility[1]=false），返回空数组 → 显示"暂无数据"
    weakArea() {
      const base = Array.isArray(this.partitionData?.conquerZone) ? this.partitionData.conquerZone : [];
      return this.legendVisibility && this.legendVisibility[1] === false ? [] : base;
    },
    // 能力提升区：直接用接口返回的 improveZone
    // 图例联动：当良好图例被隐藏时（legendVisibility[2]=false），返回空数组 → 显示"暂无数据"
    goodArea() {
      const base = Array.isArray(this.partitionData?.improveZone) ? this.partitionData.improveZone : [];
      return this.legendVisibility && this.legendVisibility[2] === false ? [] : base;
    },
    // 扎实巩固区：直接用接口返回的 consolidateZone
    // 图例联动：当优秀图例被隐藏时（legendVisibility[3]=false），返回空数组 → 显示"暂无数据"
    excellentArea() {
      const base = Array.isArray(this.partitionData?.consolidateZone) ? this.partitionData.consolidateZone : [];
      return this.legendVisibility && this.legendVisibility[3] === false ? [] : base;
    },
    // 冲刺进阶区：暂时隐藏，预留空数组
    advancedArea() {
      return [];
    }
  },
  methods: {
    // 获取得分率：优先 personalScoreRate（接口分区字段），兜底 class / grade
    getScoreRate(item) {
      if (item && (item.personalScoreRate !== null && item.personalScoreRate !== undefined)) {
        const n = Number(item.personalScoreRate);
        return isNaN(n) ? 0 : n;
      }
      const fallback = item?.gradeScoreRate ?? item?.classScoreRate;
      if (fallback === null || fallback === undefined) return 0;
      const n = Number(fallback);
      return isNaN(n) ? 0 : n;
    },
    // 获取考频：优先 examNum（接口分区字段：考频，含练习），兜底常见字段
    getExamFreq(item) {
      if (item && (item.examNum !== null && item.examNum !== undefined && item.examNum !== '')) {
        const n = Number(item.examNum);
        return isNaN(n) ? 0 : n;
      }
      const fallback = item?.examFreq ?? item?.examCount ?? item?.questionNum ?? item?.wrongCount;
      if (fallback === null || fallback === undefined || fallback === '') return 0;
      const n = Number(fallback);
      return isNaN(n) ? 0 : n;
    },
    /**
     * 判断某个知识点条目是否"已加入练习"
     * 判定：父级 prop addedKnowledgeIds 中有没有当前 knowledgeId 字段
     */
    isKnowledgeAdded(item) {
      const id = item?.knowledgeId;
      if (!id) return false;
      return !!(this.addedKnowledgeIds
        && Object.prototype.hasOwnProperty.call(this.addedKnowledgeIds, String(id)));
    },
    /**
     * 判断某个知识点条目是不是"当前联动选中"（跨区单选：三/四个区只能有一条高亮）
     * 把父级 selectedKnowledgeId 和条目 knowledgeId 做字符串比较即可
     */
    isKnowledgeSelected(item) {
      const sid = this.selectedKnowledgeId;
      const id = item?.knowledgeId;
      if (!sid || id === null || id === undefined || id === '') return false;
      return String(sid) === String(id);
    },
    /**
     * 【按 knowledgeType 精准分区匹配】联动选中时查找条目并滚动到中间
     * 规则（和你描述一致）：
     *  knowledgeType === 1 → 薄弱补强区 weakArea / weakRowsBox 容器
     *  knowledgeType === 2 → 能力提升区 goodArea / goodRowsBox
     *  knowledgeType === 3 → 扎实巩固区 excellentArea / excellentRowsBox
     *  其它（4/未知）→ 冲刺进阶区 advancedArea / advancedRowsBox（暂时隐藏）
     * 只在对应分区的数组里做 knowledgeId 的 O(n) 查找，不需要对比三个数组，效率高
     *
     *  @returns {boolean} true=找到条目并执行了滚动；false=数据/DOM未就绪需要重试
     */
    highlightAndScrollByKnowledgeId(sid) {
      if (!sid) return false;
      // 第一步：找命中条目（只查其 knowledgeType 决定的对应分区数组）
      let zoneBoxRef = null;
      const zones = [
        { type: 1, list: this.weakArea,      ref: 'weakRowsBox'      },
        { type: 2, list: this.goodArea,      ref: 'goodRowsBox'      },
        { type: 3, list: this.excellentArea, ref: 'excellentRowsBox' },
        { type: 4, list: this.advancedArea,  ref: 'advancedRowsBox'  }
      ];

      // 先尝试"根据命中条目自己的 knowledgeType 定位分区"：需要知道 ID 对应哪个 type，先 O(n) 找条目
      // 折中：如果某一个分区数组先匹配到 knowledgeId，就直接在该分区内滚动——也是只遍历最多"总数组数*平均长度"，
      // 但用户担心的是"把三个数组都循环一遍效率慢"——这里通过先 type 预判 + 找到即停，最差仍是 3*N 但 N 是分区各自长度，且实际 90% 场景第一个命中就 break。
      let matchedItem = null;
      for (const z of zones) {
        const list = Array.isArray(z.list) ? z.list : [];
        const hit = list.find(itm => String(itm.knowledgeId) === sid);
        if (hit) {
          matchedItem = hit;
          zoneBoxRef = z.ref;
          break;
        }
      }
      // 没找到条目：说明父级 partitionData 还没被接口赋值（空数组），返回 false 让外层重试
      if (!matchedItem || !zoneBoxRef) return false;

      // 第二步：找到该条目对应的 DOM（用 data-knowledge-id 属性），在其所属分区滚动容器中居中显示
      //  ——关键：只滚动该区的 knowledge_rows（overflow-y:auto），不要带动整个页面滚动
      //    （因此禁止使用 element.scrollIntoView，它会从 window 开始递归把祖先滚动容器都对齐）
      const box = this.$refs[zoneBoxRef];
      const container = box && (box.$el || box);
      // 容器 DOM 还未渲染（例如 ref 还没挂载：created 阶段 immediate watch 触发）→ 重试
      if (!container) return false;
      const idAttr = CSS.escape ? CSS.escape(sid) : sid;
      const rowEl = container.querySelector
        ? container.querySelector(`[data-knowledge-id="${idAttr}"]`)
        : null;
      // 行节点还没被 v-for patch 出来（接口刚赋值，Vue 下一 tick 才渲染）→ 重试
      if (!rowEl) return false;

      // 用 getBoundingClientRect 的差值算"行相对于容器内容顶边的绝对偏移"
      // 这样即使容器内部有 padding / border / 行嵌套其他节点导致 offsetParent 不是 container，也能精确定位
      let containerRect = null;
      let rowRect = null;
      try {
        containerRect = container.getBoundingClientRect();
        rowRect = rowEl.getBoundingClientRect();
      } catch (e) {
        return false;
      }
      if (!containerRect || !rowRect) return false;
      const relativeOffsetTop = (rowRect.top - containerRect.top) + (container.scrollTop || 0);
      const rowHeight = rowRect.height || 40;
      const viewportH = container.clientHeight || 0;
      const rawTarget = relativeOffsetTop - (viewportH / 2) + (rowHeight / 2);
      const maxScrollTop = Math.max(0, (container.scrollHeight || 0) - viewportH);
      const finalTop = Math.max(0, Math.min(maxScrollTop, rawTarget));

      try {
        if (typeof container.scrollTo === 'function') {
          // 只作用在当前分区容器上，window/父容器都不会动
          container.scrollTo({ top: finalTop, behavior: 'smooth' });
        } else {
          container.scrollTop = finalTop;
        }
      } catch (e) {
        try { container.scrollTop = finalTop; } catch (err) {}
      }
      return true;
    },
    /**
     * 调度"高亮+滚动居中"：如果本轮没找到条目/DOM 就间隔 60ms 再重试，最多 5 次（≈300ms），
     * 覆盖以下时序：
     *  1) keep-alive activated 回来，DOM 已在但数据可能未同步好
     *  2) watch selectedKnowledgeId immediate 先跑 → 接口数据还没来
     *  3) partitionData 接口刚返回 → Vue v-for 还没把行 DOM patch 出来（computed 更新但 DOM 滞后）
     */
    scheduleHighlightScroll(sid, attempt = 0) {
      const MAX_ATTEMPTS = 5;
      if (!sid || attempt >= MAX_ATTEMPTS) return;
      const ok = this.highlightAndScrollByKnowledgeId(String(sid));
      if (ok) return; // 已经成功滚动到目标行
      // 没命中：用 setTimeout 延迟再试（下一 tick 可能数据/DOM 就好了）
      setTimeout(() => this.scheduleHighlightScroll(sid, attempt + 1), 60);
    },
    /**
     * 点击某个知识点行：
     *  - 联动：emit('item-click', item) 给父级，父级写 currentLinkedKnowledgeId
     *    → 一方面本组件通过 :selectedKnowledgeId 回传后天然高亮（跨区单选）
     *    → 另一方面 circularDiffusion 会放大对应圆点 + 右侧折线表格显示数据
     */
    handleRowClick(item) {
      if (!item || !item.knowledgeId) return;
      this.$emit('item-click', item);
    },
    // 处理加入练习
    handleAddPractice(item) {
      this.$emit('add-practice', item);
    }
  }
};
</script>

<style lang="scss" scoped>
.region_container {
  display: flex;
  flex-direction: row;
  gap: 16px;
  padding: 16px;
  width: 100%;
  box-sizing: border-box;
  align-items: stretch;
  flex-wrap: wrap;
}

/* 单个区域卡片（通用样式：圆角作为通用属性） */
.region_section {
  flex: 1 1 0;
  min-width: 0;
  border-radius: 10px 10px 10px 10px;
  padding: 16px 16px 12px;
  background: #FFFFFF;
  display: flex;
  flex-direction: column;
  height: 560px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
  overflow: hidden;

  &.weak {
    border: 1px solid #F56C6C;
    background: linear-gradient(180deg, rgba(245, 108, 108, 0.1) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF;
  }

  &.good {
    border: 1px solid #FAC858;
    background: linear-gradient(180deg, rgba(250, 200, 88, 0.1) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF;
  }

  &.excellent {
    border: 1px solid #3BA272;
    background: linear-gradient(180deg, rgba(59, 162, 114, 0.1) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF;
  }

  &.advanced {
    border: 1px solid #5470C6;
    background: linear-gradient(180deg, rgba(84, 112, 198, 0.1) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF;
  }
}

/* 卡片头部：图标方块 + 标题（同一行） + 副标题（下一行） */
.region_header {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: 6px; /* 上一行（图标+标题） 与 下一行（描述） 的竖向间距 */
  margin-bottom: 14px;

  /* 第一行容器：图标 + 标题 同一行，垂直居中 */
  .region_header_main {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
  }

  .region_icon {
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 32px;

    img {
      width: 100%;
      height: 100%;
      object-fit: contain;
      display: block;
    }
    svg {
      display: block;
    }
  }

  .region_title {
    font-size: 24px;
    font-weight: 600;
    color: #333333;
    line-height: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
  }

  .region_desc {
    font-size: 14px;
    color: #666666;
    line-height: 1.4;
  }
}

/* 行列表容器：仍然可滚动，但视觉上隐藏滚动条 */
.knowledge_rows {
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow-y: auto;
  flex: 1;
  min-height: 0;
  position: relative;

  /* Webkit 系列（Chrome/Safari/新 Edge）：彻底隐藏滚动条 */
  &::-webkit-scrollbar {
    width: 0;
    height: 0;
    display: none;
  }

  &::-webkit-scrollbar-thumb {
    display: none;
  }

  &::-webkit-scrollbar-track {
    display: none;
  }

  /* Firefox：隐藏滚动条 */
  scrollbar-width: none;
  /* IE / 旧版 Edge：隐藏滚动条 */
  -ms-overflow-style: none;
}

.empty_tip {
  height: 240px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100%;
  border-radius: 8px;

  span {
    margin-top: 23%;
  }
}

/* 每行知识点条目 */
.knowledge_row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px;
  border-radius: 4px;
  border: 1px solid;
  background: #FFFFFF;
  min-width: 0;
  cursor: pointer;
  .row_left {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
    flex: 1;

    .kp_name {
      font-weight: 500;
      font-size: 14px;
      color: #333333;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      margin-bottom: 8px;
    }

    .kp_meta {
      display: flex;
      align-items: center;
      gap: 10px;
      flex-wrap: wrap;
      color: #999999; /* 考频字色固定浅灰 */
      font-size: 12px;
    }
  }

  .row_right {
    flex-shrink: 0;

    .add_btn {
      padding: 8px 12px;
      font-size: 14px;
      line-height: 1;
      border-radius: 4px;
    }
  }
}

/* 四个区的条目边框 + 按钮颜色 + 得分率主题色 + 行悬浮/选中背景 */
.weak .knowledge_row {
  border-color: #F2B5B5;
  background: #FFFFFF;
  transition: background-color 0.15s ease;
}
.weak .knowledge_row:hover {
  background: rgba(245, 108, 108, 0.1);
}
.weak .knowledge_row.selected {
  background: rgba(245, 108, 108, 0.15);
}
.weak .kp_meta .kp_score {
  color: #F56C6C; /* 薄弱区：得分率和卡片主题色一致 */
}

.weak .knowledge_row .add_btn.weak_btn {
  color: #EE6666;
  border: 1px solid #EE6666;
  background: #FFFFFF;
}

.good .knowledge_row {
  border-color: #F7DEA5;
  background: #FFFFFF;
  transition: background-color 0.15s ease;
}
.good .knowledge_row:hover {
  background: rgba(250, 200, 88, 0.1);
}
.good .knowledge_row.selected {
  background: rgba(250, 200, 88, 0.15);
}
.good .kp_meta .kp_score {
  color: #FAC858; /* 能力提升区：得分率和卡片主题色一致 */
}

.good .knowledge_row .add_btn.good_btn {
  color: #F0B429;
  border: 1px solid #F0B429;
  background: #FFFFFF;
}

.excellent .knowledge_row {
  border-color: #AEE2C5;
  background: #FFFFFF;
  transition: background-color 0.15s ease;
}
.excellent .knowledge_row:hover {
  background: rgba(59, 162, 114, 0.1);
}
.excellent .knowledge_row.selected {
  background: rgba(59, 162, 114, 0.15);
}
.excellent .kp_meta .kp_score {
  color: #3BA272; /* 扎实巩固区：得分率和卡片主题色一致 */
}

.excellent .knowledge_row .add_btn.excellent_btn {
  color: #3BA272;
  border: 1px solid #3BA272;
  background: #FFFFFF;
}

.advanced .knowledge_row {
  border-color: #B9CBFC;
  background: #FFFFFF;
  transition: background-color 0.15s ease;
}
.advanced .knowledge_row:hover {
  background: rgba(84, 112, 198, 0.1);
}
.advanced .knowledge_row.selected {
  background: rgba(84, 112, 198, 0.15);
}
.advanced .kp_meta .kp_score {
  color: #5470C6; /* 冲刺进阶区：得分率和卡片主题色一致（暂时隐藏） */
}

.advanced .knowledge_row .add_btn.advanced_btn {
  color: #2E64FA;
  border: 1px solid #2E64FA;
  background: #FFFFFF;
}

/* 【按钮置灰】加入练习成功后 :disabled 状态颜色统一为浅灰色
 *  必须写和 4 个主题色相同的高权重选择器链（.区 .knowledge_row .add_btn.区_btn），
 *  并叠加 .is-disabled / [disabled]，否则会被上面的主题色（4 层 class 特异性）覆盖，表现为"按钮不能点但颜色没变灰"
 */
.weak .knowledge_row .add_btn.weak_btn.is-disabled,
.weak .knowledge_row .add_btn.weak_btn[disabled],
.good .knowledge_row .add_btn.good_btn.is-disabled,
.good .knowledge_row .add_btn.good_btn[disabled],
.excellent .knowledge_row .add_btn.excellent_btn.is-disabled,
.excellent .knowledge_row .add_btn.excellent_btn[disabled],
.advanced .knowledge_row .add_btn.advanced_btn.is-disabled,
.advanced .knowledge_row .add_btn.advanced_btn[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;
  }
}

/* 响应式：小屏适配 */
@media (max-width: 1366px) {
  .region_container {
    gap: 10px;
    padding: 10px;
  }
  .region_section {
    height: 460px;
    padding: 12px 12px 8px;
  }
  .region_header {
    margin-bottom: 10px;
    gap: 4px;
  }
}

@media (max-width: 1200px) {
  .region_section {
    height: auto;
    min-height: 320px;
  }
  .knowledge_rows {
    flex: 1;
    min-height: 200px;
  }
}
</style>