فهرست منبع

逐步修复 bug——修复线的连接问题

yemeishu 1 ماه پیش
والد
کامیت
78bff2ac15

+ 13 - 0
app/Filament/Pages/StudentKnowledgeGraphPage.php

@@ -6,6 +6,7 @@ use BackedEnum;
 use UnitEnum;
 use Filament\Pages\Page;
 use App\Livewire\StudentKnowledgeGraph;
+use Illuminate\Support\Arr;
 
 class StudentKnowledgeGraphPage extends Page
 {
@@ -19,9 +20,21 @@ class StudentKnowledgeGraphPage extends Page
 
     protected string $view = 'filament.pages.student-knowledge-graph-page';
 
+    public bool $showNodeDetails = true;
+    public string $detailLayout = 'inline';
+
     public function mount(): void
     {
         // Page 类不需要 authorizeResourceAccess
+        $this->showNodeDetails = filter_var(
+            request()->query('show_details', true),
+            FILTER_VALIDATE_BOOLEAN
+        );
+
+        $layout = request()->query('detail_layout', 'inline');
+        $this->detailLayout = in_array($layout, ['inline', 'drawer'], true)
+            ? $layout
+            : 'inline';
     }
 
     public function getBreadcrumbs(): array

+ 7 - 1
app/Livewire/StudentKnowledgeGraph.php

@@ -19,13 +19,19 @@ class StudentKnowledgeGraph extends Component
     public $isLoading = false;
 
     public $students = [];
+    public bool $showNodeDetails = true;
+    public string $detailLayout = 'inline';
 
     protected $rules = [
         'selectedStudentId' => 'required|exists:students,student_id',
     ];
 
-    public function mount()
+    public function mount(bool $showNodeDetails = true, string $detailLayout = 'inline')
     {
+        $this->showNodeDetails = $showNodeDetails;
+        $this->detailLayout = in_array($detailLayout, ['inline', 'drawer'], true)
+            ? $detailLayout
+            : 'inline';
         $this->loadStudents();
     }
 

+ 48 - 23
public/js/knowledge-mindmap-graph.js

@@ -20,6 +20,8 @@ class KnowledgeMindmapGraph {
             { prerequisite: 'P05', target: 'P06', threshold: 0.6 },
         ];
         this.parentMap = {};
+        this.clickTimer = null;
+        this.clickDelay = 220;
 
         this.setMasteryData(options.masteryData || {});
     }
@@ -258,15 +260,6 @@ class KnowledgeMindmapGraph {
                 default: [
                     'drag-canvas',
                     'zoom-canvas',
-                    {
-                        type: 'collapse-expand',
-                        trigger: 'click',
-                        onChange: (item, collapsed) => {
-                            if (!item) return;
-                            item.getModel().collapsed = collapsed;
-                            return true;
-                        },
-                    },
                 ],
             },
             defaultNode: {
@@ -503,23 +496,22 @@ class KnowledgeMindmapGraph {
             this.hideTooltip();
         });
 
-        this.graph.on('node:click', (evt) => {
-            const model = evt.item?.getModel();
-            if (!model) return;
+        this.graph.on('node:click', (evt) => this.handleNodeClick(evt));
 
-            if (model.locked) {
-                return;
+        // 双击用于折叠/展开
+        this.graph.on('node:dblclick', (evt) => {
+            if (this.clickTimer) {
+                clearTimeout(this.clickTimer);
+                this.clickTimer = null;
             }
-
-            this.graph.getNodes().forEach((node) => {
-                this.graph.clearItemStates(node);
-            });
-            this.graph.setItemState(evt.item, 'selected', true);
-            this.flashNode(evt.item);
-            if (this.emitSelection) {
-                this.notifySelection(model);
+            const model = evt.item?.getModel();
+            if (!model) return;
+            if (model.children && model.children.length > 0) {
+                const nextState = !model.collapsed;
+                this.graph.updateItem(evt.item, { collapsed: nextState });
+                this.graph.layout?.();
+                this.redrawRelationEdges();
             }
-            this.showTooltip(evt, model);
         });
 
         this.graph.on('canvas:click', () => {
@@ -573,6 +565,16 @@ class KnowledgeMindmapGraph {
     }
 
     notifySelection(model) {
+        // 派发全局事件,便于外层(Alpine/Livewire)捕获
+        try {
+            const event = new CustomEvent('mindmap-node-selected', {
+                detail: model,
+            });
+            window.dispatchEvent(event);
+        } catch (e) {
+            // ignore dispatch errors
+        }
+
         if (typeof this.onNodeSelect === 'function') {
             this.onNodeSelect(model);
             return;
@@ -777,6 +779,29 @@ class KnowledgeMindmapGraph {
         if (!this.graph) return;
     }
 
+    handleNodeClick(evt) {
+        if (this.clickTimer) {
+            clearTimeout(this.clickTimer);
+            this.clickTimer = null;
+        }
+
+        this.clickTimer = setTimeout(() => {
+            const model = evt.item?.getModel();
+            if (!model) return;
+            if (model.locked) return;
+
+            this.graph.getNodes().forEach((node) => {
+                this.graph.clearItemStates(node);
+            });
+            this.graph.setItemState(evt.item, 'selected', true);
+            this.flashNode(evt.item);
+            if (this.emitSelection) {
+                this.notifySelection(model);
+            }
+            this.showTooltip(evt, model);
+        }, this.clickDelay);
+    }
+
     forceCollapse() {
         if (!this.graph) {
             setTimeout(() => this.forceCollapse(), 200);

+ 48 - 23
resources/js/knowledge-mindmap-graph.js

@@ -20,6 +20,8 @@ class KnowledgeMindmapGraph {
             { prerequisite: 'P05', target: 'P06', threshold: 0.6 },
         ];
         this.parentMap = {};
+        this.clickTimer = null;
+        this.clickDelay = 220;
 
         this.setMasteryData(options.masteryData || {});
     }
@@ -258,15 +260,6 @@ class KnowledgeMindmapGraph {
                 default: [
                     'drag-canvas',
                     'zoom-canvas',
-                    {
-                        type: 'collapse-expand',
-                        trigger: 'click',
-                        onChange: (item, collapsed) => {
-                            if (!item) return;
-                            item.getModel().collapsed = collapsed;
-                            return true;
-                        },
-                    },
                 ],
             },
             defaultNode: {
@@ -503,23 +496,22 @@ class KnowledgeMindmapGraph {
             this.hideTooltip();
         });
 
-        this.graph.on('node:click', (evt) => {
-            const model = evt.item?.getModel();
-            if (!model) return;
+        this.graph.on('node:click', (evt) => this.handleNodeClick(evt));
 
-            if (model.locked) {
-                return;
+        // 双击用于折叠/展开
+        this.graph.on('node:dblclick', (evt) => {
+            if (this.clickTimer) {
+                clearTimeout(this.clickTimer);
+                this.clickTimer = null;
             }
-
-            this.graph.getNodes().forEach((node) => {
-                this.graph.clearItemStates(node);
-            });
-            this.graph.setItemState(evt.item, 'selected', true);
-            this.flashNode(evt.item);
-            if (this.emitSelection) {
-                this.notifySelection(model);
+            const model = evt.item?.getModel();
+            if (!model) return;
+            if (model.children && model.children.length > 0) {
+                const nextState = !model.collapsed;
+                this.graph.updateItem(evt.item, { collapsed: nextState });
+                this.graph.layout?.();
+                this.redrawRelationEdges();
             }
-            this.showTooltip(evt, model);
         });
 
         this.graph.on('canvas:click', () => {
@@ -573,6 +565,16 @@ class KnowledgeMindmapGraph {
     }
 
     notifySelection(model) {
+        // 派发全局事件,便于外层(Alpine/Livewire)捕获
+        try {
+            const event = new CustomEvent('mindmap-node-selected', {
+                detail: model,
+            });
+            window.dispatchEvent(event);
+        } catch (e) {
+            // ignore dispatch errors
+        }
+
         if (typeof this.onNodeSelect === 'function') {
             this.onNodeSelect(model);
             return;
@@ -777,6 +779,29 @@ class KnowledgeMindmapGraph {
         if (!this.graph) return;
     }
 
+    handleNodeClick(evt) {
+        if (this.clickTimer) {
+            clearTimeout(this.clickTimer);
+            this.clickTimer = null;
+        }
+
+        this.clickTimer = setTimeout(() => {
+            const model = evt.item?.getModel();
+            if (!model) return;
+            if (model.locked) return;
+
+            this.graph.getNodes().forEach((node) => {
+                this.graph.clearItemStates(node);
+            });
+            this.graph.setItemState(evt.item, 'selected', true);
+            this.flashNode(evt.item);
+            if (this.emitSelection) {
+                this.notifySelection(model);
+            }
+            this.showTooltip(evt, model);
+        }, this.clickDelay);
+    }
+
     forceCollapse() {
         if (!this.graph) {
             setTimeout(() => this.forceCollapse(), 200);

+ 85 - 1
resources/views/filament/pages/knowledge-mindmap.blade.php

@@ -5,6 +5,26 @@
             graphInstance: null,
             stats: { nodes: 0, extraEdges: 0 },
             livewireId: '{{ $this->getId() }}',
+            selectedNode: null,
+            initEventListener() {
+                window.addEventListener('mindmap-node-selected', (evt) => {
+                    const model = evt.detail || {};
+                    const code = model?.meta?.code || model?.id;
+                    const mastery = model?.meta?.mastery_level ?? this.graphInstance?.masteryData?.[code]?.mastery_level ?? 0;
+                    const accuracy = model?.meta?.accuracy_rate ?? this.graphInstance?.masteryData?.[code]?.accuracy_rate ?? null;
+                    const attempts = model?.meta?.total_attempts ?? this.graphInstance?.masteryData?.[code]?.total_attempts ?? null;
+                    this.selectedNode = {
+                        id: model?.id,
+                        code,
+                        label: model?.label,
+                        mastery,
+                        accuracy,
+                        attempts,
+                        recommended: model?.meta?.recommended ?? false,
+                        skills: model?.meta?.skills || this.graphInstance?.masteryData?.[code]?.skills || [],
+                    };
+                });
+            },
 
             async initMindmap() {
                 try {
@@ -21,6 +41,22 @@
                         livewireMethod: 'openDrawer',
                         highlightLowMastery: true,
                         livewireId: this.livewireId,
+                        onNodeSelect: (model) => {
+                            const code = model?.meta?.code || model?.id;
+                            const mastery = model?.meta?.mastery_level ?? this.graphInstance?.masteryData?.[code]?.mastery_level ?? 0;
+                            const accuracy = model?.meta?.accuracy_rate ?? this.graphInstance?.masteryData?.[code]?.accuracy_rate ?? null;
+                            const attempts = model?.meta?.total_attempts ?? this.graphInstance?.masteryData?.[code]?.total_attempts ?? null;
+                            this.selectedNode = {
+                                id: model?.id,
+                                code,
+                                label: model?.label,
+                                mastery,
+                                accuracy,
+                                attempts,
+                                recommended: model?.meta?.recommended ?? false,
+                                skills: model?.meta?.skills || this.graphInstance?.masteryData?.[code]?.skills || [],
+                            };
+                        },
                     });
                     await this.graphInstance.init();
                     this.stats = this.graphInstance.stats;
@@ -43,7 +79,7 @@
                 }
             }
         }"
-        x-init="initMindmap()"
+        x-init="initEventListener(); initMindmap()"
         data-knowledge-mindmap-root
     >
         <div class="rounded-xl border border-slate-200 bg-white shadow-sm p-5">
@@ -99,6 +135,54 @@
             </div>
         </div>
 
+        <template x-if="selectedNode">
+            <div class="mt-4 grid grid-cols-1 gap-4 lg:grid-cols-3">
+                <div class="lg:col-span-2 rounded-xl border border-slate-200 bg-white p-4 shadow-sm">
+                    <div class="flex items-start justify-between">
+                        <div>
+                            <p class="text-sm text-slate-500">选中知识点</p>
+                            <h3 class="text-xl font-semibold text-slate-900" x-text="selectedNode.label || selectedNode.code"></h3>
+                            <p class="text-sm text-slate-500 mt-1" x-text="`ID: ${selectedNode.id || ''} · Code: ${selectedNode.code || ''}`"></p>
+                        </div>
+                        <div class="text-right">
+                            <p class="text-sm text-slate-500">掌握度</p>
+                            <p class="text-2xl font-bold" x-text="(selectedNode.mastery * 100).toFixed(1) + '%'"></p>
+                            <p class="text-xs text-slate-500" x-show="selectedNode.accuracy">准确率 <span x-text="(selectedNode.accuracy * 100).toFixed(1) + '%'"></span></p>
+                            <p class="text-xs text-slate-500" x-show="selectedNode.attempts">练习次数 <span x-text="selectedNode.attempts"></span></p>
+                        </div>
+                    </div>
+                    <div class="mt-4">
+                        <p class="text-sm font-semibold text-slate-700 mb-2">技能要点</p>
+                        <div class="flex flex-wrap gap-2">
+                            <template x-if="selectedNode.skills && selectedNode.skills.length">
+                                <template x-for="skill in selectedNode.skills" :key="skill">
+                                    <span class="rounded-full bg-slate-100 px-3 py-1 text-xs font-medium text-slate-700" x-text="skill"></span>
+                                </template>
+                            </template>
+                            <span x-show="!selectedNode.skills || !selectedNode.skills.length" class="text-sm text-slate-500">暂无技能要点</span>
+                        </div>
+                        <div class="mt-4">
+                            <a
+                                class="inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-medium text-white hover:bg-indigo-700"
+                                :href="selectedNode.code ? `/admin/knowledge-point-detail?kp_code=${encodeURIComponent(selectedNode.code)}` : '#'"
+                                target="_blank"
+                            >
+                                查看知识点详情
+                            </a>
+                        </div>
+                    </div>
+                </div>
+                <div class="rounded-xl border border-slate-200 bg-white p-4 shadow-sm space-y-2">
+                    <p class="text-sm font-semibold text-slate-700">快速信息</p>
+                    <div class="text-sm text-slate-600 space-y-1">
+                        <p><span class="font-medium text-slate-900">ID:</span><span x-text="selectedNode.id"></span></p>
+                        <p><span class="font-medium text-slate-900">Code:</span><span x-text="selectedNode.code"></span></p>
+                        <p><span class="font-medium text-slate-900">推荐关注:</span><span x-text="selectedNode.recommended ? '是' : '否'"></span></p>
+                    </div>
+                </div>
+            </div>
+        </template>
+
         <x-mindmap.detail-drawer
             :open="$drawerOpen"
             :details="$nodeDetails"

+ 89 - 2
resources/views/filament/pages/student-dashboard.blade.php

@@ -100,18 +100,67 @@
     <div
         class="mb-10 space-y-4"
         x-data="studentMindmapPanel('{{ $this->getId() }}')"
-        x-init="initMindmap()"
+        x-init="initEventListener(); initMindmap()"
         data-knowledge-mindmap-root
     >
         <div class="relative overflow-hidden rounded-2xl border border-slate-200 shadow-sm bg-white">
             <div
                 wire:ignore
                 id="student-mindmap"
-                class="knowledge-mindmap-canvas relative h-[78vh] min-h-[680px] w-full"
+                class="knowledge-mindmap-canvas relative h-[64vh] min-h-[560px] w-full"
             >
             </div>
         </div>
 
+        {{-- 内联知识点详情 --}}
+        <template x-if="selectedNode">
+            <div class="grid grid-cols-1 gap-4 lg:grid-cols-3">
+                <div class="lg:col-span-2 rounded-xl border border-slate-200 bg-white p-4 shadow-sm">
+                    <div class="flex items-start justify-between">
+                        <div>
+                            <p class="text-sm text-slate-500">选中知识点</p>
+                            <h3 class="text-xl font-semibold text-slate-900" x-text="selectedNode.label || selectedNode.code"></h3>
+                            <p class="text-sm text-slate-500 mt-1" x-text="`ID: ${selectedNode.id || ''} · Code: ${selectedNode.code || ''}`"></p>
+                        </div>
+                        <div class="text-right">
+                            <p class="text-sm text-slate-500">掌握度</p>
+                            <p class="text-2xl font-bold" x-text="(selectedNode.mastery * 100).toFixed(1) + '%'"></p>
+                            <p class="text-xs text-slate-500" x-show="selectedNode.accuracy">准确率 <span x-text="(selectedNode.accuracy * 100).toFixed(1) + '%'"></span></p>
+                            <p class="text-xs text-slate-500" x-show="selectedNode.attempts">练习次数 <span x-text="selectedNode.attempts"></span></p>
+                        </div>
+                    </div>
+                    <div class="mt-4">
+                        <p class="text-sm font-semibold text-slate-700 mb-2">技能要点</p>
+                        <div class="flex flex-wrap gap-2">
+                            <template x-if="selectedNode.skills && selectedNode.skills.length">
+                                <template x-for="skill in selectedNode.skills" :key="skill">
+                                    <span class="rounded-full bg-slate-100 px-3 py-1 text-xs font-medium text-slate-700" x-text="skill"></span>
+                                </template>
+                            </template>
+                            <span x-show="!selectedNode.skills || !selectedNode.skills.length" class="text-sm text-slate-500">暂无技能要点</span>
+                        </div>
+                        <div class="mt-4">
+                            <a
+                                class="inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-medium text-white hover:bg-indigo-700"
+                                :href="selectedNode.code ? `/admin/knowledge-point-detail?kp_code=${encodeURIComponent(selectedNode.code)}` : '#'"
+                                target="_blank"
+                            >
+                                查看知识点详情
+                            </a>
+                        </div>
+                    </div>
+                </div>
+                <div class="rounded-xl border border-slate-200 bg-white p-4 shadow-sm space-y-2">
+                    <p class="text-sm font-semibold text-slate-700">快速信息</p>
+                    <div class="text-sm text-slate-600 space-y-1">
+                        <p><span class="font-medium text-slate-900">ID:</span><span x-text="selectedNode.id"></span></p>
+                        <p><span class="font-medium text-slate-900">Code:</span><span x-text="selectedNode.code"></span></p>
+                        <p><span class="font-medium text-slate-900">推荐关注:</span><span x-text="selectedNode.recommended ? '是' : '否'"></span></p>
+                    </div>
+                </div>
+            </div>
+        </template>
+
         <x-mindmap.detail-drawer
             :open="$mindmapDrawerOpen"
             :details="$mindmapNodeDetails"
@@ -750,6 +799,28 @@
                 graphInstance: null,
                 stats: { nodes: 0, extraEdges: 0 },
                 livewireId,
+                selectedNode: null,
+
+                initEventListener() {
+                    window.addEventListener('mindmap-node-selected', (evt) => {
+                        const model = evt.detail || {};
+                        const code = model?.meta?.code || model?.id;
+                        const mastery = model?.meta?.mastery_level ?? this.graphInstance?.masteryData?.[code]?.mastery_level ?? 0;
+                        const accuracy = model?.meta?.accuracy_rate ?? this.graphInstance?.masteryData?.[code]?.accuracy_rate ?? null;
+                        const attempts = model?.meta?.total_attempts ?? this.graphInstance?.masteryData?.[code]?.total_attempts ?? null;
+                        this.selectedNode = {
+                            id: model?.id,
+                            code,
+                            label: model?.label,
+                            mastery,
+                            accuracy,
+                            attempts,
+                            recommended: model?.meta?.recommended ?? false,
+                            skills: model?.meta?.skills || this.graphInstance?.masteryData?.[code]?.skills || [],
+                        };
+                    });
+                },
+
                 async initMindmap() {
                     if (!window.KnowledgeMindmapGraph) {
                         return;
@@ -760,6 +831,22 @@
                         livewireMethod: 'openMindmapDrawer',
                         highlightLowMastery: true,
                         livewireId: this.livewireId,
+                        onNodeSelect: (model) => {
+                            const code = model?.meta?.code || model?.id;
+                            const mastery = model?.meta?.mastery_level ?? this.graphInstance?.masteryData?.[code]?.mastery_level ?? 0;
+                            const accuracy = model?.meta?.accuracy_rate ?? this.graphInstance?.masteryData?.[code]?.accuracy_rate ?? null;
+                            const attempts = model?.meta?.total_attempts ?? this.graphInstance?.masteryData?.[code]?.total_attempts ?? null;
+                            this.selectedNode = {
+                                id: model?.id,
+                                code,
+                                label: model?.label,
+                                mastery,
+                                accuracy,
+                                attempts,
+                                recommended: model?.meta?.recommended ?? false,
+                                skills: model?.meta?.skills || this.graphInstance?.masteryData?.[code]?.skills || [],
+                            };
+                        },
                     });
                     this.graphInstance.masteryData = @js($mindmapMasteryData ?? []);
                     await this.graphInstance.init();

+ 4 - 1
resources/views/filament/pages/student-knowledge-graph-page.blade.php

@@ -1,3 +1,6 @@
 <x-filament-panels::page>
-    @livewire('student-knowledge-graph')
+    @livewire('student-knowledge-graph', [
+        'showNodeDetails' => $showNodeDetails,
+        'detailLayout' => $detailLayout,
+    ])
 </x-filament-panels::page>

+ 54 - 1
resources/views/livewire/student-knowledge-graph.blade.php

@@ -235,6 +235,30 @@
                         </div>
                     </div>
                 @endif
+
+                @if($showNodeDetails && $detailLayout === 'inline')
+                    <div id="kg-node-detail" class="mt-6 bg-white shadow rounded-lg p-6 hidden">
+                        <div class="flex items-center justify-between">
+                            <div>
+                                <p class="text-sm text-gray-500">选中知识点</p>
+                                <h4 class="text-xl font-bold text-gray-900" id="kg-detail-title">-</h4>
+                                <p class="text-sm text-gray-500" id="kg-detail-code">-</p>
+                            </div>
+                            <div class="text-right">
+                                <p class="text-sm text-gray-500">掌握度</p>
+                                <p class="text-2xl font-bold text-gray-900" id="kg-detail-mastery">-</p>
+                                <p class="text-xs text-gray-500" id="kg-detail-accuracy">-</p>
+                                <p class="text-xs text-gray-500" id="kg-detail-attempts">-</p>
+                            </div>
+                        </div>
+                        <div class="mt-4">
+                            <p class="text-sm font-semibold text-gray-700 mb-2">技能要点</p>
+                            <div id="kg-detail-skills" class="flex flex-wrap gap-2 text-sm text-gray-700">
+                                <span class="text-gray-500">暂无技能要点</span>
+                            </div>
+                        </div>
+                    </div>
+                @endif
             @endif
         @else
             <!-- 选择提示 -->
@@ -256,6 +280,7 @@
         <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
 
         <script>
+            window.kgDetailLayout = '{{ $detailLayout ?? 'inline' }}';
             document.addEventListener('livewire:initialized', () => {
                 const knowledgeGraph = @this.knowledgePoints;
 
@@ -400,7 +425,35 @@
 
                 // 节点详情展示函数
                 function showNodeDetails(nodeData) {
-                    alert(`知识点详情:\n\n名称: ${nodeData.label}\n代码: ${nodeData.id}\n掌握度: ${(nodeData.mastery * 100).toFixed(1)}%\n\n点击确定继续浏览图谱`);
+                    if (window.kgDetailLayout === 'inline') {
+                        const panel = document.getElementById('kg-node-detail');
+                        if (!panel) return;
+                        panel.classList.remove('hidden');
+                        document.getElementById('kg-detail-title').textContent = nodeData.label || '-';
+                        document.getElementById('kg-detail-code').textContent = `ID: ${nodeData.id}`;
+                        document.getElementById('kg-detail-mastery').textContent = `${(nodeData.mastery * 100).toFixed(1)}%`;
+                        document.getElementById('kg-detail-accuracy').textContent = nodeData.accuracy ? `准确率: ${(nodeData.accuracy * 100).toFixed(1)}%` : '';
+                        document.getElementById('kg-detail-attempts').textContent = nodeData.attempts ? `练习次数: ${nodeData.attempts}` : '';
+                        const skillsWrap = document.getElementById('kg-detail-skills');
+                        if (skillsWrap) {
+                            skillsWrap.innerHTML = '';
+                            if (nodeData.skills && nodeData.skills.length) {
+                                nodeData.skills.forEach((s) => {
+                                    const span = document.createElement('span');
+                                    span.className = 'rounded-full bg-gray-100 px-3 py-1 text-xs font-medium text-gray-700';
+                                    span.textContent = s;
+                                    skillsWrap.appendChild(span);
+                                });
+                            } else {
+                                const span = document.createElement('span');
+                                span.className = 'text-gray-500';
+                                span.textContent = '暂无技能要点';
+                                skillsWrap.appendChild(span);
+                            }
+                        }
+                    } else {
+                        alert(`知识点详情:\n\n名称: ${nodeData.label}\n代码: ${nodeData.id}\n掌握度: ${(nodeData.mastery * 100).toFixed(1)}%\n\n点击确定继续浏览图谱`);
+                    }
                 }
 
                 // 导出PNG功能