KnowledgeDependencyGraph.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <?php
  2. namespace App\Livewire;
  3. use App\Services\LearningAnalyticsService;
  4. use App\Services\KnowledgeServiceApi;
  5. use Livewire\Component;
  6. class KnowledgeDependencyGraph extends Component
  7. {
  8. public string $studentId = '';
  9. public array $graphData = [];
  10. public bool $isLoading = false;
  11. public string $errorMessage = '';
  12. public ?string $selectedNode = null;
  13. public function mount(string $studentId): void
  14. {
  15. $this->studentId = $studentId;
  16. $this->loadGraphData();
  17. }
  18. public function loadGraphData(): void
  19. {
  20. $this->isLoading = true;
  21. $this->errorMessage = '';
  22. try {
  23. // 获取掌握度数据
  24. $learningService = app(LearningAnalyticsService::class);
  25. $masteryList = $learningService->getStudentMasteryList($this->studentId);
  26. // 获取知识点依赖关系
  27. $dependencies = $this->getKnowledgeDependencies();
  28. if ($masteryList && isset($masteryList['data']) && !empty($dependencies)) {
  29. $this->graphData = $this->processGraphData($masteryList['data'], $dependencies);
  30. } else {
  31. $this->graphData = [];
  32. }
  33. } catch (\Exception $e) {
  34. $this->errorMessage = '加载依赖关系图数据失败:' . $e->getMessage();
  35. $this->graphData = [];
  36. } finally {
  37. $this->isLoading = false;
  38. }
  39. }
  40. /**
  41. * 获取知识依赖关系
  42. */
  43. private function getKnowledgeDependencies(): array
  44. {
  45. try {
  46. // 使用现有的 KnowledgeServiceApi
  47. $knowledgeService = app(KnowledgeServiceApi::class);
  48. $allPoints = $knowledgeService->listKnowledgePoints();
  49. $dependencies = [];
  50. foreach ($allPoints as $point) {
  51. $kpCode = $point['kp_code'] ?? null;
  52. $parents = $point['parents'] ?? [];
  53. if ($kpCode && !empty($parents)) {
  54. foreach ($parents as $parentCode) {
  55. $dependencies[] = [
  56. 'prerequisite_kp_code' => $parentCode,
  57. 'dependent_kp_code' => $kpCode,
  58. 'influence_weight' => 1.0, // 默认权重
  59. 'dependency_type' => 'prerequisite',
  60. ];
  61. }
  62. }
  63. }
  64. return $dependencies;
  65. } catch (\Exception $e) {
  66. \Log::error('获取知识依赖关系失败: ' . $e->getMessage());
  67. return [];
  68. }
  69. }
  70. /**
  71. * 处理图数据
  72. */
  73. private function processGraphData(array $masteryList, array $dependencies): array
  74. {
  75. $nodes = [];
  76. $edges = [];
  77. $masteryMap = [];
  78. // 创建掌握度映射
  79. foreach ($masteryList as $mastery) {
  80. $masteryMap[$mastery['kp_code']] = $mastery['mastery_level'];
  81. }
  82. // 构建节点
  83. foreach ($dependencies as $dep) {
  84. $sourceCode = $dep['prerequisite_kp_code'];
  85. $targetCode = $dep['dependent_kp_code'];
  86. if (!isset($masteryMap[$sourceCode])) {
  87. $masteryMap[$sourceCode] = 0.0;
  88. }
  89. if (!isset($masteryMap[$targetCode])) {
  90. $masteryMap[$targetCode] = 0.0;
  91. }
  92. // 添加源节点
  93. if (!isset($nodes[$sourceCode])) {
  94. $nodes[$sourceCode] = [
  95. 'id' => $sourceCode,
  96. 'label' => $this->getKnowledgePointName($sourceCode),
  97. 'mastery' => $masteryMap[$sourceCode],
  98. 'color' => $this->getMasteryColor($masteryMap[$sourceCode]),
  99. 'size' => $this->getNodeSize($masteryMap[$sourceCode]),
  100. ];
  101. }
  102. // 添加目标节点
  103. if (!isset($nodes[$targetCode])) {
  104. $nodes[$targetCode] = [
  105. 'id' => $targetCode,
  106. 'label' => $this->getKnowledgePointName($targetCode),
  107. 'mastery' => $masteryMap[$targetCode],
  108. 'color' => $this->getMasteryColor($masteryMap[$targetCode]),
  109. 'size' => $this->getNodeSize($masteryMap[$targetCode]),
  110. ];
  111. }
  112. // 添加边
  113. $edges[] = [
  114. 'from' => $sourceCode,
  115. 'to' => $targetCode,
  116. 'width' => $dep['influence_weight'] * 3,
  117. 'color' => $this->getEdgeColor($masteryMap[$sourceCode], $masteryMap[$targetCode]),
  118. 'label' => '权重: ' . number_format($dep['influence_weight'], 2),
  119. ];
  120. }
  121. return [
  122. 'nodes' => array_values($nodes),
  123. 'edges' => array_values($edges),
  124. ];
  125. }
  126. /**
  127. * 获取知识点名称
  128. */
  129. private function getKnowledgePointName(string $kpCode): string
  130. {
  131. // 简单的名称映射,实际应从数据库获取
  132. $names = [
  133. 'KP1001' => '因式分解基础',
  134. 'KP1002' => '公因式提取',
  135. 'KP1003' => '分组分解法',
  136. 'KP1004' => '十字相乘法',
  137. 'KP1005' => '公式法',
  138. 'KP2001' => '完全平方公式',
  139. 'KP2002' => '平方差公式',
  140. 'default' => $kpCode,
  141. ];
  142. return $names[$kpCode] ?? $names['default'];
  143. }
  144. /**
  145. * 根据掌握度获取颜色
  146. */
  147. private function getMasteryColor(float $masteryLevel): string
  148. {
  149. if ($masteryLevel < 0.3) {
  150. return '#ef4444'; // 红色 - 薄弱
  151. } elseif ($masteryLevel < 0.5) {
  152. return '#f97316'; // 橙色 - 需要改进
  153. } elseif ($masteryLevel < 0.7) {
  154. return '#eab308'; // 黄色 - 一般
  155. } elseif ($masteryLevel < 0.85) {
  156. return '#22c55e'; // 绿色 - 良好
  157. } else {
  158. return '#3b82f6'; // 蓝色 - 掌握
  159. }
  160. }
  161. /**
  162. * 获取节点大小
  163. */
  164. private function getNodeSize(float $masteryLevel): int
  165. {
  166. return (int) (20 + $masteryLevel * 30); // 20-50像素
  167. }
  168. /**
  169. * 获取边的颜色
  170. */
  171. private function getEdgeColor(float $sourceMastery, float $targetMastery): string
  172. {
  173. // 根据源节点掌握度设置边的颜色
  174. if ($sourceMastery >= 0.7) {
  175. return '#22c55e'; // 绿色 - 源节点掌握良好
  176. } elseif ($sourceMastery >= 0.5) {
  177. return '#eab308'; // 黄色 - 源节点一般
  178. } else {
  179. return '#ef4444'; // 红色 - 源节点薄弱
  180. }
  181. }
  182. /**
  183. * 选择节点
  184. */
  185. public function selectNode(?string $nodeId): void
  186. {
  187. $this->selectedNode = $nodeId;
  188. }
  189. public function render()
  190. {
  191. return view('livewire.knowledge-dependency-graph');
  192. }
  193. }