You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 12:45:04