$fields */ public function download(string $title, array $fields, bool $optionalRecordKey = false): StreamedResponse { return response()->streamDownload(function () use ($title, $fields, $optionalRecordKey): void { $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); $sheet->setTitle($this->sheetTitle($title)); $headers = array_merge(['记录标识'], array_column($fields, 'label')); $sheet->fromArray($headers, null, 'A1'); $lastColumn = Coordinate::stringFromColumnIndex(count($headers)); $headerRange = 'A1:' . $lastColumn . '1'; $sheet->getStyle($headerRange)->applyFromArray([ 'font' => ['bold' => true, 'color' => ['rgb' => 'FFFFFF']], 'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '856632']], 'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER], 'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN, 'color' => ['rgb' => 'D8D2C7']]], ]); $sheet->getRowDimension(1)->setRowHeight(24); $sheet->freezePane('A2'); $sheet->setAutoFilter($headerRange); $sheet->getColumnDimension('A')->setWidth(38); $columnIndex = 2; foreach ($fields as $definition) { $column = Coordinate::stringFromColumnIndex($columnIndex); $width = max(14, min(30, mb_strlen($definition['label']) * 2 + 4)); $sheet->getColumnDimension($column)->setWidth($width); if ($definition['type'] === 'number') { $sheet->getStyle($column . '2:' . $column . '10001')->getNumberFormat()->setFormatCode('0.0000'); } else { $sheet->getStyle($column . '2:' . $column . '10001')->getNumberFormat()->setFormatCode('@'); } $columnIndex++; } $sheet->getStyle('A2:A10001')->getNumberFormat()->setFormatCode('@'); $instructions = $spreadsheet->createSheet(); $instructions->setTitle('填写说明'); $instructions->fromArray([ ['项目', '说明'], ['记录标识', $optionalRecordKey ? '选填。留空表示新增一行;需要修改已导入的行时,请先导出、修改后再导入。' : '必填。请从本页面导出的文件中复制,不能修改。'], ['日期', '请填写 YYYY-MM-DD,例如 2026-09-17。'], ['月份', '请填写 YYYY-MM,例如 2026-09。'], ['数字', '最多支持 4 位小数;金额可直接填写数字。'], ['空白单元格', '导入后将清空该字段,请确认后再导入。'], ], null, 'A1'); $instructions->getStyle('A1:B1')->applyFromArray([ 'font' => ['bold' => true, 'color' => ['rgb' => 'FFFFFF']], 'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '856632']], ]); $instructions->getColumnDimension('A')->setWidth(18); $instructions->getColumnDimension('B')->setWidth(58); $instructions->getStyle('A1:B6')->getAlignment()->setVertical(Alignment::VERTICAL_CENTER)->setWrapText(true); foreach (range(1, 6) as $row) { $instructions->getRowDimension($row)->setRowHeight($row === 1 ? 24 : 32); } $spreadsheet->setActiveSheetIndex(0); (new Xlsx($spreadsheet))->save('php://output'); $spreadsheet->disconnectWorksheets(); }, $title . '_导入模板_' . now()->format('Ymd') . '.xlsx', [ 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'Cache-Control' => 'private, no-store', ]); } private function sheetTitle(string $title): string { $title = str_replace(['\\', '/', '?', '*', '[', ']', ':'], '', $title) ?: '导入模板'; return mb_substr($title, 0, 31); } }