Laravel DataTable添加导出Excel按钮及现有代码正确性核查
Laravel DataTable导出Excel功能实现及问题排查
现有代码问题核查
JS代码问题
- Excel按钮重复定义
text属性:代码中先声明了静态text配置,后又写了text回调函数,属于冗余配置,虽然运行不会报错但不符合规范 - 缺失依赖方法:直接调用了非DataTable自带的
_buildParams、_downloadFromUrl方法,没有自行定义的话点击按钮会直接抛出JS异常 - 缺少导出触发参数:控制器通过
$request['export']判断是否执行导出,但构造请求参数时没有携带该标识,后端无法触发导出逻辑 - 未携带CSRF令牌:Laravel POST请求必须携带CSRF令牌,否则会返回419状态码报错
控制器代码问题
- 未做参数合法性校验:
$start_date、$end_date、$staff_name没有做格式校验,参数异常时会直接抛出系统错误 - 导出后缀不规范:使用
.xls后缀,若ExcelExport类实际生成的是Xlsx格式文件,会导致导出文件打开时提示格式错误
修复及完整实现方案
前置准备
在Blade模板的<head>标签中加入CSRF元标签,用于后端请求校验:
<meta name="csrf-token" content="{{ csrf_token() }}">
第一步:修复JS代码
// 提前定义缺失的工具函数 function _buildParams(dt, exportType) { // 获取DataTable当前所有筛选、搜索参数 var params = dt.ajax.params(); // 加入导出标识,对应后端判断逻辑 params.export = exportType; // 加入自定义筛选参数,替换为你实际的表单选择器 params.start_date = $('input[name=start_date]').val(); params.end_date = $('input[name=end_date]').val(); params.staff_name = $('input[name=staff_name]').val(); return params; } function _downloadFromUrl(url, params) { // 构造隐藏表单实现文件下载 var form = $('<form></form>') .attr('method', 'POST') .attr('action', url) .attr('target', '_blank'); // 加入CSRF令牌 form.append($('<input>').attr('type', 'hidden').attr('name', '_token').attr('value', $('meta[name="csrf-token"]').attr('content'))); // 遍历参数加入表单 $.each(params, function(key, value) { if (Array.isArray(value)) { $.each(value, function(i, v) { form.append($('<input>').attr('type', 'hidden').attr('name', key + '[]').attr('value', v)); }); } else { form.append($('<input>').attr('type', 'hidden').attr('name', key).attr('value', value)); } }); $('body').append(form); form.submit(); form.remove(); } // DataTable初始化逻辑 $(function () { $('#staffs').DataTable({ dom: "<'row'<'col-sm-5'l><'col-sm-4 text-center'B><'col-sm-3'f>>" + "<'row'<'col-sm-12'tr>>" + "<'row'<'col-sm-5'i><'col-sm-7'p>>", buttons: [ { extend : 'pdfHtml5', text: '<i class="fa fa-file-pdf-o"></i> Export PDF', className: 'btn btn-default', title: "Senarai Pegawai Tidak Aktif", exportOptions: { columns: ':visible:not(.not-exported)' } }, { extend : 'excelHtml5', className: 'btn btn-default', // 保留一个text配置即可,删除重复的静态声明 text: function (dt) { return '<i class="fa fa-file-excel-o"></i> Export Excel'; }, action: function (e, dt, button, config) { var url = dt.ajax.url() || window.location.href; var params = _buildParams(dt, 'excel'); _downloadFromUrl(url, params); } } ], // 剩余原有DataTable配置(ajax、columns等)保持不变 }); });
提示:如果仅需要导出当前页可见数据,不需要导出全部筛选结果,可以直接删掉自定义的
action方法,DataTable自带的excelHtml5可直接完成前端导出,不需要后端配合。
第二步:修复控制器代码
use Maatwebsite\Excel\Facades\Excel; use App\Exports\ExcelExport; public function index(Request $request) { // 参数校验 $validated = $request->validate([ 'start_date' => 'nullable|date', 'end_date' => 'nullable|date|after:start_date', 'staff_name' => 'nullable|string', 'export' => 'nullable|in:excel,pdf' ]); $start_date = $validated['start_date'] ?? now()->subMonth()->toDateString(); $end_date = $validated['end_date'] ?? now()->toDateString(); $staff_name = $validated['staff_name'] ?? ''; // 导出逻辑 if(!empty($validated['export'])) { if($validated['export'] == 'excel') { // 改用xlsx格式,兼容性更好 return Excel::download(new ExcelExport($start_date, $end_date, $staff_name), 'Laporan Pegawai Tidak Aktif.xlsx'); } } // 原有DataTable数据返回逻辑保持不变 }
第三步:确认ExcelExport类定义(示例)
// 路径:app/Exports/ExcelExport.php namespace App\Exports; use App\Models\Staff; use Maatwebsite\Excel\Concerns\FromCollection; use Maatwebsite\Excel\Concerns\WithHeadings; class ExcelExport implements FromCollection, WithHeadings { protected $start_date; protected $end_date; protected $staff_name; public function __construct($start_date, $end_date, $staff_name) { $this->start_date = $start_date; $this->end_date = $end_date; $this->staff_name = $staff_name; } // 导出数据查询逻辑,和前端DataTable查询逻辑保持一致即可 public function collection() { return Staff::where('is_active', 0) ->when($this->staff_name, function($query) { return $query->where('name', 'like', "%{$this->staff_name}%"); }) ->whereBetween('created_at', [$this->start_date, $this->end_date]) ->get() ->map(function($item) { return [ $item->name, $item->email, $item->department, $item->created_at->format('Y-m-d') ]; }); } // 导出表头 public function headings(): array { return [ '姓名', '邮箱', '部门', '入职日期' ]; } }
内容的提问来源于stack exchange,提问作者Hasyimah Suhaimi
相关产品推荐
相关产品推荐

