فهرست منبع

导出文件代码

cqp 2 ماه پیش
والد
کامیت
dd4c465ead

+ 43 - 0
app/Exports/ItemSalaryFTMultipleSheetExport.php

@@ -0,0 +1,43 @@
+<?php
+
+namespace App\Exports;
+
+use Maatwebsite\Excel\Concerns\WithMultipleSheets;
+
+class ItemSalaryFTMultipleSheetExport implements WithMultipleSheets
+{
+    protected $monthsData;
+    protected $projects;
+
+    /**
+     * @param array $monthsData 格式需要包含该月特有的项目列表:
+     * [
+     * '2024年4月' => [
+     * 'projects' => ['RD01', 'RD02'],
+     * 'data' => [ [...] ]
+     * ],
+     * '2024年5月' => [
+     * 'projects' => ['RD03'],
+     * 'data' => [ [...] ]
+     * ]
+     * ]
+     */
+    public function __construct(array $monthsData)
+    {
+        $this->monthsData = $monthsData;
+    }
+
+    public function sheets(): array
+    {
+        $sheets = [];
+        foreach ($this->monthsData as $month => $item) {
+            // 每次实例化 Sheet 时,传入该月特有的 projects 列表
+            $sheets[] = new ItemSalaryFTSheetExport(
+                $month,
+                $item['data'],
+                $item['projects']
+            );
+        }
+        return $sheets;
+    }
+}

+ 141 - 0
app/Exports/ItemSalaryFTSheetExport.php

@@ -0,0 +1,141 @@
+<?php
+
+namespace App\Exports;
+
+use Maatwebsite\Excel\Concerns\FromCollection;
+use Maatwebsite\Excel\Concerns\WithEvents;
+use Maatwebsite\Excel\Concerns\WithStyles;
+use Maatwebsite\Excel\Concerns\WithTitle;
+use Maatwebsite\Excel\Concerns\WithCustomStartCell;
+use Maatwebsite\Excel\Events\AfterSheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+
+class ItemSalaryFTSheetExport implements FromCollection, WithEvents, WithStyles, WithCustomStartCell, WithTitle
+{
+    protected $month;
+    protected $data;
+    protected $projects;
+
+    public function __construct(string $month, array $data, array $projects)
+    {
+        $this->month = $month;
+        $this->data = $data;
+        $this->projects = $projects;
+    }
+
+    public function title(): string
+    {
+        return $this->month;
+    }
+
+    public function startCell(): string
+    {
+        return 'A5'; // 数据从 A5 开始写入 (1-4行是表头)
+    }
+
+    public function collection()
+    {
+        return collect($this->data);
+    }
+
+    public function registerEvents(): array
+    {
+        return [
+            AfterSheet::class => function(AfterSheet $event) {
+                $sheet = $event->sheet->getDelegate();
+
+                $projectCount = count($this->projects);
+                // 总列数 = 序号(1) + 姓名(1) + 工资(1) + 总工时(1) + 月工时(项目数+合计1) + 对应金额(项目数+总计1)
+                $totalColNum = 4 + ($projectCount + 1) + ($projectCount + 1);
+                $highestColumn = Coordinate::stringFromColumnIndex($totalColNum);
+
+                // --- 1. 第一行:主标题 ---
+                $sheet->mergeCells("A1:{$highestColumn}1");
+                $sheet->setCellValue('A1', "{$this->month}研发工资分摊表");
+                $sheet->getStyle('A1')->getFont()->setSize(14);
+                $sheet->getStyle('A1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
+
+                // --- 2. 第二行:单位名称 ---
+                $sheet->mergeCells("A2:{$highestColumn}2");
+                $sheet->setCellValue('A2', "单位:");
+                $sheet->getStyle('A2')->getFont()->setSize(14);
+
+                // --- 3. 第三、四行:复合表头 ---
+                // 序号、人员、工资、总工时 垂直合并
+                foreach (['A', 'B', 'C', 'D'] as $col) { $sheet->mergeCells("{$col}3:{$col}4"); }
+                $sheet->setCellValue('A3', '序号');
+                $sheet->setCellValue('B3', '技术人员');
+                $sheet->setCellValue('C3', '工资');
+                $sheet->setCellValue('D3', '月总工时');
+
+                // A. 月工时区域
+                $workHourStartCol = 5;
+                $workHourEndCol = $workHourStartCol + $projectCount; // 包含“合计工时”列
+                $workHourStartLetter = Coordinate::stringFromColumnIndex($workHourStartCol);
+                $workHourEndLetter = Coordinate::stringFromColumnIndex($workHourEndCol);
+
+                $sheet->mergeCells("{$workHourStartLetter}3:{$workHourEndLetter}3");
+                $sheet->setCellValue("{$workHourStartLetter}3", '月 工 时');
+
+                // 填充 RD 项目 (月工时下)
+                foreach ($this->projects as $index => $rdCode) {
+                    $colLetter = Coordinate::stringFromColumnIndex($workHourStartCol + $index);
+                    $sheet->setCellValue($colLetter . '4', $rdCode);
+                }
+                $sheet->setCellValue($workHourEndLetter . '4', '合计工时');
+
+                // B. 项目应计工资金额区域
+                $moneyStartCol = $workHourEndCol + 1;
+                $moneyEndCol = $moneyStartCol + $projectCount; // 包含“总计工资”列
+                $moneyStartLetter = Coordinate::stringFromColumnIndex($moneyStartCol);
+                $moneyEndLetter = Coordinate::stringFromColumnIndex($moneyEndCol);
+
+                $sheet->mergeCells("{$moneyStartLetter}3:{$moneyEndLetter}3");
+                $sheet->setCellValue("{$moneyStartLetter}3", '项目应计工资金额');
+
+                // 填充 RD 项目 (金额下)
+                foreach ($this->projects as $index => $rdCode) {
+                    $colLetter = Coordinate::stringFromColumnIndex($moneyStartCol + $index);
+                    $sheet->setCellValue($colLetter . '4', $rdCode);
+                }
+                $sheet->setCellValue($moneyEndLetter . '4', '总计工资');
+
+                // --- 4. 设置列宽 ---
+                $sheet->getColumnDimension('A')->setWidth(8);
+                $sheet->getColumnDimension('B')->setWidth(12);
+                for ($i = 3; $i <= $totalColNum; $i++) {
+                    $sheet->getColumnDimension(Coordinate::stringFromColumnIndex($i))->setWidth(13);
+                }
+            },
+        ];
+    }
+
+    public function styles(Worksheet $sheet)
+    {
+        $highestRow = $sheet->getHighestRow();
+        $highestColumn = $sheet->getHighestColumn();
+
+        // 1. 全表基础样式(居中 + 细边框 + 宋体)
+        $sheet->getStyle("A1:{$highestColumn}{$highestRow}")->applyFromArray([
+            'borders' => [
+                'allBorders' => ['borderStyle' => Border::BORDER_THIN],
+            ],
+            'alignment' => [
+                'vertical' => Alignment::VERTICAL_CENTER,
+                'horizontal' => Alignment::HORIZONTAL_CENTER,
+            ],
+            'font' => ['name' => '宋体'],
+        ]);
+
+        // 2. 特殊处理:第二行左对齐
+        $sheet->getStyle("A2:{$highestColumn}2")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
+
+        // 4. 数据行样式
+        $sheet->getStyle("A5:{$highestColumn}{$highestRow}")->getFont()->setSize(10);
+
+        return [];
+    }
+}

+ 137 - 0
app/Exports/ItemSalarySheetExport.php

@@ -0,0 +1,137 @@
+<?php
+
+namespace App\Exports;
+
+use Maatwebsite\Excel\Concerns\FromCollection;
+use Maatwebsite\Excel\Concerns\WithEvents;
+use Maatwebsite\Excel\Concerns\WithStyles;
+use Maatwebsite\Excel\Concerns\WithCustomStartCell;
+use Maatwebsite\Excel\Events\AfterSheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+
+class ItemSalarySheetExport implements FromCollection, WithEvents, WithStyles, WithCustomStartCell
+{
+    protected $projects;
+    protected $data;
+
+    public function __construct(array $projects, array $data)
+    {
+        $this->projects = $projects;
+        $this->data = $data;
+    }
+
+    /**
+     * 数据从第 5 行开始写入(1-4行留给自定义表头)
+     */
+    public function startCell(): string
+    {
+        return 'A5';
+    }
+
+    /**
+     * 返回导出数据集合
+     */
+    public function collection()
+    {
+        return collect($this->data);
+    }
+
+    /**
+     * 处理复杂的表头合并、列宽、固定文字
+     */
+    public function registerEvents(): array
+    {
+        return [
+            AfterSheet::class => function(AfterSheet $event) {
+                $sheet = $event->sheet->getDelegate();
+
+                // 1. 计算总列数
+                $projectCount = count($this->projects);
+                $totalColNum = 1 + ($projectCount * 2) + 1; // 年月(1) + 每个项目2列 + 合计(1)
+                $highestColumn = Coordinate::stringFromColumnIndex($totalColNum);
+
+                // --- 2. 设置列宽 ---
+                $sheet->getColumnDimension('A')->setWidth(15); // 年月列
+
+                for ($i = 0; $i < $projectCount; $i++) {
+                    $startColNum = 2 + ($i * 2);
+                    $colLetterDays = Coordinate::stringFromColumnIndex($startColNum);     // 天数
+                    $colLetterSalary = Coordinate::stringFromColumnIndex($startColNum + 1); // 工资
+
+                    // 设置比例:工资约为天数的 2 倍强
+                    $sheet->getColumnDimension($colLetterDays)->setWidth(7);
+                    $sheet->getColumnDimension($colLetterSalary)->setWidth(16);
+
+                    // --- 3. 第三、四行:RD项目表头设置 ---
+                    // 第三行项目名水平合并 (B3:C3)
+                    $sheet->mergeCells("{$colLetterDays}3:{$colLetterSalary}3");
+                    $sheet->setCellValue("{$colLetterDays}3", $this->projects[$i]);
+
+                    // 第四行子标题
+                    $sheet->setCellValue("{$colLetterDays}4", '天数');
+                    $sheet->setCellValue("{$colLetterSalary}4", '工资');
+                }
+
+                // 合计列宽度
+                $sheet->getColumnDimension($highestColumn)->setWidth(18);
+
+                // --- 4. 第一行:主标题 ---
+                $sheet->mergeCells("A1:{$highestColumn}1");
+                $sheet->setCellValue('A1', '研发项目工资总表');
+                $sheet->getStyle('A1')->getFont()->setSize(14);
+                $sheet->getStyle('A1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
+
+                // --- 5. 第二行:单位名称 ---
+                $sheet->mergeCells("A2:{$highestColumn}2");
+                $sheet->setCellValue('A2', '单位名称:');
+                $sheet->getStyle('A2')->getFont()->setSize(14);
+
+                // --- 6. 垂直合并“年月”和“合计” ---
+                $sheet->mergeCells('A3:A4');
+                $sheet->setCellValue('A3', '年月');
+
+                $lastColLetter = $highestColumn;
+                $sheet->mergeCells("{$lastColLetter}3:{$lastColLetter}4");
+                $sheet->setCellValue("{$lastColLetter}3", '合计');
+
+                // 表头区域整体居中
+                $sheet->getStyle("A3:{$highestColumn}4")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
+            },
+        ];
+    }
+
+    /**
+     * 设置表格基础样式和边框
+     */
+    public function styles(Worksheet $sheet)
+    {
+        $highestRow = $sheet->getHighestRow();
+        $highestColumn = $sheet->getHighestColumn();
+
+        // 定义统一的边框和对齐样式
+        $commonStyle = [
+            'borders' => [
+                'allBorders' => [
+                    'borderStyle' => Border::BORDER_THIN,
+                    'color' => ['argb' => '000000'],
+                ],
+            ],
+            'alignment' => [
+                'vertical' => Alignment::VERTICAL_CENTER,
+                'horizontal' => Alignment::HORIZONTAL_CENTER,
+            ],
+            'font' => [
+                'name' => '宋体',
+                'size' => 14,
+            ],
+        ];
+        $sheet->getStyle("A1:{$highestColumn}{$highestRow}")->applyFromArray($commonStyle);
+        $sheet->getStyle("A1")->getFont()->setSize(14);
+        $sheet->getStyle("A2")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
+
+        return [];
+    }
+}

+ 39 - 0
app/Exports/ManActivityTimeCardMultipleSheetExport.php

@@ -0,0 +1,39 @@
+<?php
+
+namespace App\Exports;
+
+use Maatwebsite\Excel\Concerns\WithMultipleSheets;
+
+class ManActivityTimeCardMultipleSheetExport implements WithMultipleSheets
+{
+    protected $projectsData;
+    protected $year;
+
+    /**
+     * @param array $projectsData 格式为:[
+     * 'RD01项目名' => [ [...行数据1], [...行数据2] ],
+     * 'RD02项目名' => [ [...行数据1], [...行数据2] ],
+     * ]
+     * @param string $year
+     */
+    public function __construct(array $projectsData, string $year = '2024')
+    {
+        $this->projectsData = $projectsData;
+        $this->year = $year;
+    }
+
+    /**
+     * 定义多个 Sheet
+     */
+    public function sheets(): array
+    {
+        $sheets = [];
+
+        foreach ($this->projectsData as $projectName => $data) {
+            // 每一项都实例化之前写的那个 ProjectAnnualSalaryExport
+            $sheets[] = new ManActivityTimeCardSheetExport($projectName, $data, $this->year);
+        }
+
+        return $sheets;
+    }
+}

+ 155 - 0
app/Exports/ManActivityTimeCardSheetExport.php

@@ -0,0 +1,155 @@
+<?php
+
+namespace App\Exports;
+
+use Maatwebsite\Excel\Concerns\FromCollection;
+use Maatwebsite\Excel\Concerns\WithEvents;
+use Maatwebsite\Excel\Concerns\WithStyles;
+use Maatwebsite\Excel\Concerns\WithTitle;
+use Maatwebsite\Excel\Concerns\WithCustomStartCell;
+use Maatwebsite\Excel\Events\AfterSheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Style\Fill;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+
+class ManActivityTimeCardSheetExport implements FromCollection, WithEvents, WithStyles, WithCustomStartCell, WithTitle
+{
+    protected $projectName;
+    protected $data;
+    protected $year;
+
+    public function __construct(string $projectName, array $data, string $year)
+    {
+        $this->projectName = $projectName;
+        $this->data = $data;
+        $this->year = $year;
+    }
+
+    public function title(): string
+    {
+        // Sheet名不能超过31个字符
+        return mb_substr($this->projectName, 0, 30);
+    }
+
+    public function startCell(): string
+    {
+        return 'A6'; // 数据从 A6 开始写入
+    }
+
+    public function collection()
+    {
+        return collect($this->data);
+    }
+
+    public function registerEvents(): array
+    {
+        return [
+            AfterSheet::class => function(AfterSheet $event) {
+                $sheet = $event->sheet->getDelegate();
+                $lastCol = 'R';
+
+                // --- 1. 设置列宽 ---
+                $widths = [
+                    'A' => 8, 'B' => 12, 'C' => 10, 'D' => 10, 'E' => 10, 'F' => 10,
+                    'G' => 12, 'H' => 10, 'I' => 10, 'J' => 12, 'K' => 10, 'L' => 10,
+                    'M' => 12, 'N' => 10, 'O' => 10, 'P' => 12, 'Q' => 10, 'R' => 10
+                ];
+                foreach ($widths as $col => $w) {
+                    $sheet->getColumnDimension($col)->setWidth($w);
+                }
+
+                // --- 2. 第一行:主标题 (宋体 18 加粗) ---
+                $sheet->mergeCells("A1:{$lastCol}1");
+                $sheet->setCellValue('A1', "{$this->year}年研发人员研发活动工时占比计算及研发人员费用调整表");
+                $sheet->getStyle('A1')->applyFromArray([
+                    'font' => ['name' => '宋体', 'size' => 18, 'bold' => true],
+                    'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
+                ]);
+
+                // --- 3. 第二行:项目名称 (宋体 9 加粗) ---
+                $sheet->mergeCells("A2:{$lastCol}2");
+                $sheet->setCellValue('A2', "项目名称:{$this->projectName}");
+                $sheet->getStyle('A2')->applyFromArray([
+                    'font' => ['name' => '宋体', 'size' => 9, 'bold' => true],
+                    'alignment' => ['horizontal' => Alignment::HORIZONTAL_LEFT],
+                ]);
+
+                // --- 4. 第三至五行:表头逻辑 (宋体 11 加粗 自动换行) ---
+                foreach (['A', 'B', 'C'] as $col) { $sheet->mergeCells("{$col}3:{$col}5"); }
+                $sheet->setCellValue('A3', "月份"); $sheet->setCellValue('B3', "人员类别"); $sheet->setCellValue('C3', "姓名");
+
+                $sheet->mergeCells('D3:F3'); $sheet->setCellValue('D3', '工时占比计算');
+                $sheet->mergeCells('D4:D5'); $sheet->setCellValue('D4', '应出勤工时');
+                $sheet->mergeCells('E4:E5'); $sheet->setCellValue('E4', '研发出勤工时');
+                $sheet->mergeCells('F4:F5'); $sheet->setCellValue('F4', '研发工时占比');
+
+                $sheet->mergeCells('G3:L3'); $sheet->setCellValue('G3', '账面归集金额');
+                $sheet->mergeCells('G4:I4'); $sheet->setCellValue('G4', '归集总额');
+                $sheet->setCellValue('G5', '工资总额'); $sheet->setCellValue('H5', '社保'); $sheet->setCellValue('I5', '公积金');
+
+                $sheet->mergeCells('J4:L4'); $sheet->setCellValue('J4', '其中属于研发活动期间金额');
+                $sheet->setCellValue('J5', '工资总额'); $sheet->setCellValue('K5', '社保'); $sheet->setCellValue('L5', '公积金');
+
+                $sheet->mergeCells('M3:O4'); $sheet->setCellValue('M3', '研发活动期间的确定金额');
+                $sheet->setCellValue('M5', '工资总额'); $sheet->setCellValue('N5', '社保'); $sheet->setCellValue('O5', '公积金');
+
+                $sheet->mergeCells('P3:R4'); $sheet->setCellValue('P3', '加计调整金额');
+                $sheet->setCellValue('P5', '工资总额'); $sheet->setCellValue('Q5', '社保'); $sheet->setCellValue('R5', '公积金');
+
+                // 应用三到五行样式
+                $sheet->getStyle("A3:{$lastCol}5")->applyFromArray([
+                    'font' => ['name' => '宋体', 'size' => 11, 'bold' => true],
+                    'alignment' => [
+                        'wrapText' => true,
+                        'horizontal' => Alignment::HORIZONTAL_CENTER,
+                        'vertical' => Alignment::VERTICAL_CENTER
+                    ],
+                ]);
+
+                // --- 5. 染色逻辑 ---
+                $highestRow = $sheet->getHighestRow();
+                for ($row = 6; $row <= $highestRow; $row++) {
+                    $cellA = $sheet->getCell("A{$row}")->getValue();
+                    if ($cellA === '小计:') {
+                        $sheet->getStyle("A{$row}:{$lastCol}{$row}")->getFill()
+                            ->setFillType(Fill::FILL_SOLID)->getStartColor()->setRGB('F4B084');
+                    } elseif ($cellA === '合计') {
+                        $sheet->getStyle("A{$row}:{$lastCol}{$row}")->getFill()
+                            ->setFillType(Fill::FILL_SOLID)->getStartColor()->setRGB('FFFF00');
+                    }
+                }
+            },
+        ];
+    }
+
+    public function styles(Worksheet $sheet)
+    {
+        $highestRow = $sheet->getHighestRow();
+        $lastCol = 'R';
+
+        return [
+            // 全表通用对齐和边框
+            "A1:{$lastCol}{$highestRow}" => [
+                'borders' => [
+                    'allBorders' => ['borderStyle' => Border::BORDER_THIN],
+                ],
+                'alignment' => [
+                    'vertical' => Alignment::VERTICAL_CENTER,
+                ],
+            ],
+            // 数据行:宋体 10,居中,不加粗
+            "A6:{$lastCol}{$highestRow}" => [
+                'font' => [
+                    'name' => '宋体',
+                    'size' => 10,
+                    'bold' => false
+                ],
+                'alignment' => [
+                    'horizontal' => Alignment::HORIZONTAL_CENTER,
+                ],
+            ]
+        ];
+    }
+}

+ 29 - 0
app/Exports/ManMonthlyWorkHourMultipleSheetExport.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Exports;
+
+use Maatwebsite\Excel\Concerns\WithMultipleSheets;
+
+class ManMonthlyWorkHourMultipleSheetExport implements WithMultipleSheets
+{
+    protected $monthsData;
+
+    public function __construct(array $monthsData)
+    {
+        $this->monthsData = $monthsData;
+    }
+
+    public function sheets(): array
+    {
+        $sheets = [];
+        foreach ($this->monthsData as $monthName => $config) {
+            // 传入 3 个参数,匹配子类新的构造函数
+            $sheets[] = new ManMonthlyWorkHourSheetExport(
+                (string)$monthName,
+                $config['data'] ?? [],
+                (int)($config['days'] ?? 30)
+            );
+        }
+        return $sheets;
+    }
+}

+ 117 - 0
app/Exports/ManMonthlyWorkHourSheetExport.php

@@ -0,0 +1,117 @@
+<?php
+
+namespace App\Exports;
+
+use Maatwebsite\Excel\Concerns\WithEvents;
+use Maatwebsite\Excel\Concerns\WithTitle;
+use Maatwebsite\Excel\Events\AfterSheet;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Style\Borders;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+
+class ManMonthlyWorkHourSheetExport implements WithEvents, WithTitle
+{
+    protected $monthName;
+    protected $data;
+    protected $daysInMonth;
+
+    public function __construct(string $monthName, array $data, int $daysInMonth)
+    {
+        $this->monthName   = $monthName;
+        $this->data        = $data;
+        $this->daysInMonth = $daysInMonth;
+    }
+
+    public function title(): string
+    {
+        return $this->monthName;
+    }
+
+    public function registerEvents(): array
+    {
+        return [
+            AfterSheet::class => function (AfterSheet $event) {
+                $sheet = $event->sheet->getDelegate();
+
+                // 计算最大列(项目B + 姓名C + 天数)
+                $totalColNum = 2 + $this->daysInMonth;
+                $highestColumn = Coordinate::stringFromColumnIndex($totalColNum);
+
+                // --- 1. 第一行:主标题 ---
+                $sheet->mergeCells("A1:{$highestColumn}1");
+                $sheet->setCellValue('A1', "{$this->monthName}研发人员工时统计表");
+                $sheet->getStyle('A1')->getFont()->setSize(14);
+                $sheet->getStyle('A1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
+
+                // --- 2. 第二行:单位行 ---
+                $sheet->mergeCells("A2:{$highestColumn}2");
+                $sheet->setCellValue('A2', "单位:");
+                $sheet->getStyle('A2')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
+
+                // --- 3. 第三行表头设置 ---
+
+                // (1) A3: 项目名称 (独立单元格)
+                $sheet->setCellValue('A3', "项目名称");
+                $sheet->getColumnDimension('A')->setWidth(14);
+
+                // (2) B3: 人员名称/日期 (斜线分割单元格)
+                $sheet->setCellValue('B3', "                        日期       \n人员名称");
+                $sheet->getStyle('B3')->getAlignment()->setWrapText(true);
+                $sheet->getStyle('B3')->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
+                $sheet->getStyle('B3')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
+                $sheet->getColumnDimension('B')->setWidth(16);
+
+                // 绘制 B3 斜线
+                $sheet->getStyle('B3')->getBorders()->applyFromArray([
+                    'diagonal' => [
+                        'borderStyle' => Border::BORDER_THIN,
+                        'color' => ['rgb' => '000000'],
+                    ],
+                ]);
+                $sheet->getStyle('B3')->getBorders()->setDiagonalDirection(Borders::DIAGONAL_DOWN);
+
+                // (3) C3 往后: 动态日期 (1, 2, 3...)
+                for ($day = 1; $day <= $this->daysInMonth; $day++) {
+                    $colLetter = Coordinate::stringFromColumnIndex($day + 2); // 从 C 列开始
+                    $sheet->setCellValue($colLetter . '3', $day);
+                    $sheet->getColumnDimension($colLetter)->setWidth(4);
+                }
+
+                // 设置第三行行高
+                $sheet->getRowDimension(3)->setRowHeight(50);
+
+                // --- 4. 写入数据行 (从第4行开始) ---
+                $dataStartRow = 4;
+                if (!empty($this->data)) {
+                    foreach ($this->data as $rowIndex => $row) {
+                        $currentRow = $dataStartRow + $rowIndex;
+                        foreach ($row as $colIndex => $cellValue) {
+                            $colLetter = Coordinate::stringFromColumnIndex($colIndex + 1);
+                            $sheet->setCellValue($colLetter . $currentRow, $cellValue);
+                        }
+                    }
+                }
+
+                // --- 5. 全局样式微调 ---
+                $highestRow = $sheet->getHighestRow();
+
+                // 所有单元格垂直居中
+                $sheet->getStyle("A1:{$highestColumn}{$highestRow}")->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
+
+                // 表头及日期数据水平居中 (排除 A3 和 B3)
+                $sheet->getStyle("A3:{$highestColumn}{$highestRow}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
+                $sheet->getStyle('B3')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT); // 斜线格保持左对齐
+
+                // 添加表格边框 (从 A3 开始)
+                $sheet->getStyle("A3:{$highestColumn}{$highestRow}")->applyFromArray([
+                    'borders' => [
+                        'allBorders' => [
+                            'borderStyle' => Border::BORDER_THIN,
+                        ],
+                    ],
+                ]);
+            },
+        ];
+    }
+}

+ 29 - 0
app/Exports/ProjectDepreciationMultipleSheetExport.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Exports;
+
+use Maatwebsite\Excel\Concerns\WithMultipleSheets;
+
+class ProjectDepreciationMultipleSheetExport implements WithMultipleSheets
+{
+    protected $data; // 格式:[ '2025-RD01' => [ 'project_name' => '...', 'months' => [ 1 => [...数据...], 2 => [...] ] ] ]
+
+    public function __construct(array $data)
+    {
+        $this->data = $data;
+    }
+
+    public function sheets(): array
+    {
+        $sheets = [];
+        foreach ($this->data as $groupKey => $payload) {
+            // $groupKey 比如 "2025-RD01"
+            $sheets[] = new ProjectDepreciationSheetExport(
+                $groupKey,
+                $payload['project_name'],
+                $payload['months']
+            );
+        }
+        return $sheets;
+    }
+}

+ 127 - 0
app/Exports/ProjectDepreciationSheetExport.php

@@ -0,0 +1,127 @@
+<?php
+
+namespace App\Exports;
+
+use Maatwebsite\Excel\Concerns\WithEvents;
+use Maatwebsite\Excel\Concerns\WithTitle;
+use Maatwebsite\Excel\Events\AfterSheet;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Style\Borders;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+
+class ProjectDepreciationSheetExport implements WithEvents, WithTitle
+{
+    protected $groupKey;
+    protected $projectName;
+    protected $monthsData;
+
+    public function __construct($groupKey, $projectName, $monthsData)
+    {
+        $this->groupKey = $groupKey;
+        $this->projectName = $projectName;
+        $this->monthsData = $monthsData;
+    }
+
+    public function title(): string { return $this->groupKey; }
+
+    public function registerEvents(): array
+    {
+        return [
+            AfterSheet::class => function (AfterSheet $event) {
+                $sheet = $event->sheet->getDelegate();
+                $currentRow = 1;
+
+                // --- 关键:预先设置列宽 ---
+                $sheet->getColumnDimension('A')->setWidth(6);
+                $sheet->getColumnDimension('B')->setWidth(25);
+                $sheet->getColumnDimension('J')->setWidth(15);
+                $sheet->getColumnDimension('L')->setWidth(12); // 给 L 列足够的空间
+
+                foreach ($this->monthsData as $month => $items) {
+                    // 记录本月表格的起始行
+                    $tableStartRow = $currentRow;
+
+                    // 1. 标题 (A-J)
+                    $sheet->mergeCells("A{$currentRow}:J{$currentRow}");
+                    $sheet->setCellValue("A{$currentRow}", "2025年度研发项目折旧费用调整表");
+                    $sheet->getStyle("A{$currentRow}")->getFont()->setBold(true)->setSize(16);
+                    $sheet->getStyle("A{$currentRow}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
+                    $currentRow++;
+
+                    // 2. 项目名称与单位 (A-J)
+                    $sheet->setCellValue("A{$currentRow}", "研发项目名称:{$this->projectName}");
+                    $sheet->mergeCells("H{$currentRow}:J{$currentRow}");
+                    $sheet->setCellValue("H{$currentRow}", "单位:元以下角分");
+                    $sheet->getStyle("H{$currentRow}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
+                    $currentRow++;
+
+                    // 3. 表头 (A-J)
+                    $headerRow1 = $currentRow;
+                    $sheet->mergeCells("A{$currentRow}:A" . ($currentRow + 1)); $sheet->setCellValue("A{$currentRow}", "序号");
+                    $sheet->mergeCells("B{$currentRow}:B" . ($currentRow + 1)); $sheet->setCellValue("B{$currentRow}", "设备名称");
+                    $sheet->mergeCells("C{$currentRow}:E{$currentRow}"); $sheet->setCellValue("C{$currentRow}", "设备工时");
+                    $sheet->mergeCells("F{$currentRow}:I{$currentRow}"); $sheet->setCellValue("F{$currentRow}", "设备折旧");
+                    $sheet->mergeCells("J{$currentRow}:J" . ($currentRow + 1)); $sheet->setCellValue("J{$currentRow}", "加计调整\n金额");
+                    $currentRow++;
+
+                    $sheet->setCellValue("C{$currentRow}", "设备总工时");
+                    $sheet->setCellValue("D{$currentRow}", "其中本项目\n工时");
+                    $sheet->setCellValue("E{$currentRow}", "研发活动工\n时占比(%)");
+                    $sheet->setCellValue("F{$currentRow}", "原值");
+                    $sheet->setCellValue("G{$currentRow}", "折旧额");
+                    $sheet->setCellValue("H{$currentRow}", "确定的本\n项目折旧额");
+                    $sheet->setCellValue("I{$currentRow}", "备注说明");
+                    $currentRow++;
+
+                    // 4. 数据行
+                    $dataRowsCount = count($items);
+                    foreach ($items as $index => $item) {
+                        $sheet->setCellValue("A{$currentRow}", $index + 1);
+                        $sheet->setCellValue("B{$currentRow}", $item['name'] ?? '');
+                        $sheet->setCellValue("C{$currentRow}", $item['total_hours'] ?? 0);
+                        $sheet->setCellValue("D{$currentRow}", $item['project_hours'] ?? 0);
+                        $sheet->setCellValue("E{$currentRow}", ($item['ratio'] ?? 0) . '%');
+                        $sheet->setCellValue("F{$currentRow}", $item['original_value'] ?? 0);
+                        $sheet->setCellValue("G{$currentRow}", $item['depreciation'] ?? 0);
+                        $sheet->setCellValue("H{$currentRow}", $item['confirmed_depreciation'] ?? 0);
+                        $sheet->setCellValue("I{$currentRow}", "");
+                        $sheet->setCellValue("J{$currentRow}", $item['adjust_amount'] ?? 0);
+
+                        // --- 核心修正:确保月份在 L 列显示 ---
+                        // 我们把月份放在数据行的第一行对应的 L 列
+                        if ($index === 0) {
+                            $sheet->setCellValue("L{$currentRow}", $month . "月");
+                            // 样式:宋体、加粗、靠左对齐
+                            $sheet->getStyle("L{$currentRow}")->getFont()->setName('SimSun')->setBold(true)->setSize(12);
+                            $sheet->getStyle("L{$currentRow}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
+                        }
+                        $currentRow++;
+                    }
+
+                    // 5. 合计行
+                    $sheet->setCellValue("A{$currentRow}", "合计");
+                    $sheet->mergeCells("A{$currentRow}:B{$currentRow}");
+                    // 这里可以填充合计逻辑...
+                    $currentRow++;
+
+                    // 6. 备注
+                    $sheet->mergeCells("A{$currentRow}:J{$currentRow}");
+                    $sheet->setCellValue("A{$currentRow}", "备注:按研发项目的研发活动工作使用情况记录等相关证据资料...");
+                    $sheet->getStyle("A{$currentRow}")->getFont()->setSize(9);
+                    $currentRow += 2; // 间隔
+
+                    // --- 7. 样式应用:仅针对 A-J 列加边框 ---
+                    $tableRange = "A{$tableStartRow}:J" . ($currentRow - 2);
+                    $sheet->getStyle($tableRange)->getFont()->setName('SimSun');
+                    $sheet->getStyle("A" . ($tableStartRow + 2) . ":J" . ($currentRow - 2))->applyFromArray([
+                        'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN]],
+                    ]);
+
+                    // 居中对齐
+                    $sheet->getStyle("A{$tableStartRow}:J" . ($currentRow - 2))->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
+                }
+            },
+        ];
+    }
+}

+ 36 - 0
app/Exports/ResearchExpenseMultipleSheetExport.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Exports;
+
+use Maatwebsite\Excel\Concerns\WithMultipleSheets;
+
+class ResearchExpenseMultipleSheetExport implements WithMultipleSheets
+{
+    protected $sheetsData;
+
+    /**
+     * @param array $sheetsData 现在的格式应该是:
+     * [
+     * '2025-RD01' => ['project' => [...], 'data' => [...]],
+     * '2025-RD02' => ['project' => [...], 'data' => [...]]
+     * ]
+     */
+    public function __construct(array $sheetsData)
+    {
+        $this->sheetsData = $sheetsData;
+    }
+
+    public function sheets(): array
+    {
+        $sheets = [];
+        foreach ($this->sheetsData as $sheetTitle => $content) {
+            // 将每一个唯一的 Key (年份-项目) 传给具体的 Sheet 类
+            $sheets[] = new ResearchExpenseSheetExport(
+                (string)$sheetTitle, // 这里的 title 会显示在 Excel 标签页上
+                $content['data'],
+                $content['project'] ?? []
+            );
+        }
+        return $sheets;
+    }
+}

+ 168 - 0
app/Exports/ResearchExpenseSheetExport.php

@@ -0,0 +1,168 @@
+<?php
+
+namespace App\Exports;
+
+use Maatwebsite\Excel\Concerns\FromCollection;
+use Maatwebsite\Excel\Concerns\WithEvents;
+use Maatwebsite\Excel\Concerns\WithStyles;
+use Maatwebsite\Excel\Concerns\WithTitle;
+use Maatwebsite\Excel\Concerns\WithCustomStartCell;
+use Maatwebsite\Excel\Events\AfterSheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+
+class ResearchExpenseSheetExport implements FromCollection, WithEvents, WithStyles, WithCustomStartCell, WithTitle
+{
+    protected $sheetTitle; // Sheet 标签名,如 "2025-RD01"
+    protected $data;
+    protected $projectInfo;
+    protected $year;
+
+    public function __construct(string $sheetTitle, array $data, array $projectInfo = [])
+    {
+        $this->sheetTitle = $sheetTitle;
+        $this->data = $data;
+        $this->projectInfo = $projectInfo;
+
+        // 自动从 Sheet 标题中提取前4位作为大标题年份
+        $this->year = substr($sheetTitle, 0, 4);
+    }
+
+    public function title(): string
+    {
+        return $this->sheetTitle;
+    }
+
+    public function startCell(): string
+    {
+        return 'A7'; // 数据从 A7 开始写入
+    }
+
+    public function collection()
+    {
+        return collect($this->data);
+    }
+
+    public function registerEvents(): array
+    {
+        return [
+            AfterSheet::class => function(AfterSheet $event) {
+                $sheet = $event->sheet->getDelegate();
+
+                // --- 1. 设置列宽 (精简适中版) ---
+                $sheet->getColumnDimension('A')->setWidth(12);  // 日期
+                $sheet->getColumnDimension('B')->setWidth(6);   // 种类
+                $sheet->getColumnDimension('C')->setWidth(6);   // 号数
+                $sheet->getColumnDimension('D')->setWidth(30);  // 摘要
+                $sheet->getColumnDimension('E')->setWidth(14);  // 会计金额
+                $sheet->getColumnDimension('F')->setWidth(14);  // 税法金额
+                $sheet->getColumnDimension('G')->setWidth(13);  // 人员人工
+                $sheet->getColumnDimension('H')->setWidth(13);  // 直接投入
+                $sheet->getColumnDimension('I')->setWidth(11);  // 折旧
+                $sheet->getColumnDimension('J')->setWidth(11);  // 无形资产
+                $sheet->getColumnDimension('K')->setWidth(11);  // 新产品
+                $sheet->getColumnDimension('L')->setWidth(11);  // 其他
+                $sheet->getColumnDimension('M')->setWidth(18);  // 委托境内
+                $sheet->getColumnDimension('N')->setWidth(18);  // 委托境外
+
+                // --- 2. 设置行高 ---
+                $sheet->getRowDimension('2')->setRowHeight(45); // 大标题行
+                $sheet->getRowDimension('3')->setRowHeight(40); // 项目信息行
+                $sheet->getRowDimension('4')->setRowHeight(22); // 表头1
+                $sheet->getRowDimension('5')->setRowHeight(25); // 表头2
+                $sheet->getRowDimension('6')->setRowHeight(60); // 表头底行 (容纳长文字)
+
+                // --- 3. 第二行:大标题 (居中/加粗) ---
+                $sheet->mergeCells("A2:N2");
+                $sheet->setCellValue('A2', "{$this->year}年研发支出辅助账");
+                $sheet->getStyle('A2')->applyFromArray([
+                    'font' => ['size' => 16],
+                    'alignment' => [
+                        'horizontal' => Alignment::HORIZONTAL_CENTER,
+                        'vertical' => Alignment::VERTICAL_CENTER,
+                    ],
+                ]);
+
+                // --- 4. 第三行:项目信息 (精准合并/无边框) ---
+                // 项目编号:标签 ABC,值 DE
+                $sheet->mergeCells("A3:C3");
+                $sheet->setCellValue('A3', '项目编号:');
+                $sheet->mergeCells("D3:E3");
+                $sheet->setCellValue('D3', $this->projectInfo['code'] ?? '');
+
+                // 项目名称:标签 F,值 G (开启换行)
+                $sheet->setCellValue('F3', '项目名称:');
+                $sheet->setCellValue('G3', $this->projectInfo['name'] ?? '');
+                $sheet->getStyle('G3')->getAlignment()->setWrapText(true);
+
+                $sheet->setCellValue('H3', '完成情况:');
+                $sheet->setCellValue('I3', '已完成');
+                $sheet->setCellValue('J3', '支出类型:');
+                $sheet->setCellValue('K3', '费用化');
+                $sheet->setCellValue('M3', '金额单位:');
+                $sheet->setCellValue('N3', '元');
+
+                // 第三行对齐
+                $sheet->getStyle('A3:N3')->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
+
+                // --- 5. 复杂表头绘制 (第 4-6 行) ---
+                // A-D 凭证信息
+                $sheet->mergeCells("A4:D5");
+                $sheet->setCellValue('A4', '凭证信息');
+                $sheet->setCellValue('A6', '日期');
+                $sheet->setCellValue('B6', '种类');
+                $sheet->setCellValue('C6', '号数');
+                $sheet->setCellValue('D6', '摘要');
+
+                // E-F 金额列 (垂直合并)
+                $sheet->mergeCells("E4:E6");
+                $sheet->setCellValue('E4', "会计凭证记载\n金额");
+                $sheet->mergeCells("F4:F6");
+                $sheet->setCellValue('F4', "税法规定的归\n集金额");
+
+                // G-N 费用明细总标题
+                $sheet->mergeCells("G4:N4");
+                $sheet->setCellValue('G4', '费用明细(税法规定)');
+
+                // 各科目垂直合并 (5-6行)
+                $subItems = [
+                    'G' => '人员人工费用', 'H' => '直接投入费用', 'I' => '折旧费用',
+                    'J' => '无形资产摊销', 'K' => '新产品设计费等', 'L' => '其他相关费用'
+                ];
+                foreach ($subItems as $col => $text) {
+                    $sheet->mergeCells("{$col}5:{$col}6");
+                    $sheet->setCellValue("{$col}5", $text);
+                }
+
+                // 委托研发费用
+                $sheet->mergeCells("M5:N5");
+                $sheet->setCellValue('M5', '委托研发费用');
+                $sheet->setCellValue('M6', "委托境内机构或个人进行研\n发活动所发生的费用");
+                $sheet->setCellValue('N6', "委托境外机构进行研发活动\n所发生的费用");
+
+                // --- 6. 开启换行 ---
+                $sheet->getStyle('A4:N6')->getAlignment()->setWrapText(true);
+            },
+        ];
+    }
+
+    public function styles(Worksheet $sheet)
+    {
+        $highestRow = $sheet->getHighestRow();
+
+        // 全局基础样式:从第 4 行开始加边框 (跳过第 3 行项目信息)
+        $sheet->getStyle("A4:N{$highestRow}")->applyFromArray([
+            'borders' => [
+                'allBorders' => ['borderStyle' => Border::BORDER_THIN],
+            ],
+            'alignment' => [
+                'vertical' => Alignment::VERTICAL_CENTER,
+                'horizontal' => Alignment::HORIZONTAL_CENTER,
+            ],
+            'font' => ['name' => '宋体', 'size' => 10],
+        ]);
+
+        return [];
+    }
+}

+ 24 - 0
app/Exports/ResearchExpenseSummaryMultipleSheetExport.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace App\Exports;
+
+use Maatwebsite\Excel\Concerns\WithMultipleSheets;
+
+class ResearchExpenseSummaryMultipleSheetExport implements WithMultipleSheets
+{
+    protected $data; // 格式: ['2026' => [ 'items' => [...], 'summary' => [...] ], '2025' => [...] ]
+
+    public function __construct(array $data)
+    {
+        $this->data = $data;
+    }
+
+    public function sheets(): array
+    {
+        $sheets = [];
+        foreach ($this->data as $year => $payload) {
+            $sheets[] = new ResearchExpenseSummarySheetExport($year, $payload);
+        }
+        return $sheets;
+    }
+}

+ 173 - 0
app/Exports/ResearchExpenseSummarySheetExport.php

@@ -0,0 +1,173 @@
+<?php
+
+namespace App\Exports;
+
+use Maatwebsite\Excel\Concerns\WithEvents;
+use Maatwebsite\Excel\Concerns\WithTitle;
+use Maatwebsite\Excel\Events\AfterSheet;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+
+class ResearchExpenseSummarySheetExport implements WithEvents, WithTitle
+{
+    protected $year;
+    protected $items;
+    protected $taxId;
+    protected $companyName;
+
+    public function __construct($year, array $payload)
+    {
+        $this->year = $year;
+        $this->items = $payload['items'] ?? [];
+        $this->taxId = $payload['tax_id'] ?? '';
+        $this->companyName = $payload['company_name'] ?? '';
+    }
+
+    public function title(): string { return $this->year . '年汇总表'; }
+
+    public function registerEvents(): array
+    {
+        return [
+            AfterSheet::class => function (AfterSheet $event) {
+                $sheet = $event->sheet->getDelegate();
+
+                // 1. 设置列宽 (A-Q)
+                $widths = ['A'=>12, 'B'=>40, 'C'=>10, 'D'=>12, 'E'=>18, 'F'=>12, 'G'=>12, 'H'=>12, 'I'=>12, 'J'=>12, 'K'=>15, 'L'=>12, 'M'=>12, 'N'=>12, 'O'=>12, 'P'=>12, 'Q'=>12];
+                foreach ($widths as $col => $w) { $sheet->getColumnDimension($col)->setWidth($w); }
+
+                // 2. 绘制表头 (去大标题样式)
+                $this->drawCorrectHeader($sheet);
+
+                // 3. 填充数据
+                $currentRow = 8;
+                $capRows = []; // 仅存 资本化且已完成(status=3) 的行
+                $expRows = []; // 存 费用化 的行
+
+                foreach ($this->items as $item) {
+                    $sheet->setCellValue("A{$currentRow}", $item['no']);
+                    $sheet->setCellValue("B{$currentRow}", $item['name']);
+                    $sheet->setCellValue("C{$currentRow}", $item['status'] == 3 ? '已完成' : '进行中');
+                    $sheet->setCellValue("D{$currentRow}", $item['type']);
+
+                    // 原始数据
+                    $sheet->setCellValue("F{$currentRow}", $item['val1']); // 1.人工
+                    $sheet->setCellValue("G{$currentRow}", $item['val2']); // 2.投入
+                    $sheet->setCellValue("H{$currentRow}", $item['val3']); // 3.折旧
+                    $sheet->setCellValue("I{$currentRow}", $item['val4']); // 4.摊销
+                    $sheet->setCellValue("J{$currentRow}", $item['val5']); // 5.新产品
+                    $sheet->setCellValue("L{$currentRow}", $item['val7_1']); // 7.1
+                    $sheet->setCellValue("N{$currentRow}", $item['val8_1']); // 8.1
+                    $sheet->setCellValue("P{$currentRow}", $item['val8_3'] ?? 0); // 8.3
+
+                    // 【逻辑修正】明细行公式
+                    $sheet->setCellValue("K{$currentRow}", "=SUM(F{$currentRow}:J{$currentRow})"); // 6列
+                    $sheet->setCellValue("O{$currentRow}", "=N{$currentRow}*0.8"); // 8.2 = 8.1 * 80%
+                    // 明细行不限制 7.2 和 8.4,通常直接取 7.1 和 8.3
+                    $sheet->setCellValue("M{$currentRow}", "=L{$currentRow}"); // 7.2
+                    $sheet->setCellValue("Q{$currentRow}", "=P{$currentRow}"); // 8.4
+                    // E列 = 6 + 7.2 + 8.2 + 8.4
+                    $sheet->setCellValue("E{$currentRow}", "=K{$currentRow}+M{$currentRow}+O{$currentRow}+Q{$currentRow}");
+
+                    if ($item['type'] == '资本化支出' && $item['status'] == 3) {
+                        $capRows[] = $currentRow;
+                    } elseif ($item['type'] == '费用化支出') {
+                        $expRows[] = $currentRow;
+                    }
+                    $currentRow++;
+                }
+
+                // 4. 合计行勾稽计算
+                $this->applySummaryLogic($sheet, $currentRow, $capRows, $expRows);
+
+                // 5. 样式设置
+                $lastDataRow = $currentRow + 3;
+                $sheet->getStyle("A5:Q{$lastDataRow}")->applyFromArray(['borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN]]]);
+                $sheet->getStyle("E8:Q{$lastDataRow}")->getNumberFormat()->setFormatCode('#,##0.00');
+                $sheet->getStyle("A5:Q7")->getAlignment()->setWrapText(true)->setHorizontal('center')->setVertical('center');
+
+                // 签字行
+                $sheet->setCellValue("A" . ($lastDataRow + 1), "法定代表人(签章):");
+            },
+        ];
+    }
+
+    private function drawCorrectHeader($sheet)
+    {
+        $sheet->mergeCells('A2:Q2');
+        $sheet->setCellValue('A2', '研发支出辅助账汇总表');
+        $sheet->getStyle('A2')->getAlignment()->setHorizontal('center');
+        $sheet->getStyle('A2')->getFont()->setSize(16)->setBold(true);
+
+        $sheet->setCellValue('A3', '纳税人识别号:' . $this->taxId);
+        $sheet->setCellValue('J3', '纳税人名称:' . $this->companyName);
+        $sheet->setCellValue('O3', "属期:{$this->year}年");
+        $sheet->setCellValue('Q3', '单位:元');
+
+        $vHeaders = ['A'=>'项目编号', 'B'=>'项目名称', 'C'=>'完成情况', 'D'=>'支出类型', 'E'=>"允许加计\n扣除金额合计"];
+        foreach ($vHeaders as $col => $text) {
+            $sheet->mergeCells("{$col}5:{$col}7");
+            $sheet->setCellValue("{$col}5", $text);
+        }
+
+        $subHeaders = ['F'=>'人员人工费用', 'G'=>'直接投入费用', 'H'=>'折旧费用', 'I'=>'无形资产摊销', 'J'=>'新产品设计费等', 'K'=>'前五项 小计'];
+        foreach ($subHeaders as $col => $text) {
+            $sheet->mergeCells("{$col}5:{$col}6");
+            $sheet->setCellValue("{$col}5", $text);
+        }
+
+        $sheet->mergeCells('L5:M5'); $sheet->setCellValue('L5', '其他相关费用及限额');
+        $sheet->setCellValue('L6', '其他相关费用合计'); $sheet->setCellValue('M6', "经限额调整后的\n其他相关费用");
+
+        $sheet->mergeCells('N5:Q5'); $sheet->setCellValue('N5', '委托研发费用及限额');
+        $sheet->setCellValue('N6', "委托境内机构或\n个人研发费用"); $sheet->setCellValue('O6', "允许加计扣除的\n委托境内研发费");
+        $sheet->setCellValue('P6', "委托境外机构\n进行研发活动费"); $sheet->setCellValue('Q6', "经限额调整后的\n委托境外研发费");
+
+        $nums = ['F'=>'1','G'=>'2','H'=>'3','I'=>'4','J'=>'5','K'=>'6','L'=>'7.1','M'=>'7.2','N'=>'8.1','O'=>'8.2','P'=>'8.3','Q'=>'8.4'];
+        foreach ($nums as $col => $n) { $sheet->setCellValue("{$col}7", $n); }
+    }
+
+    private function applySummaryLogic($sheet, $r, $capRows, $expRows)
+    {
+        $r_cap = $r; $r_exp = $r + 1; $r_oth = $r + 2; $r_all = $r + 3;
+        $labels = ['资本化金额小计', '费用化金额小计', '其中:其他事项', '全额合计'];
+        foreach ($labels as $i => $label) {
+            $sheet->mergeCells("A" . ($r+$i) . ":D" . ($r+$i));
+            $sheet->setCellValue("A" . ($r+$i), $label);
+        }
+
+        // 基础汇总列 (F,G,H,I,J,L,N,P)
+        $cols = ['F', 'G', 'H', 'I', 'J', 'L', 'N', 'P'];
+        foreach ($cols as $col) {
+            $f_cap = !empty($capRows) ? "=SUM(".implode(',', array_map(fn($row)=>"{$col}{$row}", $capRows)).")" : "0";
+            $f_exp = !empty($expRows) ? "=SUM(".implode(',', array_map(fn($row)=>"{$col}{$row}", $expRows)).") + {$col}{$r_oth}" : "{$col}{$r_oth}";
+            $sheet->setCellValue("{$col}{$r_cap}", $f_cap);
+            $sheet->setCellValue("{$col}{$r_exp}", $f_exp);
+            $sheet->setCellValue("{$col}{$r_all}", "={$col}{$r_cap}+{$col}{$r_exp}");
+        }
+
+        // --- 核心勾稽逻辑修正 ---
+        foreach ([$r_cap, $r_exp, $r_all] as $row) {
+            $sheet->setCellValue("K{$row}", "=SUM(F{$row}:J{$row})"); // 6列
+            $sheet->setCellValue("O{$row}", "=N{$row}*0.8");           // 8.2列
+
+            // 修正:7.2列 (规则六)
+            if ($row == $r_all) {
+                $sheet->setCellValue("M{$row}", "=MIN(L{$row}, K{$row}*0.1/0.9)");
+            } else {
+                $sheet->setCellValue("M{$row}", "=IF(L{$r_all}>0, (M{$r_all}/L{$r_all})*L{$row}, 0)");
+            }
+
+            // 修正:8.4列 (规则八:金额合计行计算公式)
+            if ($row == $r_all) {
+                // 1. 金额合计 = MIN((6列+7.2列+8.2列)*2/3, 8.3列*80%)
+                $sheet->setCellValue("Q{$row}", "=MIN((K{$row}+M{$row}+O{$row})*2/3, P{$row}*0.8)");
+            } else {
+                // 分摊逻辑
+                $sheet->setCellValue("Q{$row}", "=IF(P{$r_all}>0, (Q{$r_all}/P{$r_all})*P{$row}, 0)");
+            }
+
+            // 修正:E列 (规则四:E = 6+7.2+8.2+8.4)
+            $sheet->setCellValue("E{$row}", "=K{$row}+M{$row}+O{$row}+Q{$row}");
+        }
+    }
+}