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

Laravel+DOMPDF生成PDF报表的分页与显示问题求助

Laravel + DOMPDF 生成活动日志PDF报表的问题修复

问题1:分页后每页缺失一条数据

原因

手动拆分表格的分页逻辑存在计算误差,且强制分页与DOMPDF自动分页机制冲突,导致数据行遗漏。

解决方案

移除手动分页的PHP逻辑,依赖DOMPDF的自动分页功能,让系统根据页面容量自动处理分页。


问题2:表格表头仅在第一页显示

原因

手动拆分表格后,每个页面是独立表格,DOMPDF无法识别需重复的表头;同时缺少支持表头重复的DOMPDF配置项。

解决方案

  1. 使用单一完整表格,保留<thead>标签
  2. 确保CSS中thead { display: table-header-group; }生效
  3. 在控制器PDF配置中添加setOption('keep_header_footer', true)

问题3:页脚页码无法正常渲染

原因

  • PHP页码脚本位置错误,与固定footer的占位符位置冲突
  • 未开启DOMPDF的PHP脚本支持选项
  • 手动分页导致页码变量无法正确计算

解决方案

  1. 移除HTML中的页码占位符,通过PHP脚本直接输出到页脚对应位置
  2. 在控制器PDF配置中添加setOption('enable_php', true)
  3. 调整脚本位置到<body>末尾,确保正确获取页面变量

修改后的完整代码

控制器代码(仅调整PDF选项)

public function exportData(Request $request)
{
    // Get current authenticated user
    $authUser = User::where('id', Auth::user()->id)->with('profile')->first();
    $userName = $authUser->profile->first_name . ' ' . $authUser->profile->middle_name . ' ' . $authUser->profile->last_name;

    // Build query
    $query = ActivityLog::with([
        'causer.profile:id,user_id,title,first_name,middle_name,last_name',
        'subject'
    ]);

    // Apply search if exists
    if ($request->filled('search')) {
        $query->where(function($q) use ($request) {
            $q->where('description', 'like', "%{$request->search}%")
            ->orWhereHas('causer.profile', function($q) use ($request) {
                $q->where('first_name', 'like', "%{$request->search}%")
                    ->orWhere('last_name', 'like', "%{$request->search}%");
            });
        });
    }

    // Get data
    $logs = $query->get();

    // Generate PDF
    $pdf = Pdf::loadView('exports.activity-logs', [
        'logs' => $logs,
        'searchTerm' => $request->search,
        'generatedBy' => $userName,
        'generatedAt' => now()->format('Y-m-d H:i:s'),
        'watermarkPath' => public_path('dist/back/img/logo-3-300x161.png') // Path your watermark image
    ]);

    // Set PDF options
    $pdf->setPaper('A4', 'landscape')
    ->setOption('margin-top', 70) // 适配固定header高度
    ->setOption('margin-right', 15)
    ->setOption('margin-bottom', 40) // 适配固定footer高度
    ->setOption('margin-left', 15)
    ->setOption('enable_html5_parser', true)
    ->setOption('enable_remote', true)
    ->setOption('keep_header_footer', true) // 开启表头重复
    ->setOption('enable_php', true); // 开启PHP脚本支持

    return $pdf->download('activity_logs_export_'.now()->format('Ymd_His').'.pdf');
}

HTML模板代码(移除手动分页,修复表头和页码)

<!DOCTYPE html>
<html>

<head>
    <title>Activity Logs Report</title>
    <style nonce="{{ csp_nonce() }}">
        /* Watermark styling */
        .watermark {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%) rotate(-45deg);
            opacity: 0.1;
            z-index: -1;
            width: 60%;
        }

        /* Fixed header */
        .header {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            text-align: center;
            padding: 10px 0;
            background-color: white;
            border-bottom: 1px solid #dee2e6;
            height: 60px;
        }

        .header-image {
            height: 40px;
            margin-bottom: 5px;
        }

        .header-text {
            margin: 5px 0;
            font-size: 14pt;
        }

        .search-term {
            font-size: 9pt;
        }

        /* Fixed footer */
        .footer {
            position: fixed;
            bottom: 0;
            left: 0;
            right: 0;
            font-size: 8pt;
            padding: 10px 0;
            background-color: white;
            border-top: 1px solid #dee2e6;
            height: 30px;
        }

        .float-right {
            text-align: right;
        }

        .float-left {
            text-align: left;
        }

        .text-muted {
            color: #949494;
        }

        /* Main content with safe margins */
        .content {
            margin-top: 70px;
            margin-bottom: 40px;
            padding: 0 15px;
        }

        /* Table styling */
        .table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 0;
        }

        .table-bordered th,
        .table-bordered td {
            border: 1px solid #dee2e6;
            padding: 0.3rem;
            font-size: 9pt;
        }

        .table-striped tbody tr:nth-of-type(odd) {
            background-color: rgba(0, 0, 0, 0.05);
        }

        /* Table row handling */
        tr {
            page-break-inside: avoid;
        }

        /* Repeat headers on each page */
        thead {
            display: table-header-group;
        }

        tbody {
            display: table-row-group;
        }

        body {
            font-family: 'Helvetica', 'Arial', sans-serif;
            font-size: 10pt;
            margin: 0;
            padding: 0;
        }

        .footer-content {
            display: flex;
            justify-content: space-between;
            padding: 0 15px;
        }
    </style>
</head>

<body>
    <!-- Watermark -->
    <img class="watermark" src="{{ $watermarkPath }}" alt="Watermark">

    <!-- Header -->
    <div class="header">
        <img src="{{ $watermarkPath }}" class="header-image">
        <h2 class="header-text">ACTIVITY LOGS REPORT</h2>
        @if (!empty($searchTerm))
            <div class="search-term">Filtered by: "{{ $searchTerm }}"</div>
        @endif
    </div>

    <!-- Footer -->
    <div class="footer">
        <div class="footer-content">
            <div class="float-left text-muted">
                Generated by: {{ $generatedBy }}<br>
                Generated at: {{ $generatedAt }}
            </div>
            <div class="float-right text-muted"></div>
        </div>
    </div>

    <!-- Main Content -->
    <div class="content">
        <table class="table table-bordered table-striped">
            <thead>
                <tr>
                    <th>#</th>
                    <th>Description</th>
                    <th>Performed By</th>
                    <th>Affected Resource</th>
                    <th>Subject</th>
                    <th>User Agent</th>
                    <th>Date/Time</th>
                </tr>
            </thead>
            <tbody>
                @foreach ($logs as $index => $log)
                    <tr>
                        <td>{{ $index + 1 }}</td>
                        <td>{{ $log->description }}</td>
                        <td>
                            @if ($log->causer && $log->causer->profile)
                                {{ optional($log->causer->profile)->title }}
                                {{ optional($log->causer->profile)->first_name }}
                                {{ optional($log->causer->profile)->last_name }}
                            @else
                                System
                            @endif
                        </td>
                        <td>{{ $log->event }}</td>
                        <td>
                            @if ($log->subject)
                                {{ class_basename($log->subject_type) }} #{{ $log->subject_id }}
                            @else
                                N/A
                            @endif
                        </td>
                        <td>{{ $log->user_agent }}</td>
                        <td>{{ $log->created_at->format('Y-m-d H:i:s') }}</td>
                    </tr>
                @endforeach
            </tbody>
        </table>
    </div>

    <!-- Page numbers -->
    <script type="text/php">
        if (isset($pdf)) {
            $font = $fontMetrics->get_font('Helvetica', 'normal');
            $size = 8;
            $pageText = "Page " . $PAGE_NUM . " of " . $PAGE_COUNT;
            // 计算位置,对应footer的右侧区域
            $y = $pdf->get_height() - 25;
            $x = $pdf->get_width() - 150;
            $pdf->text($x, $y, $pageText, $font, $size);
        }
    </script>
</body>

</html>

内容的提问来源于stack exchange,提问作者The Only Smart Boy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 19:37:02