|
|
@@ -4,7 +4,7 @@ class KnowledgeMindmapGraph {
|
|
|
this.rawTree = null;
|
|
|
this.treeData = null;
|
|
|
this.relationEdges = [];
|
|
|
- this.masteryData = options.masteryData || {};
|
|
|
+ this.masteryData = {};
|
|
|
this.masteryCache = {};
|
|
|
this.stats = { nodes: 0, extraEdges: 0 };
|
|
|
this.containerId = options.containerId || 'knowledge-mindmap';
|
|
|
@@ -13,10 +13,14 @@ class KnowledgeMindmapGraph {
|
|
|
this.livewireId = options.livewireId || null;
|
|
|
this.highlightLowMastery = options.highlightLowMastery ?? true;
|
|
|
this.emitSelection = options.emitSelection ?? true;
|
|
|
+ this.tooltipEl = null;
|
|
|
+ this.nodeIdSet = new Set();
|
|
|
this.lockRules = options.lockRules || [
|
|
|
{ prerequisite: 'P04', target: 'P05', threshold: 0.6 },
|
|
|
{ prerequisite: 'P05', target: 'P06', threshold: 0.6 },
|
|
|
];
|
|
|
+
|
|
|
+ this.setMasteryData(options.masteryData || {});
|
|
|
}
|
|
|
|
|
|
async init() {
|
|
|
@@ -47,6 +51,11 @@ class KnowledgeMindmapGraph {
|
|
|
this.masteryCache = {};
|
|
|
this.treeData = this.transformNode(this.rawTree);
|
|
|
this.relationEdges = this.normalizeEdges(rawEdges);
|
|
|
+ const flatIds = [];
|
|
|
+ this.collectIds(this.treeData, flatIds);
|
|
|
+ console.log('知识点总数', flatIds.length, '列表:', flatIds);
|
|
|
+ this.nodeIdSet = new Set(flatIds);
|
|
|
+ this.logMasteryCoverage();
|
|
|
this.stats = {
|
|
|
nodes: this.countNodes(this.treeData),
|
|
|
extraEdges: this.relationEdges.length,
|
|
|
@@ -62,8 +71,9 @@ class KnowledgeMindmapGraph {
|
|
|
node.label ||
|
|
|
`node-${Math.random().toString(36).slice(2, 8)}`;
|
|
|
const label = node.name || node.label || node.code || node.id || id;
|
|
|
+ const masteryInfo = this.masteryData[id] || null;
|
|
|
const masteryLevel = this.getMasteryLevel(id);
|
|
|
- const accuracy = this.masteryData[id]?.accuracy_rate || 0;
|
|
|
+ const accuracy = this.toNumber(masteryInfo?.accuracy_rate);
|
|
|
const recommended = masteryLevel < 0.6;
|
|
|
|
|
|
const model = {
|
|
|
@@ -71,14 +81,15 @@ class KnowledgeMindmapGraph {
|
|
|
label,
|
|
|
depth,
|
|
|
locked: false,
|
|
|
- collapsed: depth > 0 && (node.children || []).length > 0,
|
|
|
+ collapsed: depth > 0 && (node.children || []).length > 0, // 默认折叠所有有子节点的节点(除根节点)
|
|
|
meta: {
|
|
|
code: id,
|
|
|
name: label,
|
|
|
mastery_level: masteryLevel,
|
|
|
accuracy_rate: accuracy,
|
|
|
- total_attempts: this.masteryData[id]?.total_attempts || 0,
|
|
|
- mastery_info: this.masteryData[id] || null,
|
|
|
+ total_attempts: this.toNumber(masteryInfo?.total_attempts),
|
|
|
+ mastery_info: masteryInfo,
|
|
|
+ has_mastery: Boolean(masteryInfo),
|
|
|
recommended,
|
|
|
},
|
|
|
children: (node.children || [])
|
|
|
@@ -95,11 +106,7 @@ class KnowledgeMindmapGraph {
|
|
|
}
|
|
|
|
|
|
const remote = this.masteryData[id]?.mastery_level;
|
|
|
- const randomFallback = Math.random() * 0.55 + 0.25;
|
|
|
- const value =
|
|
|
- typeof remote === 'number' && !Number.isNaN(remote)
|
|
|
- ? remote
|
|
|
- : randomFallback;
|
|
|
+ const value = this.normalizeMasteryLevel(remote);
|
|
|
|
|
|
this.masteryCache[id] = value;
|
|
|
return value;
|
|
|
@@ -127,6 +134,7 @@ class KnowledgeMindmapGraph {
|
|
|
|
|
|
applyInitialCollapse(node, depth = 0) {
|
|
|
if (!node) return;
|
|
|
+ // 默认折叠所有有子节点的节点(除了根节点)
|
|
|
if (depth > 0 && node.children.length > 0) {
|
|
|
node.collapsed = true;
|
|
|
}
|
|
|
@@ -211,6 +219,12 @@ class KnowledgeMindmapGraph {
|
|
|
return normalized;
|
|
|
}
|
|
|
|
|
|
+ collectIds(node, bucket) {
|
|
|
+ if (!node) return;
|
|
|
+ bucket.push(node.id);
|
|
|
+ (node.children || []).forEach((child) => this.collectIds(child, bucket));
|
|
|
+ }
|
|
|
+
|
|
|
renderGraph() {
|
|
|
const container = document.getElementById(this.containerId);
|
|
|
if (!container) return;
|
|
|
@@ -219,6 +233,9 @@ class KnowledgeMindmapGraph {
|
|
|
const width = Math.max(bounds.width, 640);
|
|
|
const height = Math.max(bounds.height, 640);
|
|
|
|
|
|
+ // 直接在数据层面设置折叠状态
|
|
|
+ this.setCollapsedState(this.treeData);
|
|
|
+
|
|
|
this.graph = new G6.TreeGraph({
|
|
|
container: this.containerId,
|
|
|
width,
|
|
|
@@ -270,16 +287,38 @@ class KnowledgeMindmapGraph {
|
|
|
getWidth: () => 150,
|
|
|
getVGap: () => 18,
|
|
|
getHGap: () => 50,
|
|
|
+ preventOverlap: true,
|
|
|
},
|
|
|
});
|
|
|
|
|
|
this.graph.data(this.treeData);
|
|
|
this.graph.render();
|
|
|
this.graph.fitView(12);
|
|
|
+ // 暴露实例便于调试
|
|
|
+ window.KnowledgeMindmapGraphInstance = this;
|
|
|
+ window.KnowledgeMindmapG6Graph = this.graph;
|
|
|
+
|
|
|
this.drawRelationEdges();
|
|
|
this.applyNodeStates();
|
|
|
this.startEdgeFlows();
|
|
|
this.focusOnLowestMastery();
|
|
|
+ this.repaintNodes();
|
|
|
+ }
|
|
|
+
|
|
|
+ setCollapsedState(nodeData, depth = 0) {
|
|
|
+ if (!nodeData) return;
|
|
|
+
|
|
|
+ // 折叠除根节点外的所有有子节点的节点
|
|
|
+ if (depth > 0 && nodeData.children && nodeData.children.length > 0) {
|
|
|
+ if (nodeData.collapsed === undefined) {
|
|
|
+ nodeData.collapsed = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 递归处理子节点
|
|
|
+ if (nodeData.children) {
|
|
|
+ nodeData.children.forEach(child => this.setCollapsedState(child, depth + 1));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
clearRelationEdges() {
|
|
|
@@ -295,6 +334,11 @@ class KnowledgeMindmapGraph {
|
|
|
drawRelationEdges() {
|
|
|
if (!this.graph || !this.relationEdges.length) return;
|
|
|
this.relationEdges.forEach((edge) => {
|
|
|
+ // 跳过缺失节点的边,避免 G6 报错导致后续渲染异常
|
|
|
+ if (!this.graph.findById(edge.source) || !this.graph.findById(edge.target)) {
|
|
|
+ console.warn('[mindmap] 跳过无效关联边', edge.id || edge.source + '-' + edge.target);
|
|
|
+ return;
|
|
|
+ }
|
|
|
this.graph.addItem('edge', edge);
|
|
|
});
|
|
|
}
|
|
|
@@ -304,13 +348,16 @@ class KnowledgeMindmapGraph {
|
|
|
this.graph.getNodes().forEach((node) => {
|
|
|
const model = node.getModel();
|
|
|
const mastery = model.meta?.mastery_level ?? 0;
|
|
|
+ const hasMastery =
|
|
|
+ model.meta?.has_mastery ?? Boolean(this.masteryData[model.id]);
|
|
|
+ this.graph.setItemState(node, 'dimmed', !hasMastery);
|
|
|
if (model.locked) {
|
|
|
this.graph.setItemState(node, 'locked', true);
|
|
|
}
|
|
|
- if (mastery < 0.4 && this.highlightLowMastery) {
|
|
|
+ if (hasMastery && mastery < 0.4 && this.highlightLowMastery) {
|
|
|
this.graph.setItemState(node, 'weak', true);
|
|
|
}
|
|
|
- if (mastery >= 0.8 && !model.locked) {
|
|
|
+ if (hasMastery && mastery >= 0.8 && !model.locked) {
|
|
|
this.playHalo(node);
|
|
|
}
|
|
|
});
|
|
|
@@ -393,6 +440,7 @@ class KnowledgeMindmapGraph {
|
|
|
if (!item || item.getModel().locked) return;
|
|
|
this.graph.setItemState(item, 'hover', true);
|
|
|
this.highlightNeighbors(item.getModel().id);
|
|
|
+ this.showTooltip(evt, item.getModel());
|
|
|
});
|
|
|
|
|
|
this.graph.on('node:mouseleave', (evt) => {
|
|
|
@@ -400,6 +448,7 @@ class KnowledgeMindmapGraph {
|
|
|
if (!item) return;
|
|
|
this.graph.setItemState(item, 'hover', false);
|
|
|
this.clearNeighborHighlight();
|
|
|
+ this.hideTooltip();
|
|
|
});
|
|
|
|
|
|
this.graph.on('node:click', (evt) => {
|
|
|
@@ -418,6 +467,7 @@ class KnowledgeMindmapGraph {
|
|
|
if (this.emitSelection) {
|
|
|
this.notifySelection(model);
|
|
|
}
|
|
|
+ this.showTooltip(evt, model);
|
|
|
});
|
|
|
|
|
|
this.graph.on('canvas:click', () => {
|
|
|
@@ -425,6 +475,7 @@ class KnowledgeMindmapGraph {
|
|
|
this.graph.clearItemStates(node);
|
|
|
});
|
|
|
this.clearNeighborHighlight();
|
|
|
+ this.hideTooltip();
|
|
|
});
|
|
|
}
|
|
|
|
|
|
@@ -489,10 +540,67 @@ class KnowledgeMindmapGraph {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ ensureTooltipEl() {
|
|
|
+ if (this.tooltipEl) return this.tooltipEl;
|
|
|
+ const div = document.createElement('div');
|
|
|
+ div.style.position = 'fixed';
|
|
|
+ div.style.zIndex = '9999';
|
|
|
+ div.style.pointerEvents = 'none';
|
|
|
+ div.style.padding = '10px 12px';
|
|
|
+ div.style.background = 'rgba(15,23,42,0.95)';
|
|
|
+ div.style.color = '#e2e8f0';
|
|
|
+ div.style.borderRadius = '10px';
|
|
|
+ div.style.boxShadow = '0 10px 30px rgba(0,0,0,0.18)';
|
|
|
+ div.style.fontSize = '12px';
|
|
|
+ div.style.lineHeight = '1.4';
|
|
|
+ document.body.appendChild(div);
|
|
|
+ this.tooltipEl = div;
|
|
|
+ return div;
|
|
|
+ }
|
|
|
+
|
|
|
+ showTooltip(evt, model) {
|
|
|
+ const tip = this.ensureTooltipEl();
|
|
|
+ const mastery = (model.meta?.mastery_level ?? 0) * 100;
|
|
|
+ const attempts = model.meta?.total_attempts ?? 0;
|
|
|
+ const recommended = model.meta?.recommended ? '是' : '否';
|
|
|
+ const locked = model.locked ? '是' : '否';
|
|
|
+ tip.innerHTML = `
|
|
|
+ <div style="font-weight:700;font-size:13px;">${model.id} · ${model.meta?.name || model.label}</div>
|
|
|
+ <div>掌握度:${mastery.toFixed(1)}%</div>
|
|
|
+ <div>推荐练习:${recommended}</div>
|
|
|
+ <div>尝试次数:${attempts}</div>
|
|
|
+ <div>锁定:${locked}</div>
|
|
|
+ `;
|
|
|
+ const x =
|
|
|
+ evt?.clientX ??
|
|
|
+ evt?.canvasX ??
|
|
|
+ evt?.x ??
|
|
|
+ evt?.event?.clientX ??
|
|
|
+ 0;
|
|
|
+ const y =
|
|
|
+ evt?.clientY ??
|
|
|
+ evt?.canvasY ??
|
|
|
+ evt?.y ??
|
|
|
+ evt?.event?.clientY ??
|
|
|
+ 0;
|
|
|
+ tip.style.left = `${x + 16}px`;
|
|
|
+ tip.style.top = `${y + 12}px`;
|
|
|
+ tip.style.opacity = '1';
|
|
|
+ }
|
|
|
+
|
|
|
+ hideTooltip() {
|
|
|
+ if (!this.tooltipEl) return;
|
|
|
+ this.tooltipEl.style.opacity = '0';
|
|
|
+ }
|
|
|
+
|
|
|
setupLivewireListeners() {
|
|
|
['mastery-updated', 'mindmap-mastery-updated'].forEach((event) => {
|
|
|
window.addEventListener(event, (detailEvent) => {
|
|
|
- this.masteryData = detailEvent.detail?.data || {};
|
|
|
+ const payload =
|
|
|
+ detailEvent.detail?.data ??
|
|
|
+ detailEvent.detail ??
|
|
|
+ {};
|
|
|
+ this.setMasteryData(payload);
|
|
|
this.refreshGraph();
|
|
|
});
|
|
|
});
|
|
|
@@ -538,19 +646,68 @@ class KnowledgeMindmapGraph {
|
|
|
|
|
|
this.masteryCache = {};
|
|
|
this.treeData = this.transformNode(this.rawTree);
|
|
|
+ const flatIds = [];
|
|
|
+ this.collectIds(this.treeData, flatIds);
|
|
|
+ this.nodeIdSet = new Set(flatIds);
|
|
|
+ this.logMasteryCoverage();
|
|
|
this.applyUnlockRules(this.treeData);
|
|
|
this.applyInitialCollapse(this.treeData);
|
|
|
this.expandForMastery();
|
|
|
|
|
|
- this.graph.changeData(this.treeData);
|
|
|
+ // 强制全量重绘,确保 meta/颜色更新
|
|
|
+ this.graph.clear();
|
|
|
+ this.graph.data(this.treeData);
|
|
|
this.graph.render();
|
|
|
+ this.repaintNodes();
|
|
|
this.graph.fitView(12);
|
|
|
+ // 暴露实例便于调试(刷新后仍可用)
|
|
|
+ window.KnowledgeMindmapGraphInstance = this;
|
|
|
+ window.KnowledgeMindmapG6Graph = this.graph;
|
|
|
|
|
|
this.clearRelationEdges();
|
|
|
this.drawRelationEdges();
|
|
|
this.applyNodeStates();
|
|
|
this.startEdgeFlows();
|
|
|
this.focusOnLowestMastery();
|
|
|
+ this.graph.paint();
|
|
|
+ }
|
|
|
+
|
|
|
+ forceCollapseNodes() {
|
|
|
+ if (!this.graph) return;
|
|
|
+ }
|
|
|
+
|
|
|
+ forceCollapse() {
|
|
|
+ if (!this.graph) {
|
|
|
+ setTimeout(() => this.forceCollapse(), 200);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const nodes = this.graph.getNodes();
|
|
|
+ let collapsedCount = 0;
|
|
|
+
|
|
|
+ nodes.forEach(node => {
|
|
|
+ const model = node.getModel();
|
|
|
+ // 折叠除根节点外的所有有子节点的节点
|
|
|
+ if (
|
|
|
+ this.masteryData[model.id] ||
|
|
|
+ (model.meta && this.masteryData[model.meta.code])
|
|
|
+ ) {
|
|
|
+ model.collapsed = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (model.depth > 0 && model.children && model.children.length > 0) {
|
|
|
+ try {
|
|
|
+ this.graph.collapseItem(node);
|
|
|
+ collapsedCount++;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('折叠节点失败:', model.id, error);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 重新渲染和适配视图
|
|
|
+ this.graph.refresh();
|
|
|
+ this.graph.fitView(12);
|
|
|
}
|
|
|
|
|
|
resizeGraph() {
|
|
|
@@ -560,6 +717,176 @@ class KnowledgeMindmapGraph {
|
|
|
this.graph.changeSize(container.clientWidth, container.clientHeight);
|
|
|
this.graph.fitView(12);
|
|
|
}
|
|
|
+
|
|
|
+ setMasteryData(payload = {}) {
|
|
|
+ this.masteryData = this.normalizeMasteryPayload(payload);
|
|
|
+ this.masteryCache = {};
|
|
|
+ this.logMasteryCoverage();
|
|
|
+ }
|
|
|
+
|
|
|
+ normalizeMasteryPayload(payload = {}) {
|
|
|
+ // 支持 {masteries: []} / {data: []} / {Target: {KP: {...}}} / 直接的键值对
|
|
|
+ const map = {};
|
|
|
+ const addEntry = (entry, fallbackKey = null) => {
|
|
|
+ if (!entry || typeof entry !== 'object') return;
|
|
|
+ const code =
|
|
|
+ entry.kp_code ||
|
|
|
+ entry.code ||
|
|
|
+ entry.id ||
|
|
|
+ fallbackKey;
|
|
|
+ if (!code) return;
|
|
|
+ const masteryLevel = this.normalizeMasteryLevel(
|
|
|
+ entry.mastery_level
|
|
|
+ );
|
|
|
+ map[code] = {
|
|
|
+ ...entry,
|
|
|
+ kp_code: code,
|
|
|
+ mastery_level: masteryLevel,
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ const normalizeCandidate = (candidate) => {
|
|
|
+ if (!candidate) return;
|
|
|
+ if (Array.isArray(candidate)) {
|
|
|
+ candidate.forEach((item) => addEntry(item));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (typeof candidate === 'object') {
|
|
|
+ Object.entries(candidate).forEach(([key, value]) =>
|
|
|
+ addEntry(value, key)
|
|
|
+ );
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ normalizeCandidate(payload.masteries);
|
|
|
+ normalizeCandidate(payload.data);
|
|
|
+ normalizeCandidate(payload.Target);
|
|
|
+ // 允许直接传入键值对
|
|
|
+ normalizeCandidate(payload);
|
|
|
+
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+
|
|
|
+ normalizeMasteryLevel(value) {
|
|
|
+ const num = this.toNumber(value);
|
|
|
+ if (!Number.isFinite(num)) return 0;
|
|
|
+ return Math.max(0, Math.min(1, num));
|
|
|
+ }
|
|
|
+
|
|
|
+ toNumber(value) {
|
|
|
+ const num = Number(value);
|
|
|
+ return Number.isFinite(num) ? num : 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ repaintNodes() {
|
|
|
+ if (!this.graph || !this.treeData) return;
|
|
|
+ const nodeMap = {};
|
|
|
+ const walk = (node) => {
|
|
|
+ if (!node) return;
|
|
|
+ nodeMap[node.id] = node;
|
|
|
+ (node.children || []).forEach((child) => walk(child));
|
|
|
+ };
|
|
|
+ walk(this.treeData);
|
|
|
+
|
|
|
+ let updatedCount = 0;
|
|
|
+ let firstUpdated = null;
|
|
|
+ this.graph.getNodes().forEach((node) => {
|
|
|
+ const id = node.getModel().id;
|
|
|
+ const freshModel = nodeMap[id];
|
|
|
+ if (!freshModel) return;
|
|
|
+ // 确保 meta 中 has_mastery 等字段存在
|
|
|
+ const masteryInfo =
|
|
|
+ this.masteryData[id] ||
|
|
|
+ this.masteryData[freshModel.meta?.code] ||
|
|
|
+ null;
|
|
|
+ freshModel.meta = {
|
|
|
+ ...freshModel.meta,
|
|
|
+ mastery_level: this.normalizeMasteryLevel(
|
|
|
+ masteryInfo?.mastery_level ?? freshModel.meta?.mastery_level
|
|
|
+ ),
|
|
|
+ has_mastery: Boolean(masteryInfo),
|
|
|
+ mastery_info: masteryInfo,
|
|
|
+ total_attempts: this.toNumber(
|
|
|
+ masteryInfo?.total_attempts ?? freshModel.meta?.total_attempts
|
|
|
+ ),
|
|
|
+ };
|
|
|
+ this.graph.updateItem(node, freshModel);
|
|
|
+ this.graph.refreshItem?.(node);
|
|
|
+ this.applyDirectStyles(node, freshModel.meta.mastery_level, freshModel.meta.has_mastery);
|
|
|
+ // 直接打上选中状态,避免样式被缓存
|
|
|
+ if (freshModel.meta.has_mastery) {
|
|
|
+ this.graph.setItemState(node, 'selected', true);
|
|
|
+ } else {
|
|
|
+ this.graph.clearItemStates(node, ['selected']);
|
|
|
+ }
|
|
|
+ updatedCount += 1;
|
|
|
+ if (!firstUpdated) {
|
|
|
+ firstUpdated = {
|
|
|
+ id,
|
|
|
+ mastery: freshModel.meta?.mastery_level,
|
|
|
+ has_mastery: freshModel.meta?.has_mastery,
|
|
|
+ };
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 强制重绘,避免样式缓存
|
|
|
+ this.graph.paint();
|
|
|
+ }
|
|
|
+
|
|
|
+ applyDirectStyles(node, mastery, hasMastery) {
|
|
|
+ // 直接对关键 shape 赋色,避免 G6 缓存导致颜色不变
|
|
|
+ const keyShape = node.getKeyShape?.();
|
|
|
+ const group = node.getContainer?.();
|
|
|
+ if (!keyShape || !group) return;
|
|
|
+
|
|
|
+ const palette = (() => {
|
|
|
+ if (!hasMastery) {
|
|
|
+ return { fill: '#f8fafc', stroke: '#cbd5e1', card: '#ffffff' };
|
|
|
+ }
|
|
|
+ if (mastery >= 0.8) return { fill: '#fffbeb', stroke: '#d3b55f', card: '#ffffff' };
|
|
|
+ if (mastery >= 0.6) return { fill: '#ecfdf3', stroke: '#34d399', card: '#ffffff' };
|
|
|
+ if (mastery >= 0.4) return { fill: '#fffbeb', stroke: '#f59e0b', card: '#fff7ed' };
|
|
|
+ return { fill: '#fef2f2', stroke: '#f87171', card: '#fff1f2' };
|
|
|
+ })();
|
|
|
+
|
|
|
+ keyShape.attr({
|
|
|
+ fill: palette.fill,
|
|
|
+ stroke: palette.stroke,
|
|
|
+ });
|
|
|
+
|
|
|
+ const card = group.find((e) => e.get?.('name') === 'card-shape');
|
|
|
+ if (card) {
|
|
|
+ card.attr({ fill: palette.card });
|
|
|
+ }
|
|
|
+ // 额外高亮轮廓,确保视觉可见
|
|
|
+ this.graph.setItemState(node, 'selected', hasMastery);
|
|
|
+ this.graph.refreshItem?.(node);
|
|
|
+ }
|
|
|
+
|
|
|
+ logMasteryCoverage() {
|
|
|
+ if (!this.nodeIdSet || !this.nodeIdSet.size) return;
|
|
|
+ const masteries = Object.keys(this.masteryData || {});
|
|
|
+ if (!masteries.length) return;
|
|
|
+ const missing = masteries.filter((id) => !this.nodeIdSet.has(id));
|
|
|
+ if (missing.length) {
|
|
|
+ console.warn(
|
|
|
+ '掌握度返回的知识点未在图谱中找到:',
|
|
|
+ missing.slice(0, 20),
|
|
|
+ missing.length > 20 ? `...共${missing.length}条` : ''
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// 定义KnowledgeMindmapGraph类,确保G6已加载
|
|
|
+function defineGraphClass() {
|
|
|
+ if (typeof window.G6 === 'undefined') {
|
|
|
+ setTimeout(defineGraphClass, 100);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ window.KnowledgeMindmapGraph = KnowledgeMindmapGraph;
|
|
|
}
|
|
|
|
|
|
-window.KnowledgeMindmapGraph = KnowledgeMindmapGraph;
|
|
|
+// 启动定义流程
|
|
|
+defineGraphClass();
|