框架提供了便捷的数据导出功能,支持 Excel 和 CSV 格式导出。
Excel 导出需要安装:composer require rap2hpoutre/fast-excel
// 在列表工具栏添加导出按钮
public function list(): Page
{
$crud = $this->baseCRUD()
->headerToolbar([
$this->createButton(),
...$this->baseHeaderToolBar(),
// 添加导出按钮
$this->exportAction(),
])
->columns([
// ...
]);
return $this->baseList($crud);
}
// 在控制器中重写 exportFileName 方法
protected function exportFileName()
{
return '此处为导出文件名';
}
// 在控制器中重写 exportMap 方法, $row 是数组格式
// 该方法会被循环调用, 请不要在里面执行 IO 操作
protected function exportMap($row)
{
return [
'ID' => $row['id'],
'用户名' => $row['username'],
'真实姓名' => $row['real_name'],
'邮箱' => $row['email'],
'部门' => $row['department']['name'] ?? '',
'状态' => $row['status'] ? '启用' : '禁用',
'创建时间' => date('Y-m-d H:i:s', strtotime($row['created_at'])),
];
}
如果以上配置不能满足你的需求, 你可以重写 export
方法, 自定义导出逻辑
// 在控制器中重写 export 方法
// 此方法在 index() 中被调用, 当请求参数 _action=export 时
protected function export()
{
// 默认在 storage/app/ 下
$path = sprintf('%s-%s.xlsx', $this->exportFileName(), date('YmdHis'));
// 导出本页和导出选中项都是通过 _ids 查询
$ids = request()->input('_ids');
// listQuery() 为列表查询条件,与获取列表数据一致
$query = $this->service->listQuery()
->when($ids, fn($query) => $query->whereIn($this->service->primaryKey(), explode(',', $ids)));
try {
fastexcel($query->get())->export(storage_path('app/' . $path), fn($row) => $this->exportMap($row));
} catch (\Throwable $e) {
admin_abort(__('admin.action_failed'));
}
return $this->response()->success(compact('path'));
}
支持导出为不同格式:
protected function export()
{
$format = request('format', 'xlsx'); // 支持 xlsx, csv
$filename = $this->exportFileName() . '-' . date('YmdHis');
$ids = request()->input('_ids');
$query = $this->service->listQuery()
->when($ids, fn($query) => $query->whereIn($this->service->primaryKey(), explode(',', $ids)));
switch ($format) {
case 'csv':
return $this->exportCsv($query, $filename);
default:
return $this->exportExcel($query, $filename);
}
}
/**
* 导出 CSV
*/
protected function exportCsv($query, $filename)
{
$path = "exports/{$filename}.csv";
$fullPath = storage_path("app/{$path}");
// 确保目录存在
$directory = dirname($fullPath);
if (!is_dir($directory)) {
mkdir($directory, 0755, true);
}
$file = fopen($fullPath, 'w');
// 添加 BOM 头,解决中文乱码
fwrite($file, "\xEF\xBB\xBF");
$isFirstRow = true;
$query->chunk(1000, function($rows) use ($file, &$isFirstRow) {
foreach ($rows as $row) {
$data = $this->exportMap($row->toArray());
if ($isFirstRow) {
fputcsv($file, array_keys($data)); // 写入表头
$isFirstRow = false;
}
fputcsv($file, array_values($data)); // 写入数据
}
});
fclose($file);
return $this->response()->success(['path' => $path]);
}
处理大量数据时的优化方案:
protected function export()
{
$query = $this->service->listQuery();
$count = $query->count();
// 大数据量分块处理
if ($count > 5000) {
return $this->exportLargeData($query);
}
// 小数据量直接导出
return $this->exportNormal($query);
}
/**
* 大数据量导出
*/
protected function exportLargeData($query)
{
$filename = $this->exportFileName() . '-' . date('YmdHis') . '.xlsx';
$path = "exports/{$filename}";
$fullPath = storage_path("app/{$path}");
$directory = dirname($fullPath);
if (!is_dir($directory)) {
mkdir($directory, 0755, true);
}
$isFirstChunk = true;
$chunkSize = 1000;
$query->chunk($chunkSize, function($rows) use (&$isFirstChunk, $fullPath) {
$data = $rows->map(function($row) {
return $this->exportMap($row->toArray());
})->toArray();
if ($isFirstChunk) {
fastexcel($data)->export($fullPath);
$isFirstChunk = false;
} else {
fastexcel($data)->appendTo($fullPath);
}
});
return $this->response()->success(['path' => $path]);
}
exportMap
中执行数据库查询