|
@@ -11,38 +11,29 @@ use Maatwebsite\Excel\Events\AfterSheet;
|
|
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
|
|
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
|
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
|
|
use PhpOffice\PhpSpreadsheet\Style\Border;
|
|
use PhpOffice\PhpSpreadsheet\Style\Border;
|
|
|
|
|
+use PhpOffice\PhpSpreadsheet\Style\Fill;
|
|
|
|
|
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
|
|
|
|
|
|
|
class ResearchExpenseSheetExport implements FromCollection, WithEvents, WithStyles, WithCustomStartCell, WithTitle
|
|
class ResearchExpenseSheetExport implements FromCollection, WithEvents, WithStyles, WithCustomStartCell, WithTitle
|
|
|
{
|
|
{
|
|
|
- protected $sheetTitle; // Sheet 标签名,如 "2025-RD01"
|
|
|
|
|
|
|
+ protected $sheetTitle;
|
|
|
protected $data;
|
|
protected $data;
|
|
|
protected $projectInfo;
|
|
protected $projectInfo;
|
|
|
protected $year;
|
|
protected $year;
|
|
|
|
|
+ protected $dynamicHeaders;
|
|
|
|
|
|
|
|
- public function __construct(string $sheetTitle, array $data, array $projectInfo = [])
|
|
|
|
|
|
|
+ public function __construct(string $sheetTitle, array $data, array $projectInfo = [], array $dynamicHeaders = [], string $year)
|
|
|
{
|
|
{
|
|
|
$this->sheetTitle = $sheetTitle;
|
|
$this->sheetTitle = $sheetTitle;
|
|
|
$this->data = $data;
|
|
$this->data = $data;
|
|
|
$this->projectInfo = $projectInfo;
|
|
$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 开始写入
|
|
|
|
|
|
|
+ $this->year = $year;
|
|
|
|
|
+ $this->dynamicHeaders = $dynamicHeaders;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public function collection()
|
|
|
|
|
- {
|
|
|
|
|
- return collect($this->data);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ public function title(): string { return $this->sheetTitle; }
|
|
|
|
|
+ public function startCell(): string { return 'A7'; }
|
|
|
|
|
+ public function collection() { return collect($this->data); }
|
|
|
|
|
|
|
|
public function registerEvents(): array
|
|
public function registerEvents(): array
|
|
|
{
|
|
{
|
|
@@ -50,31 +41,18 @@ class ResearchExpenseSheetExport implements FromCollection, WithEvents, WithStyl
|
|
|
AfterSheet::class => function(AfterSheet $event) {
|
|
AfterSheet::class => function(AfterSheet $event) {
|
|
|
$sheet = $event->sheet->getDelegate();
|
|
$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");
|
|
|
|
|
|
|
+ // --- 1. 计算列信息 ---
|
|
|
|
|
+ $baseColumnCount = 8; // A-H (日期 到 折旧)
|
|
|
|
|
+ $dynamicCount = count($this->dynamicHeaders);
|
|
|
|
|
+ $totalColumnCount = $baseColumnCount + $dynamicCount + 2; // 加上委托研发2列
|
|
|
|
|
+ $highestColumn = Coordinate::stringFromColumnIndex($totalColumnCount);
|
|
|
|
|
+ $highestRow = $sheet->getHighestRow(); // 获取最后一行(合计行)
|
|
|
|
|
+
|
|
|
|
|
+ // --- 2. 样式初始化:防止灰色块和默认填充 ---
|
|
|
|
|
+ $sheet->getStyle("A1:{$highestColumn}{$highestRow}")->getFill()->setFillType(Fill::FILL_NONE);
|
|
|
|
|
+
|
|
|
|
|
+ // --- 3. 大标题 (A2) ---
|
|
|
|
|
+ $sheet->mergeCells("A2:{$highestColumn}2");
|
|
|
$sheet->setCellValue('A2', "{$this->year}年研发支出辅助账");
|
|
$sheet->setCellValue('A2', "{$this->year}年研发支出辅助账");
|
|
|
$sheet->getStyle('A2')->applyFromArray([
|
|
$sheet->getStyle('A2')->applyFromArray([
|
|
|
'font' => ['size' => 16],
|
|
'font' => ['size' => 16],
|
|
@@ -84,29 +62,17 @@ class ResearchExpenseSheetExport implements FromCollection, WithEvents, WithStyl
|
|
|
],
|
|
],
|
|
|
]);
|
|
]);
|
|
|
|
|
|
|
|
- // --- 4. 第三行:项目信息 (精准合并/无边框) ---
|
|
|
|
|
- // 项目编号:标签 ABC,值 DE
|
|
|
|
|
|
|
+ // --- 4. 项目信息行 (A3) ---
|
|
|
$sheet->mergeCells("A3:C3");
|
|
$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 行) ---
|
|
|
|
|
|
|
+ $sheet->setCellValue('A3', '项目编号:' . ($this->projectInfo['code'] ?? ''));
|
|
|
|
|
+ $sheet->mergeCells("D3:G3");
|
|
|
|
|
+ $sheet->setCellValue('D3', '项目名称:' . ($this->projectInfo['name'] ?? ''));
|
|
|
|
|
+
|
|
|
|
|
+ // 单位放在最右边一列
|
|
|
|
|
+ $sheet->setCellValue($highestColumn . '3', '单位:元');
|
|
|
|
|
+ $sheet->getStyle($highestColumn . '3')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
|
|
|
|
|
+
|
|
|
|
|
+ // --- 5. 复杂表头绘制 (4-6行) ---
|
|
|
// A-D 凭证信息
|
|
// A-D 凭证信息
|
|
|
$sheet->mergeCells("A4:D5");
|
|
$sheet->mergeCells("A4:D5");
|
|
|
$sheet->setCellValue('A4', '凭证信息');
|
|
$sheet->setCellValue('A4', '凭证信息');
|
|
@@ -115,34 +81,78 @@ class ResearchExpenseSheetExport implements FromCollection, WithEvents, WithStyl
|
|
|
$sheet->setCellValue('C6', '号数');
|
|
$sheet->setCellValue('C6', '号数');
|
|
|
$sheet->setCellValue('D6', '摘要');
|
|
$sheet->setCellValue('D6', '摘要');
|
|
|
|
|
|
|
|
- // E-F 金额列 (垂直合并)
|
|
|
|
|
|
|
+ // E-F 金额固定列 (垂直合并)
|
|
|
$sheet->mergeCells("E4:E6");
|
|
$sheet->mergeCells("E4:E6");
|
|
|
$sheet->setCellValue('E4', "会计凭证记载\n金额");
|
|
$sheet->setCellValue('E4', "会计凭证记载\n金额");
|
|
|
$sheet->mergeCells("F4:F6");
|
|
$sheet->mergeCells("F4:F6");
|
|
|
$sheet->setCellValue('F4', "税法规定的归\n集金额");
|
|
$sheet->setCellValue('F4', "税法规定的归\n集金额");
|
|
|
|
|
|
|
|
- // G-N 费用明细总标题
|
|
|
|
|
- $sheet->mergeCells("G4:N4");
|
|
|
|
|
|
|
+ // G-末尾: 费用明细总标题
|
|
|
|
|
+ $sheet->mergeCells("G4:{$highestColumn}4");
|
|
|
$sheet->setCellValue('G4', '费用明细(税法规定)');
|
|
$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);
|
|
|
|
|
|
|
+ // 固定明细项:人员人工(G), 折旧(H)
|
|
|
|
|
+ $sheet->mergeCells("G5:G6"); $sheet->setCellValue("G5", '人员人工费用');
|
|
|
|
|
+ $sheet->mergeCells("H5:H6"); $sheet->setCellValue("H5", '折旧费用');
|
|
|
|
|
+
|
|
|
|
|
+ // 动态明细项 (从 I/第9列开始)
|
|
|
|
|
+ $currentColIdx = 9;
|
|
|
|
|
+ foreach ($this->dynamicHeaders as $headerText) {
|
|
|
|
|
+ $colLetter = Coordinate::stringFromColumnIndex($currentColIdx);
|
|
|
|
|
+ $sheet->mergeCells("{$colLetter}5:{$colLetter}6");
|
|
|
|
|
+ $sheet->setCellValue("{$colLetter}5", $headerText);
|
|
|
|
|
+ $currentColIdx++;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 委托研发费用
|
|
|
|
|
- $sheet->mergeCells("M5:N5");
|
|
|
|
|
- $sheet->setCellValue('M5', '委托研发费用');
|
|
|
|
|
- $sheet->setCellValue('M6', "委托境内机构或个人进行研\n发活动所发生的费用");
|
|
|
|
|
- $sheet->setCellValue('N6', "委托境外机构进行研发活动\n所发生的费用");
|
|
|
|
|
|
|
+ // 委托研发 (最后两列)
|
|
|
|
|
+ $mCol = Coordinate::stringFromColumnIndex($currentColIdx);
|
|
|
|
|
+ $nCol = Coordinate::stringFromColumnIndex($currentColIdx + 1);
|
|
|
|
|
+ $sheet->mergeCells("{$mCol}5:{$nCol}5");
|
|
|
|
|
+ $sheet->setCellValue("{$mCol}5", '委托研发费用');
|
|
|
|
|
+ $sheet->setCellValue("{$mCol}6", "委托境内研发");
|
|
|
|
|
+ $sheet->setCellValue("{$nCol}6", "委托境外研发");
|
|
|
|
|
+
|
|
|
|
|
+ // --- 6. 强制刷新表头样式 (边框和居中) ---
|
|
|
|
|
+ $headerRange = "A4:{$highestColumn}6";
|
|
|
|
|
+ $sheet->getStyle($headerRange)->applyFromArray([
|
|
|
|
|
+ 'borders' => [
|
|
|
|
|
+ 'allBorders' => ['borderStyle' => Border::BORDER_THIN],
|
|
|
|
|
+ ],
|
|
|
|
|
+ 'alignment' => [
|
|
|
|
|
+ 'wrapText' => true,
|
|
|
|
|
+ 'vertical' => Alignment::VERTICAL_CENTER,
|
|
|
|
|
+ 'horizontal' => Alignment::HORIZONTAL_CENTER,
|
|
|
|
|
+ ],
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ // --- 7. 合计行特殊处理 (合并 A-D 列) ---
|
|
|
|
|
+ // 注意:由于数据从 A7 开始,且我们在 Controller 塞入了合计行,
|
|
|
|
|
+ // 所以 $highestRow 对应的就是那一行。
|
|
|
|
|
+ $sheet->mergeCells("A{$highestRow}:D{$highestRow}");
|
|
|
|
|
+ // 确保合计单元格样式加粗且有背景色
|
|
|
|
|
+ $footerRange = "A{$highestRow}:{$highestColumn}{$highestRow}";
|
|
|
|
|
+ $sheet->getStyle($footerRange)->applyFromArray([
|
|
|
|
|
+ 'fill' => [
|
|
|
|
|
+ 'fillType' => Fill::FILL_SOLID,
|
|
|
|
|
+ 'startColor' => ['argb' => 'FFF2F2F2'], // 灰色背景区分
|
|
|
|
|
+ ],
|
|
|
|
|
+ 'borders' => [
|
|
|
|
|
+ 'allBorders' => ['borderStyle' => Border::BORDER_THIN],
|
|
|
|
|
+ ],
|
|
|
|
|
+ ]);
|
|
|
|
|
+ $sheet->getStyle("A{$highestRow}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
|
|
|
|
+
|
|
|
|
|
+ // --- 8. 添加会计主管 (合计行下方空一行) ---
|
|
|
|
|
+ $supervisorRow = $highestRow + 1;
|
|
|
|
|
+ $sheet->setCellValue("A{$supervisorRow}", '会计主管:');
|
|
|
|
|
+ $sheet->getStyle("A{$supervisorRow}")->applyFromArray([
|
|
|
|
|
+ 'font' => ['size' => 10],
|
|
|
|
|
+ 'alignment' => ['horizontal' => Alignment::HORIZONTAL_LEFT]
|
|
|
|
|
+ ]);
|
|
|
|
|
|
|
|
- // --- 6. 开启换行 ---
|
|
|
|
|
- $sheet->getStyle('A4:N6')->getAlignment()->setWrapText(true);
|
|
|
|
|
|
|
+ // 设置行高
|
|
|
|
|
+ $sheet->getRowDimension('2')->setRowHeight(35);
|
|
|
|
|
+ $sheet->getRowDimension('6')->setRowHeight(45);
|
|
|
},
|
|
},
|
|
|
];
|
|
];
|
|
|
}
|
|
}
|
|
@@ -150,9 +160,12 @@ class ResearchExpenseSheetExport implements FromCollection, WithEvents, WithStyl
|
|
|
public function styles(Worksheet $sheet)
|
|
public function styles(Worksheet $sheet)
|
|
|
{
|
|
{
|
|
|
$highestRow = $sheet->getHighestRow();
|
|
$highestRow = $sheet->getHighestRow();
|
|
|
|
|
+ // 样式中也需要重新动态获取列,否则 styles 的作用范围会跟不上
|
|
|
|
|
+ $totalCol = 8 + count($this->dynamicHeaders) + 2;
|
|
|
|
|
+ $highestCol = Coordinate::stringFromColumnIndex($totalCol);
|
|
|
|
|
|
|
|
- // 全局基础样式:从第 4 行开始加边框 (跳过第 3 行项目信息)
|
|
|
|
|
- $sheet->getStyle("A4:N{$highestRow}")->applyFromArray([
|
|
|
|
|
|
|
+ // 数据区域边框 (从第7行开始)
|
|
|
|
|
+ $sheet->getStyle("A7:{$highestCol}{$highestRow}")->applyFromArray([
|
|
|
'borders' => [
|
|
'borders' => [
|
|
|
'allBorders' => ['borderStyle' => Border::BORDER_THIN],
|
|
'allBorders' => ['borderStyle' => Border::BORDER_THIN],
|
|
|
],
|
|
],
|