| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- <x-filament-panels::page>
- @push('styles')
- <style>
- .graph-container {
- width: 100%;
- height: 700px;
- border: 1px solid #e5e7eb;
- border-radius: 8px;
- background: #f9fafb;
- }
- </style>
- @endpush
- <div class="space-y-6">
- <!-- 页面标题和操作 -->
- <div class="flex justify-between items-center">
- <div>
- <h2 class="text-2xl font-bold text-gray-900">知识图谱可视化</h2>
- <p class="mt-1 text-sm text-gray-500">
- 基于学生掌握度的知识节点热力图,点击节点查看详细信息
- </p>
- </div>
- <div class="flex gap-3">
- <button
- wire:click="$refresh"
- type="button"
- class="filament-button filament-button-size-sm filament-button-color-gray filament-button-icon-start inline-flex items-center justify-center px-4 py-2 text-sm font-medium transition-colors border border-transparent rounded-lg focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
- >
- 刷新图谱
- </button>
- </div>
- </div>
- <!-- 学生选择器 -->
- <div class="bg-white p-6 rounded-lg border shadow-sm">
- <div class="flex items-center gap-4">
- <div class="flex-1">
- <select
- wire:model.live="selectedStudentId"
- class="form-select w-full px-3 py-2 border rounded-lg"
- >
- <option value="">-- 显示全部知识点(灰色) --</option>
- @foreach($this->getStudents() as $student)
- <option value="{{ $student->student_id }}">
- {{ $student->name ?? $student->student_id }}
- </option>
- @endforeach
- </select>
- </div>
- @if($selectedStudentId)
- <div class="text-sm text-gray-600">
- 当前查看:<span class="font-semibold">{{ $selectedStudentId }}</span> 的掌握度
- </div>
- @endif
- </div>
- </div>
- <!-- 图例 -->
- <div class="bg-white p-6 rounded-lg border shadow-sm">
- <h3 class="text-sm font-medium text-gray-900 mb-3">掌握度图例</h3>
- <div class="flex flex-wrap gap-3">
- <div class="flex items-center gap-2 px-3 py-1 rounded">
- <div style="width: 20px; height: 20px; background: #d1d5db; border-radius: 4px; border: 1px solid #d1d5db;"></div>
- <span class="text-sm text-gray-700">未学习</span>
- </div>
- <div class="flex items-center gap-2 px-3 py-1 rounded">
- <div style="width: 20px; height: 20px; background: #ef4444; border-radius: 4px; border: 1px solid #dc2626;"></div>
- <span class="text-sm text-gray-700">< 60% 需提升</span>
- </div>
- <div class="flex items-center gap-2 px-3 py-1 rounded">
- <div style="width: 20px; height: 20px; background: #fb923c; border-radius: 4px; border: 1px solid #f97316;"></div>
- <span class="text-sm text-gray-700">60-69% 及格</span>
- </div>
- <div class="flex items-center gap-2 px-3 py-1 rounded">
- <div style="width: 20px; height: 20px; background: #fbbf24; border-radius: 4px; border: 1px solid #f59e0b;"></div>
- <span class="text-sm text-gray-700">70-79% 中等</span>
- </div>
- <div class="flex items-center gap-2 px-3 py-1 rounded">
- <div style="width: 20px; height: 20px; background: #34d399; border-radius: 4px; border: 1px solid #10b981;"></div>
- <span class="text-sm text-gray-700">80-89% 良好</span>
- </div>
- <div class="flex items-center gap-2 px-3 py-1 rounded">
- <div style="width: 20px; height: 20px; background: #10b981; border-radius: 4px; border: 1px solid #059669;"></div>
- <span class="text-sm text-gray-700">≥ 90% 优秀</span>
- </div>
- </div>
- </div>
- <!-- 图谱容器 -->
- <div class="bg-white p-6 rounded-lg border shadow-sm">
- <div id="mountNode" class="graph-container"></div>
- </div>
- <!-- 操作提示 -->
- <div class="text-sm text-gray-600 bg-blue-50 p-4 rounded-lg">
- <div class="font-medium text-blue-900 mb-2">💡 操作提示</div>
- <ul class="space-y-1 text-blue-800">
- <li>• 鼠标拖拽画布移动视图,滚轮缩放</li>
- <li>• 拖拽节点调整位置</li>
- <li>• 鼠标悬停节点查看掌握度详情</li>
- <li>• 点击节点查看练习建议</li>
- </ul>
- </div>
- </div>
- @push('scripts')
- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.8.21/dist/g6.min.js"></script>
- <script>
- document.addEventListener('livewire:initialized', () => {
- const graphData = @js($graphData);
- const studentMasteryData = @js($studentMasteryData);
- const selectedStudentId = @js($selectedStudentId);
- // 构建掌握度映射
- const masteryMap = {};
- if (selectedStudentId && studentMasteryData) {
- studentMasteryData.forEach(item => {
- masteryMap[item.kp_code] = item.mastery;
- });
- }
- // 转换数据格式
- const nodes = graphData.nodes.map(node => {
- const kpCode = node.id;
- const mastery = masteryMap[kpCode];
- // 根据掌握度获取颜色
- let fillColor = '#d1d5db'; // 默认灰色(未学习)
- let strokeColor = '#9ca3af';
- if (mastery !== undefined) {
- if (mastery >= 0.9) {
- fillColor = '#10b981';
- strokeColor = '#059669';
- } else if (mastery >= 0.8) {
- fillColor = '#34d399';
- strokeColor = '#10b981';
- } else if (mastery >= 0.7) {
- fillColor = '#fbbf24';
- strokeColor = '#f59e0b';
- } else if (mastery >= 0.6) {
- fillColor = '#fb923c';
- strokeColor = '#f97316';
- } else {
- fillColor = '#ef4444';
- strokeColor = '#dc2626';
- }
- }
- return {
- ...node,
- style: {
- fill: fillColor,
- stroke: strokeColor,
- lineWidth: 2,
- radius: 6,
- },
- mastery: mastery,
- };
- });
- const edges = graphData.edges.map(edge => ({
- ...edge,
- style: {
- stroke: '#94a3b8',
- lineWidth: 1.5,
- opacity: 0.6,
- endArrow: true,
- },
- }));
- const container = document.getElementById('mountNode');
- const width = container.scrollWidth;
- const height = container.scrollHeight;
- // 创建图表
- const graph = new G6.Graph({
- container: 'mountNode',
- width,
- height,
- fitView: true,
- fitViewPadding: [20, 20, 20, 20],
- modes: {
- default: ['drag-canvas', 'zoom-canvas', 'drag-node'],
- },
- layout: {
- type: 'dagre',
- rankdir: 'LR',
- align: 'UL',
- controlPoints: true,
- nodesep: 20,
- ranksep: 60,
- },
- defaultNode: {
- type: 'rect',
- size: [160, 50],
- labelCfg: {
- position: 'center',
- style: {
- fontSize: 12,
- },
- },
- },
- defaultEdge: {
- type: 'polyline',
- style: {
- radius: 10,
- offset: 30,
- },
- },
- });
- // 渲染图表
- graph.data({
- nodes: nodes,
- edges: edges,
- });
- graph.render();
- // 绑定事件
- graph.on('node:click', (evt) => {
- const node = evt.item;
- const model = node.getModel();
- const kpCode = model.id;
- const kpName = model.label || kpCode;
- const mastery = model.mastery;
- // 显示详细信息
- let message = `知识点:${kpName}\n`;
- message += `代码:${kpCode}\n`;
- if (mastery !== undefined) {
- const percentage = (mastery * 100).toFixed(1);
- message += `掌握度:${percentage}%\n`;
- if (mastery < 0.7) {
- message += `\n建议:需要加强练习,建议进行针对性训练`;
- } else if (mastery >= 0.9) {
- message += `\n表现优秀!可以挑战更高难度题目`;
- }
- } else {
- message += `\n状态:未学习\n建议:开始学习该知识点`;
- }
- alert(message);
- });
- // 窗口大小变化时重新适应
- window.addEventListener('resize', () => {
- if (!graph || graph.get('destroyed')) return;
- const width = container.scrollWidth;
- const height = container.scrollHeight;
- graph.changeSize(width, height);
- });
- });
- </script>
- @endpush
- </x-filament-panels::page>
|