一般情況大家可能也不需要。有需要時(shí)來復(fù)制。
1,2 部分 直接復(fù)制粘貼, 3,4部分 復(fù)制粘貼后 改著用。
<?php
namespace plugin\admin\app\common;
/**
*
* @author Administrator
*
*/
class CsvExporter
{
protected $config = [
'max_direct_export' => 2000,
'page_size' => 2000, // 分頁界面每頁顯示條數(shù)
'temp_dir' => 'runtime/export',
];
protected $callbacks = [
'getTotal' => null,
'getData' => null,
'processField' => null,
];
protected $fields = [];
public function __construct(array $config = [])
{
$this->config = array_merge($this->config, $config);
if (!is_dir($this->config['temp_dir'])) {
mkdir($this->config['temp_dir'], 0755, true);
}
}
public function setCallback(string $type, callable $callback)
{
if (array_key_exists($type, $this->callbacks)) {
$this->callbacks[$type] = $callback;
}
return $this;
}
public function setFields(array $fields)
{
$this->fields = $fields;
return $this;
}
public function export(string $filename)
{
if (!is_callable($this->callbacks['getTotal'])) {
throw new \RuntimeException('必須設(shè)置getTotal回調(diào)');
}
// 檢查是否是分頁導(dǎo)出請(qǐng)求
$page = (int) request()->get('page', 0);
$limit = (int) request()->get('limit', 0);
if ($page > 0 && $limit > 0) {
return $this->exportPage($filename, $page, $limit);
}
// 普通導(dǎo)出模式
$total = call_user_func($this->callbacks['getTotal']);
if ($total <= $this->config['max_direct_export']) {
return $this->directExport($filename, $total);
} else {
return $this->showPaginatedExport($filename, $total);
}
}
protected function exportPage(string $filename, int $page, int $limit)
{
if (!is_callable($this->callbacks['getData'])) {
throw new \RuntimeException('必須設(shè)置getData回調(diào)');
}
$data = call_user_func($this->callbacks['getData'], $page, $limit);
$processedData = $this->processData($data);
$start = ($page - 1) * $limit + 1;
$end = $start + count($data) - 1;
return $this->generateCsvResponse(
$filename . '_' . $start . '_' . $end,
array_values($this->fields),
$processedData
);
}
protected function directExport(string $filename, int $total)
{
if (!is_callable($this->callbacks['getData'])) {
throw new \RuntimeException('必須設(shè)置getData回調(diào)');
}
$data = call_user_func($this->callbacks['getData'], 1, $total);
$processedData = $this->processData($data);
return $this->generateCsvResponse(
$filename,
array_values($this->fields),
$processedData
);
}
// 在CsvExporter類中修改showPaginatedExport方法
protected function showPaginatedExport(string $filename, int $total)
{
$totalPages = ceil($total / $this->config['page_size']);
return view('common/paginated_export', [
'filename' => $filename,
'headers' => array_values($this->fields),
'totalPages' => $totalPages,
'pageSize' => $this->config['page_size'],
'total' => $total,
'exporter' => $this,
'gets'=>request()->get()
]);
}
protected function processData(array $data): array
{
$result = [];
foreach ($data as $item) {
$row = [];
foreach ($this->fields as $field => $title) {
$value = $item[$field] ?? '';
if (is_callable($this->callbacks['processField'])) {
$value = call_user_func($this->callbacks['processField'], $field, $value, $item);
}
$row[] = $value;
}
$result[] = $row;
}
return $result;
}
protected function generateCsvResponse(string $filename, array $headers, array $data)
{
$filePath = $this->config['temp_dir'] . '/' . uniqid() . '.csv';
$this->writeCsv($filePath, $headers, $data);
$response = response()->file($filePath, [
'Content-Disposition' => 'attachment; filename="' . $filename . '.csv"'
]);
// $response->deleteFileAfterSend(true);
return $response;
}
protected function writeCsv(string $filePath, array $headers, array $data)
{
$file = fopen($filePath, 'w');
if ($file === false) {
throw new \RuntimeException("無法打開文件: {$filePath}");
}
fwrite($file, "\xEF\xBB\xBF"); // BOM頭
fputcsv($file, $headers);
foreach ($data as $row) {
fputcsv($file, $row);
}
fclose($file);
}
}
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<title>數(shù)據(jù)導(dǎo)出</title>
<link href="/app/admin/component/pear/css/pear.css" rel="stylesheet" />
<link href="/app/admin/admin/css/pages/error.css" rel="stylesheet" />
<style>
.export-buttons {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin: 20px 0;
}
.export-btn {
min-width: 120px;
}
.export-summary {
margin-bottom: 20px;
font-size: 16px;
}
.content {
position: absolute;
top: 20%;
padding: 0 50px;
width: 100%;
text-align: left;
}
</style>
</head>
<body>
<div class="content">
<div class="export-dialog">
<h3>數(shù)據(jù)導(dǎo)出</h3>
<div style="margin: 10px 0;">
<button onclick="history.back()" class="pear-btn pear-btn-warming">
← 返回
</button>
</div>
<div class="export-summary">
總記錄數(shù): <?=$total?> 條,共 <?=$totalPages?> 頁
</div>
<div class="export-buttons">
<?php for($i = 1; $i <= $totalPages; $i++): ?>
<?php
$start = ($i - 1) * $pageSize + 1;
$end = min($i * $pageSize, $total);
?>
<button class="pear-btn pear-btn-primary export-btn"
data-i="<?=$i?>">
導(dǎo)出 <?=$start?>-<?=$end?> 條
</button>
<?php endfor; ?>
</div>
</div>
</div>
<script src="/app/admin/component/layui/layui.js?v=2.8.12"></script>
<script src="/app/admin/component/pear/pear.js"></script>
<script>
layui.use(['jquery'], function(){
var $ = layui.$;
// 導(dǎo)出按鈕點(diǎn)擊事件
$('.export-btn').click(function() {
// 獲取當(dāng)前URL對(duì)象
var url = new URL(window.location.href);
// 更新page和limit參數(shù)
<?php foreach($gets as $f=>$v): ?>
url.searchParams.set('<?=$f?>', '<?=$v?>');
<?php endforeach;?>
url.searchParams.set('page', $(this).data('i'));
url.searchParams.set('limit', '<?=$pageSize?>');
// 跳轉(zhuǎn)到新URL
window.location.href = url.toString();
});
});
</script>
</body>
</html>
public function export(Request $request)
{
$exporter = new CsvExporter();
$exporter->setFields([
'order_no' => '訂單編號(hào)',
'user_id' => '用戶ID',
'amount' => '金額',
'status' => '狀態(tài)',
'created_at' => '創(chuàng)建時(shí)間'
]);
$status = $request->get('status','');
$exporter->setCallback('getTotal', function()use($status) {
return \app\model\Order::count();
//return \app\model\Order::where('status',$status)->count();
});
$exporter->setCallback('getData', function($page, $limit)use($status) {
return \app\model\Order::with('user')
->orderBy('id', 'desc')
->offset(($page - 1) * $limit)
->limit($limit)
//->where('status',$status)
->get()
->toArray();
});
$exporter->setCallback('processField', function($field, $value, $item) {
if ($field === 'status') {
$statusMap = [
0 => '待支付',
1 => '已支付',
2 => '已發(fā)貨',
3 => '已完成',
4 => '已取消'
];
return $statusMap[$value] ?? '未知狀態(tài)';
}
if ($field === 'user_id') {
return $item['user']['username'] ?? $value;
}
return $value;
});
return $exporter->export('訂單列表_' . date('Ymd'));
}
<a href="/app/admin/order/export" class="btn btn-primary">導(dǎo)出用戶數(shù)據(jù)</a>
或者下面的 帶參數(shù)的:
<!-- 表格頂部工具欄 -->
<script type="text/html" id="table-toolbar">
<button class="pear-btn pear-btn-md" lay-event="export" permission="app.admin.order.export">
<i class="layui-icon layui-icon-export"></i>導(dǎo)出
</button>
</script>
// 表格頂部工具欄事件
table.on("toolbar(data-table)", function(obj) {
if (obj.event === "refresh") {
refreshTable();
} else if (obj.event === "batchRemove") {
batchRemove(obj);
} else if(obj.event=='export'){
//class="layui-form" lay-filter="searchForm"
var status = form.val("searchForm").status*1;
location.href='/app/admin/order/export?status='+status;
}
});
好帖 感謝分享 以收藏備用