|
|
@@ -15,10 +15,7 @@
|
|
|
</div>
|
|
|
<div class="header_stats">
|
|
|
<span class="stats_text">
|
|
|
- 你的知识图谱共包含 <span class="stats_num">{{ totalCount }}</span> 个知识点。当前
|
|
|
- 红色薄弱知识点<span class="stats_weak"> {{ weakCount }}</span> 个,
|
|
|
- 黄色待提升知识点<span class="stats_good"> {{ goodCount }}</span> 个,
|
|
|
- <span class="stats_excellent">坚持练习,把全部知识点都进阶为绿色已掌握状态吧!</span>
|
|
|
+ 你的知识图谱共包含<span class="stats_num">{{ totalCount }}</span> 个知识点。当前红色薄弱知识点<span class="stats_weak"> {{ weakCount }}</span> 个,黄色待提升知识点<span class="stats_good"> {{ goodCount }}</span> 个,<span class="stats_excellent">坚持练习,把全部知识点都进阶为绿色已掌握状态吧!</span>
|
|
|
</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -30,7 +27,6 @@
|
|
|
:currentKnowledgeId="currentLinkedKnowledgeId || currentKnowledgeId || defaultKnowledgeId"
|
|
|
:addedKnowledgeIds="addedKnowledgeIds"
|
|
|
@legend-change="handleLegendChange"
|
|
|
- @legend-visibility-change="handleLegendVisibilityChange"
|
|
|
@activeTabChange="handleActiveTabChange"
|
|
|
@add-practice="handleAddPractice"
|
|
|
@knowledge-item-click="handleKnowledgeItemClick" />
|
|
|
@@ -38,9 +34,7 @@
|
|
|
<!-- 区域展示组件 -->
|
|
|
<region
|
|
|
:partitionData="partitionData"
|
|
|
- :addedKnowledgeIds="addedKnowledgeIds"
|
|
|
:selectedKnowledgeId="currentLinkedKnowledgeId || currentKnowledgeId || defaultKnowledgeId"
|
|
|
- :legendVisibility="legendVisibility"
|
|
|
@add-practice="handleAddPractice"
|
|
|
@item-click="handleRegionItemClick" />
|
|
|
</div>
|
|
|
@@ -133,9 +127,7 @@ export default {
|
|
|
// - 点 region 某行条目 → 写这里 → circularDiffusion 读 prop :currentKnowledgeId 放大圆点 + 更新右侧折线表格
|
|
|
currentLinkedKnowledgeId: '',
|
|
|
// 假数据:表格树形结构数据(用于无接口数据时兜底)
|
|
|
- mockTableData: [],
|
|
|
- // 图例可见状态联动:薄弱(1)/良好(2)/优秀(3) → 控制 region.vue 对应区域显示/隐藏
|
|
|
- legendVisibility: { 1: true, 2: true, 3: true }
|
|
|
+ mockTableData: []
|
|
|
};
|
|
|
},
|
|
|
computed: {
|
|
|
@@ -215,6 +207,12 @@ export default {
|
|
|
this.fetchKnowledgeGraph();
|
|
|
this.fetchKnowledgePartition();
|
|
|
}
|
|
|
+ },
|
|
|
+ // 监听 vuex store 中 gradesList 变化(TargetSubject 异步接口返回后会更新 store)
|
|
|
+ '$store.state.target.gradesList'(newVal) {
|
|
|
+ if (Array.isArray(newVal) && newVal.length > 0 && this.gradeList.length === 0) {
|
|
|
+ this.loadGradeListFromStorage();
|
|
|
+ }
|
|
|
}
|
|
|
},
|
|
|
mounted() {
|
|
|
@@ -228,6 +226,10 @@ export default {
|
|
|
this.fetchKnowledgeGraph();
|
|
|
this.fetchKnowledgePartition();
|
|
|
},
|
|
|
+ // 当从其他路由切回来时(keep-alive),重新加载年级列表
|
|
|
+ activated() {
|
|
|
+ this.loadGradeListFromStorage();
|
|
|
+ },
|
|
|
beforeDestroy() {
|
|
|
// 移除窗口大小变化监听
|
|
|
window.removeEventListener('resize', this.calculateContainerHeight);
|
|
|
@@ -239,10 +241,18 @@ export default {
|
|
|
*/
|
|
|
loadGradeListFromStorage() {
|
|
|
try {
|
|
|
+ let list = [];
|
|
|
+ // 1. 优先从 localStorage 读取
|
|
|
const raw = localStorage.getItem('student_gradesList');
|
|
|
- if (!raw) return;
|
|
|
- const list = JSON.parse(raw);
|
|
|
- if (!Array.isArray(list)) return;
|
|
|
+ if (raw) {
|
|
|
+ const parsed = JSON.parse(raw);
|
|
|
+ if (Array.isArray(parsed)) list = parsed;
|
|
|
+ }
|
|
|
+ // 2. localStorage 没有则从 vuex store 兜底(store 初始化时也会从 localStorage 读)
|
|
|
+ if (list.length === 0 && this.$store.state.target?.gradesList) {
|
|
|
+ list = this.$store.state.target.gradesList;
|
|
|
+ }
|
|
|
+ if (!Array.isArray(list) || list.length === 0) return;
|
|
|
|
|
|
// 规范化字段:支持 label/value、label/code、name/value 等常见组合
|
|
|
this.gradeList = list.map(item => ({
|
|
|
@@ -252,7 +262,7 @@ export default {
|
|
|
|
|
|
// 默认只选中最后一个年级
|
|
|
// 例:[{高一,10},{高二,11}] → currentGradeList = ['11'](仅选中高二)
|
|
|
- // 同“加入练习接口 gradeCode = gradeList 末尾 value”规则保持一致
|
|
|
+ // 同"加入练习接口 gradeCode = gradeList 末尾 value"规则保持一致
|
|
|
this.currentGradeList = this.gradeList.length > 0
|
|
|
? [this.gradeList[this.gradeList.length - 1].value]
|
|
|
: [];
|
|
|
@@ -333,6 +343,8 @@ export default {
|
|
|
improveZone: this.mapPartitionItem(Array.isArray(d.improveZone) ? d.improveZone : []),
|
|
|
consolidateZone:this.mapPartitionItem(Array.isArray(d.consolidateZone)? d.consolidateZone : [])
|
|
|
};
|
|
|
+ // 默认选中:薄弱补强区第一条 → 能力提升区第一条 → 扎实巩固区第一条
|
|
|
+ this.setDefaultSelectionFromPartition();
|
|
|
} else {
|
|
|
this.resetPartitionEmpty();
|
|
|
}
|
|
|
@@ -511,6 +523,27 @@ export default {
|
|
|
this.fetchKnowledgeGraph();
|
|
|
this.fetchKnowledgePartition();
|
|
|
},
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 默认选中:薄弱补强区第一条 → 能力提升区第一条 → 扎实巩固区第一条
|
|
|
+ * 将选中 knowledgeId 写入 currentLinkedKnowledgeId,联动 circularDiffusion 圆点高亮
|
|
|
+ */
|
|
|
+ setDefaultSelectionFromPartition() {
|
|
|
+ const conquer = this.partitionData.conquerZone || [];
|
|
|
+ const improve = this.partitionData.improveZone || [];
|
|
|
+ const consolidate = this.partitionData.consolidateZone || [];
|
|
|
+ let firstItem = null;
|
|
|
+ if (conquer.length > 0) {
|
|
|
+ firstItem = conquer[0];
|
|
|
+ } else if (improve.length > 0) {
|
|
|
+ firstItem = improve[0];
|
|
|
+ } else if (consolidate.length > 0) {
|
|
|
+ firstItem = consolidate[0];
|
|
|
+ }
|
|
|
+ if (firstItem && firstItem.knowledgeId) {
|
|
|
+ this.currentLinkedKnowledgeId = String(firstItem.knowledgeId);
|
|
|
+ }
|
|
|
+ },
|
|
|
// 计算容器高度
|
|
|
calculateContainerHeight() {
|
|
|
if (this.$refs.knowledgeGraph) {
|
|
|
@@ -528,18 +561,6 @@ export default {
|
|
|
this.$emit('legend-change', data);
|
|
|
},
|
|
|
|
|
|
- // 处理图例可见状态变化:薄弱(1)/良好(2)/优秀(3) 的可见性
|
|
|
- // 同步更新到 legendVisibility,驱动 region.vue 对应区域显示/隐藏
|
|
|
- handleLegendVisibilityChange(visibility) {
|
|
|
- if (!visibility) return;
|
|
|
- // 保证响应式:直接替换整个对象引用
|
|
|
- this.legendVisibility = {
|
|
|
- 1: !!visibility[1],
|
|
|
- 2: !!visibility[2],
|
|
|
- 3: !!visibility[3]
|
|
|
- };
|
|
|
- },
|
|
|
-
|
|
|
// 处理tab切换
|
|
|
handleActiveTabChange(tab) {
|
|
|
this.$emit('activeTabChange', tab);
|
|
|
@@ -644,9 +665,11 @@ export default {
|
|
|
|
|
|
this.$api.target.addExercise(params).then(res => {
|
|
|
if (res && (res.code == 200 || res.code == 0)) {
|
|
|
- // 成功:提示 + 记录 ID(用 $set 保证响应式,region/circularDiffusion 会自动重新渲染并置灰按钮)
|
|
|
this.$message.success('已加入练习成功');
|
|
|
+ // 1. 记录已加入练习 ID → circularDiffusion 的加入练习按钮置灰
|
|
|
this.$set(this.addedKnowledgeIds, String(knowledgeId), true);
|
|
|
+ // 2. 从 region 分区列表中移除该条数据
|
|
|
+ this.removeKnowledgeFromPartition(knowledgeId);
|
|
|
} else {
|
|
|
this.$message.error(res.msg);
|
|
|
}
|
|
|
@@ -654,6 +677,28 @@ export default {
|
|
|
console.error('[KnowledgeGraph] addExercise 接口异常:', err);
|
|
|
this.$message.error('加入练习失败,请稍后重试');
|
|
|
});
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从分区列表中移除已加入练习的知识点
|
|
|
+ * 遍历三个分区数组,按 knowledgeId 删除匹配项
|
|
|
+ */
|
|
|
+ removeKnowledgeFromPartition(knowledgeId) {
|
|
|
+ const sid = String(knowledgeId);
|
|
|
+ const zones = ['conquerZone', 'improveZone', 'consolidateZone'];
|
|
|
+ let changed = false;
|
|
|
+ for (const zone of zones) {
|
|
|
+ const list = this.partitionData[zone];
|
|
|
+ if (!Array.isArray(list)) continue;
|
|
|
+ const idx = list.findIndex(item => String(item.knowledgeId) === sid);
|
|
|
+ if (idx !== -1) {
|
|
|
+ // 用 splice 保证响应式
|
|
|
+ this.partitionData[zone].splice(idx, 1);
|
|
|
+ changed = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return changed;
|
|
|
}
|
|
|
}
|
|
|
};
|