Laravel框架下如何为图表配置日期范围选择器并加载数据
解决方案:关联日期选择器、API与Chart.js图表
1. 修正后端控制器逻辑
当前控制器接收参数与前端传递格式不匹配,同时需要处理默认显示当日数据的需求,调整代码如下:
public function LoadPendapatan(Request $request) { // 获取前端传递的日期范围,默认当日 $dateRange = $request->get('from-to', now()->toDateString() . ' - ' . now()->toDateString()); list($startDate, $endDate) = explode(' - ', $dateRange); // 查询KasMasuk的总和 $pendapatan = KasMasuk::whereDate('created_at', '>=', trim($startDate)) ->whereDate('created_at', '<=', trim($endDate)) ->sum('debet'); // 查询KasKeluar的总和 $penjualan = KasKeluar::whereDate('created_at', '>=', trim($startDate)) ->whereDate('created_at', '<=', trim($endDate)) ->sum('sub_total'); $income = $pendapatan - $penjualan; // 返回结构化JSON,方便前端解析 return response()->json([ 'pendapatan' => $pendapatan, 'penjualan' => $penjualan, 'income' => $income ]); }
2. 重构前端脚本:封装数据获取与图表更新逻辑
把数据请求和图表更新封装成独立函数,在页面初始化和日期选择器变化时调用:
$(function () { // 初始化日期选择器的默认日期(当日) let start = moment(); let end = moment(); // 从URL参数获取已选中的日期范围(如果有) let searchParams = new URLSearchParams(window.location.search); let dateInterval = searchParams.get('from-to'); if (dateInterval) { [start, end] = dateInterval.split(' - '); start = moment(start); end = moment(end); } // 初始化Chart.js实例(保留原有配置,优化数据结构) 'use strict' var ticksStyle = { fontColor: '#495057', fontStyle: 'bold' } var mode = 'index' var intersect = true var $salesChart = $('#sales-chart') var salesChart = new Chart($salesChart, { type: 'bar', data: { labels: ['Pendapatan', 'Penjualan', 'Income'], // 匹配实际数据的标签 datasets: [{ backgroundColor: ['#007bff', '#dc3545', '#28a745'], data: [0, 0, 0] // 初始空数据 }] }, options: { maintainAspectRatio: false, tooltips: { mode: mode, intersect: intersect }, hover: { mode: mode, intersect: intersect }, legend: { display: false }, scales: { yAxes: [{ gridLines: { display: true, lineWidth: '4px', color: 'rgba(0, 0, 0, .2)', zeroLineColor: 'transparent' }, ticks: $.extend({ beginAtZero: true, callback: function (value) { if (value >= 1000000) { value /= 1000000 value += 'jt' } else if (value >= 1000) { value /= 1000 value += 'k' } return 'Rp. ' + value } }, ticksStyle) }], xAxes: [{ display: true, gridLines: { display: false }, ticks: ticksStyle }] } } }); // 封装:获取数据并更新图表 function updateChart(startDate, endDate) { // 拼接日期范围参数 let dateRange = `${startDate.format('YYYY-MM-DD')} - ${endDate.format('YYYY-MM-DD')}`; // 调用API获取数据 $.get('/load-pendapatan', { 'from-to': dateRange }, function(response) { // 更新图表数据并触发重绘 salesChart.data.datasets[0].data = [response.pendapatan, response.penjualan, response.income]; salesChart.update(); }); } // 初始化日期选择器 $('#reservation').daterangepicker({ "showWeekNumbers": true, "alwaysShowCalendars": true, startDate: start, endDate: end, locale: { format: 'YYYY-MM-DD', firstDay: 1, }, ranges: { 'Today': [moment(), moment()], 'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')], 'Last 7 Days': [moment().subtract(6, 'days'), moment()], 'Last 30 Days': [moment().subtract(29, 'days'), moment()], 'This Month': [moment().startOf('month'), moment().endOf('month')], 'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')], 'This Year': [moment().startOf('year'), moment().endOf('year')], 'Last Year': [moment().subtract(1, 'year').startOf('year'), moment().subtract(1, 'year').endOf('year')], 'All time': [moment().subtract(30, 'year').startOf('month'), moment().endOf('month')], } }, function(start, end) { // 日期选择完成后触发图表更新 updateChart(start, end); // 可选:更新URL参数,刷新页面时保留选中的日期 searchParams.set('from-to', `${start.format('YYYY-MM-DD')} - ${end.format('YYYY-MM-DD')}`); window.history.replaceState({}, '', `${window.location.pathname}?${searchParams}`); }); // 页面加载完成后,初始化图表数据(默认当日) updateChart(start, end); });
3. 关键修改说明
- 后端:
- 改为接收
from-to参数(与前端表单name一致) - 默认值设为当日日期范围,满足页面加载时显示当日数据的需求
- 返回结构化JSON,前端更易解析
- 改为接收
- 前端:
- 封装
updateChart函数,统一处理数据请求和图表更新 - 日期选择器的回调中调用
updateChart,实现选择日期后自动刷新图表 - 修改图表标签为实际数据对应的名称,优化展示
- 添加URL参数更新,刷新页面时保留用户选中的日期范围
- 封装
内容的提问来源于stack exchange,提问作者Firdan Mahendra Wardana
相关产品推荐
相关产品推荐

