| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- <div>
- <div class="space-y-6">
- <!-- 标题和控制 -->
- <div class="bg-white shadow rounded-lg p-6">
- <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
- <div>
- <h2 class="text-2xl font-bold text-gray-900">学生知识图谱</h2>
- <p class="text-sm text-gray-600 mt-1">可视化展示学生的知识点掌握情况和依赖关系</p>
- </div>
- <div class="flex items-center gap-3">
- <select
- wire:model.live="selectedStudentId"
- class="rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
- >
- <option value="">-- 选择学生 --</option>
- @foreach ($students as $student)
- <option value="{{ $student['id'] }}">{{ $student['label'] }}</option>
- @endforeach
- </select>
- @if ($selectedStudent)
- <button
- wire:click="loadStudentData('{{ $selectedStudentId }}')"
- class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
- >
- <svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
- </svg>
- 刷新
- </button>
- <button
- onclick="exportGraph()"
- class="inline-flex items-center px-3 py-2 border border-gray-300 text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
- >
- <svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
- </svg>
- 导出PNG
- </button>
- @endif
- </div>
- </div>
- @error('selectedStudentId')
- <p class="mt-2 text-sm text-red-600">{{ $message }}</p>
- @enderror
- </div>
- @if ($selectedStudent)
- <!-- 学生信息 -->
- <div class="bg-white shadow rounded-lg p-6">
- <div class="flex items-center gap-4">
- <div class="flex-shrink-0">
- <div class="w-16 h-16 rounded-full bg-indigo-100 flex items-center justify-center">
- <span class="text-2xl font-bold text-indigo-600">{{ substr($selectedStudent->name, 0, 1) }}</span>
- </div>
- </div>
- <div>
- <h3 class="text-lg font-semibold text-gray-900">{{ $selectedStudent->name }}</h3>
- <p class="text-sm text-gray-600">{{ $selectedStudent->grade }} {{ $selectedStudent->class_name }}</p>
- </div>
- </div>
- </div>
- @if ($isLoading)
- <!-- 加载状态 -->
- <div class="bg-white shadow rounded-lg p-12">
- <div class="flex flex-col items-center justify-center">
- <svg class="animate-spin h-12 w-12 text-indigo-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
- <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
- </svg>
- <p class="mt-4 text-sm text-gray-600">正在加载知识图谱数据...</p>
- </div>
- </div>
- @else
- <!-- 统计信息 -->
- @if (!empty($statistics))
- <div class="grid grid-cols-1 md:grid-cols-4 gap-6">
- <div class="bg-white shadow rounded-lg p-6">
- <div class="flex items-center">
- <div class="flex-shrink-0">
- <div class="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
- <svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
- </svg>
- </div>
- </div>
- <div class="ml-4">
- <p class="text-sm font-medium text-gray-500">平均掌握度</p>
- <p class="text-2xl font-semibold text-gray-900">{{ number_format($statistics['average_mastery'] * 100, 1) }}%</p>
- </div>
- </div>
- </div>
- <div class="bg-white shadow rounded-lg p-6">
- <div class="flex items-center">
- <div class="flex-shrink-0">
- <div class="w-8 h-8 bg-green-100 rounded-full flex items-center justify-center">
- <svg class="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
- </svg>
- </div>
- </div>
- <div class="ml-4">
- <p class="text-sm font-medium text-gray-500">优秀 (≥80%)</p>
- <p class="text-2xl font-semibold text-gray-900">{{ $statistics['high_mastery_count'] }}</p>
- </div>
- </div>
- </div>
- <div class="bg-white shadow rounded-lg p-6">
- <div class="flex items-center">
- <div class="flex-shrink-0">
- <div class="w-8 h-8 bg-yellow-100 rounded-full flex items-center justify-center">
- <svg class="w-5 h-5 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
- </svg>
- </div>
- </div>
- <div class="ml-4">
- <p class="text-sm font-medium text-gray-500">中等 (40-80%)</p>
- <p class="text-2xl font-semibold text-gray-900">{{ $statistics['medium_mastery_count'] }}</p>
- </div>
- </div>
- </div>
- <div class="bg-white shadow rounded-lg p-6">
- <div class="flex items-center">
- <div class="flex-shrink-0">
- <div class="w-8 h-8 bg-red-100 rounded-full flex items-center justify-center">
- <svg class="w-5 h-5 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
- </svg>
- </div>
- </div>
- <div class="ml-4">
- <p class="text-sm font-medium text-gray-500">待提高 (<40%)</p>
- <p class="text-2xl font-semibold text-gray-900">{{ $statistics['low_mastery_count'] }}</p>
- </div>
- </div>
- </div>
- </div>
- @endif
- <!-- 知识图谱 -->
- <div class="bg-white shadow rounded-lg p-6">
- <div class="flex items-center justify-between mb-4">
- <h3 class="text-lg font-semibold text-gray-900">知识点依赖关系图</h3>
- <div class="flex items-center gap-4 text-xs text-gray-500">
- <div class="flex items-center gap-1">
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5M7.188 2.239l.777 2.897M5.136 7.965l-2.898-.777M13.95 4.05l-2.122 2.122m-5.657 5.656l-2.12 2.122"></path>
- </svg>
- <span>拖拽移动</span>
- </div>
- <div class="flex items-center gap-1">
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
- </svg>
- <span>滚轮缩放</span>
- </div>
- <div class="flex items-center gap-1">
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
- </svg>
- <span>悬浮查看</span>
- </div>
- </div>
- </div>
- <div class="relative">
- <div id="knowledge-graph" class="w-full h-96 border border-gray-200 rounded-lg"></div>
- <!-- 图例 -->
- <div class="absolute top-4 right-4 bg-white p-4 rounded-lg shadow-lg border border-gray-200">
- <p class="text-xs font-semibold text-gray-700 mb-3">掌握度</p>
- <div class="space-y-2">
- <div class="flex items-center gap-2">
- <div class="w-4 h-4 rounded-full bg-green-500 border-2 border-white shadow-sm"></div>
- <span class="text-xs text-gray-700 font-medium">优秀 (≥80%)</span>
- </div>
- <div class="flex items-center gap-2">
- <div class="w-4 h-4 rounded-full bg-blue-500 border-2 border-white shadow-sm"></div>
- <span class="text-xs text-gray-700 font-medium">良好 (60-80%)</span>
- </div>
- <div class="flex items-center gap-2">
- <div class="w-4 h-4 rounded-full bg-yellow-500 border-2 border-white shadow-sm"></div>
- <span class="text-xs text-gray-700 font-medium">中等 (40-60%)</span>
- </div>
- <div class="flex items-center gap-2">
- <div class="w-4 h-4 rounded-full bg-orange-500 border-2 border-white shadow-sm"></div>
- <span class="text-xs text-gray-700 font-medium">待提高 (20-40%)</span>
- </div>
- <div class="flex items-center gap-2">
- <div class="w-4 h-4 rounded-full bg-red-500 border-2 border-white shadow-sm"></div>
- <span class="text-xs text-gray-700 font-medium">薄弱 (<20%)</span>
- </div>
- </div>
- <div class="mt-3 pt-3 border-t border-gray-200">
- <p class="text-xs text-gray-500">节点大小表示掌握程度</p>
- </div>
- </div>
- </div>
- </div>
- <!-- 掌握度分布图 -->
- @if (!empty($masteryData['masteries']))
- <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
- <div class="bg-white shadow rounded-lg p-6">
- <h3 class="text-lg font-semibold text-gray-900 mb-4">掌握度分布</h3>
- <canvas id="mastery-distribution" class="w-full h-64"></canvas>
- </div>
- <div class="bg-white shadow rounded-lg p-6">
- <h3 class="text-lg font-semibold text-gray-900 mb-4">知识点列表</h3>
- <div class="space-y-3 max-h-64 overflow-y-auto">
- @foreach ($masteryData['masteries'] as $mastery)
- <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
- <div>
- <p class="text-sm font-medium text-gray-900">{{ $mastery['kp_code'] }}</p>
- <p class="text-xs text-gray-500">置信度: {{ number_format($mastery['confidence_level'] * 100, 1) }}%</p>
- </div>
- <div class="text-right">
- <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium"
- style="background-color: {{ $this->getMasteryColor($mastery['mastery_level']) }}20; color: {{ $this->getMasteryColor($mastery['mastery_level']) }}">
- {{ number_format($mastery['mastery_level'] * 100, 1) }}%
- </span>
- </div>
- </div>
- @endforeach
- </div>
- </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
- <!-- 选择提示 -->
- <div class="bg-white shadow rounded-lg p-12">
- <div class="text-center">
- <svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
- </svg>
- <h3 class="mt-4 text-lg font-medium text-gray-900">选择学生查看知识图谱</h3>
- <p class="mt-2 text-sm text-gray-500">从上方下拉列表中选择一个学生,系统将自动加载其知识图谱数据</p>
- </div>
- </div>
- @endif
- </div>
- <!-- 知识图谱脚本 -->
- @push('scripts')
- <script src="https://d3js.org/d3.v7.min.js"></script>
- <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
- <script>
- window.kgDetailLayout = '{{ $detailLayout ?? 'inline' }}';
- document.addEventListener('livewire:initialized', () => {
- const knowledgeGraph = @this.knowledgePoints;
- if (knowledgeGraph && knowledgeGraph.nodes && knowledgeGraph.nodes.length > 0) {
- renderKnowledgeGraph(knowledgeGraph);
- renderMasteryChart(@this.masteryData.masteries);
- }
- // 监听数据更新
- Livewire.on('knowledgeGraphUpdated', (data) => {
- renderKnowledgeGraph(data);
- });
- });
- function renderKnowledgeGraph(data) {
- const container = document.getElementById('knowledge-graph');
- if (!container) return;
- // 清空容器
- container.innerHTML = '';
- const width = container.clientWidth;
- const height = container.clientHeight;
- const svg = d3.select('#knowledge-graph')
- .append('svg')
- .attr('width', width)
- .attr('height', height);
- // 创建力导向图
- const simulation = d3.forceSimulation(data.nodes)
- .force('link', d3.forceLink(data.links).id(d => d.id).distance(100))
- .force('charge', d3.forceManyBody().strength(-300))
- .force('center', d3.forceCenter(width / 2, height / 2));
- // 绘制边
- const link = svg.append('g')
- .selectAll('line')
- .data(data.links)
- .enter().append('line')
- .attr('stroke', '#999')
- .attr('stroke-opacity', 0.6)
- .attr('stroke-width', d => Math.sqrt(d.strength * 5));
- // 创建tooltip
- const tooltip = d3.select('body').append('div')
- .attr('class', 'knowledge-graph-tooltip')
- .style('position', 'absolute')
- .style('visibility', 'hidden')
- .style('background-color', 'rgba(0, 0, 0, 0.8)')
- .style('color', '#fff')
- .style('padding', '8px 12px')
- .style('border-radius', '6px')
- .style('font-size', '12px')
- .style('pointer-events', 'none')
- .style('z-index', '9999');
- // 绘制节点
- const node = svg.append('g')
- .selectAll('circle')
- .data(data.nodes)
- .enter().append('circle')
- .attr('r', d => d.size)
- .attr('fill', d => d.color)
- .attr('stroke', '#fff')
- .attr('stroke-width', 2)
- .style('cursor', 'pointer')
- .on('mouseover', function(event, d) {
- tooltip.style('visibility', 'visible')
- .html(`<strong>${d.label}</strong><br/>
- 掌握度: ${(d.mastery * 100).toFixed(1)}%<br/>
- 节点ID: ${d.id}`);
- })
- .on('mousemove', function(event) {
- tooltip.style('top', (event.pageY - 10) + 'px')
- .style('left', (event.pageX + 10) + 'px');
- })
- .on('mouseout', function() {
- tooltip.style('visibility', 'hidden');
- })
- .on('click', function(event, d) {
- showNodeDetails(d);
- })
- .call(d3.drag()
- .on('start', dragstarted)
- .on('drag', dragged)
- .on('end', dragended));
- // 添加标签
- const label = svg.append('g')
- .selectAll('text')
- .data(data.nodes)
- .enter().append('text')
- .text(d => d.label)
- .attr('font-size', '12px')
- .attr('fill', '#333')
- .attr('text-anchor', 'middle')
- .attr('dy', '.35em');
- // 更新位置
- simulation.on('tick', () => {
- link
- .attr('x1', d => d.source.x)
- .attr('y1', d => d.source.y)
- .attr('x2', d => d.target.x)
- .attr('y2', d => d.target.y);
- node
- .attr('cx', d => d.x)
- .attr('cy', d => d.y);
- label
- .attr('x', d => d.x)
- .attr('y', d => d.y + d.size + 15);
- });
- function dragstarted(event, d) {
- if (!event.active) simulation.alphaTarget(0.3).restart();
- d.fx = d.x;
- d.fy = d.y;
- }
- function dragged(event, d) {
- d.fx = event.x;
- d.fy = event.y;
- }
- function dragended(event, d) {
- if (!event.active) simulation.alphaTarget(0);
- d.fx = null;
- d.fy = null;
- }
- // 添加缩放功能
- const zoom = d3.zoom()
- .scaleExtent([0.5, 3])
- .on('zoom', (event) => {
- svg.selectAll('g').attr('transform', event.transform);
- });
- svg.call(zoom);
- // 节点详情展示函数
- function showNodeDetails(nodeData) {
- 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功能
- window.exportGraph = function() {
- const container = document.getElementById('knowledge-graph');
- if (!container) return;
- const svgElement = container.querySelector('svg');
- if (!svgElement) return;
- // 创建canvas
- const canvas = document.createElement('canvas');
- const ctx = canvas.getContext('2d');
- const data = (new XMLSerializer()).serializeToString(svgElement);
- const DOMURL = window.URL || window.webkitURL || window;
- const img = new Image();
- const svgBlob = new Blob([data], {type: 'image/svg+xml;charset=utf-8'});
- const url = DOMURL.createObjectURL(svgBlob);
- img.onload = function () {
- canvas.width = svgElement.clientWidth;
- canvas.height = svgElement.clientHeight;
- ctx.fillStyle = '#ffffff';
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- ctx.drawImage(img, 0, 0);
- DOMURL.revokeObjectURL(url);
- // 下载图片
- const link = document.createElement('a');
- link.download = `知识图谱_${new Date().getTime()}.png`;
- link.href = canvas.toDataURL('image/png');
- link.click();
- };
- img.src = url;
- };
- }
- function renderMasteryChart(masteries) {
- const ctx = document.getElementById('mastery-distribution');
- if (!ctx) return;
- // 分类数据
- const ranges = {
- '优秀 (≥80%)': 0,
- '良好 (60-80%)': 0,
- '中等 (40-60%)': 0,
- '待提高 (20-40%)': 0,
- '薄弱 (<20%)': 0
- };
- masteries.forEach(m => {
- const mastery = m.mastery_level * 100;
- if (mastery >= 80) ranges['优秀 (≥80%)']++;
- else if (mastery >= 60) ranges['良好 (60-80%)']++;
- else if (mastery >= 40) ranges['中等 (40-60%)']++;
- else if (mastery >= 20) ranges['待提高 (20-40%)']++;
- else ranges['薄弱 (<20%)']++;
- });
- new Chart(ctx, {
- type: 'bar',
- data: {
- labels: Object.keys(ranges),
- datasets: [{
- label: '知识点数量',
- data: Object.values(ranges),
- backgroundColor: [
- '#10b981',
- '#3b82f6',
- '#f59e0b',
- '#f97316',
- '#ef4444'
- ]
- }]
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: {
- display: false
- }
- },
- scales: {
- y: {
- beginAtZero: true,
- ticks: {
- stepSize: 1
- }
- }
- }
- }
- });
- }
- </script>
- @endpush
- </div>
|