makeSpreadsheet(); return response()->streamDownload(function () use ($spreadsheet): void { $writer = new Xlsx($spreadsheet); $writer->setPreCalculateFormulas(false); $writer->save('php://output'); $spreadsheet->disconnectWorksheets(); }, '财务台账-账户总表录入模板.xlsx', [ 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'Cache-Control' => 'no-store, no-cache, must-revalidate', ]); } private function makeSpreadsheet(): Spreadsheet { $spreadsheet = new Spreadsheet(); $spreadsheet->getProperties() ->setTitle('财务台账账户总表录入模板') ->setSubject('账户总表重新录入'); $sheet = $spreadsheet->getActiveSheet(); $sheet->setTitle('账户总表'); $lastColumn = Coordinate::stringFromColumnIndex(count(self::HEADERS)); $lastRow = self::MAX_ROWS + 1; foreach (self::HEADERS as $index => $header) { $column = Coordinate::stringFromColumnIndex($index + 1); $sheet->setCellValue("{$column}1", $header); $sheet->getColumnDimension($column)->setWidth($this->width($index)); } $sheet->freezePane('A2'); $sheet->setAutoFilter("A1:{$lastColumn}1"); $sheet->getRowDimension(1)->setRowHeight(28); $sheet->getStyle("A1:{$lastColumn}1")->getAlignment() ->setHorizontal(Alignment::HORIZONTAL_CENTER) ->setVertical(Alignment::VERTICAL_CENTER) ->setWrapText(true); $sheet->getStyle("A1:{$lastColumn}1")->getFont()->setBold(true)->getColor()->setARGB('FFFFFFFF'); $sheet->getStyle("A1:{$lastColumn}1")->getFill() ->setFillType(Fill::FILL_SOLID) ->getStartColor()->setARGB('FF856632'); $sheet->getStyle("A1:{$lastColumn}{$lastRow}")->getBorders()->getAllBorders() ->setBorderStyle(Border::BORDER_THIN)->getColor()->setARGB('FFD8D2C7'); $sheet->getStyle("A2:O{$lastRow}")->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_TEXT); $sheet->getStyle("P2:R{$lastRow}")->getNumberFormat()->setFormatCode('#,##0.00'); $sheet->getStyle("S2:S{$lastRow}")->getNumberFormat()->setFormatCode('0.00'); $sheet->getStyle("T2:Z{$lastRow}")->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_TEXT); $options = $spreadsheet->createSheet(); $options->setTitle('选项'); $options->setCellValue('A1', '账户状态'); $options->setCellValue('B1', '账户类型'); $options->setCellValue('C1', '账户是否保留'); $statuses = array_values(FinanceLedgerAccountStatus::OPTIONS); $types = $this->accountTypes(); $keepOptions = FinanceLedgerAccountShouldKeep::OPTIONS; foreach ($statuses as $index => $status) { $options->setCellValue('A' . ($index + 2), $status); } foreach ($types as $index => $type) { $options->setCellValue('B' . ($index + 2), $type); } foreach ($keepOptions as $index => $option) { $options->setCellValue('C' . ($index + 2), $option); } $options->setSheetState(Worksheet::SHEETSTATE_HIDDEN); $this->applyListValidation($sheet, 'D', '\'选项\'!$A$2:$A$' . (count($statuses) + 1), '请选择账户状态'); if ($types !== []) { $this->applyListValidation($sheet, 'C', '\'选项\'!$B$2:$B$' . (count($types) + 1), '请选择账户类型'); } $this->applyListValidation($sheet, 'J', '\'选项\'!$C$2:$C$' . (count($keepOptions) + 1), '请选择账户是否保留'); $notes = $spreadsheet->createSheet(); $notes->setTitle('填写说明'); $notes->fromArray([ ['项目', '说明'], ['用途', '账户总表重新录入模板,不包含当前账户数据。'], ['必填', '公司名称、账户简称。'], ['账户状态', implode(' / ', $statuses)], ['账户类型', $types !== [] ? implode(' / ', $types) : '留空或按实际填写'], ['账户是否保留', implode(' / ', $keepOptions)], ['金额', '可用金额、不可用金额、每日可出按数字填写;限额列保留原有组合写法。'], ['提交前', '请核对账户数、余额合计和期初合计,再导入系统。'], ], null, 'A1'); $notes->getColumnDimension('A')->setWidth(16); $notes->getColumnDimension('B')->setWidth(70); $notes->getStyle('A1:B1')->getFont()->setBold(true)->getColor()->setARGB('FFFFFFFF'); $notes->getStyle('A1:B1')->getFill() ->setFillType(Fill::FILL_SOLID) ->getStartColor()->setARGB('FF856632'); $notes->getStyle('A1:B8')->getAlignment()->setVertical(Alignment::VERTICAL_TOP)->setWrapText(true); $notes->getStyle('A1:B8')->getBorders()->getAllBorders() ->setBorderStyle(Border::BORDER_THIN)->getColor()->setARGB('FFD8D2C7'); $notes->getRowDimension(1)->setRowHeight(26); $spreadsheet->setActiveSheetIndex(0); return $spreadsheet; } private function applyListValidation( Worksheet $sheet, string $column, string $formula, string $prompt, ): void { $validation = $sheet->getCell("{$column}2")->getDataValidation(); $validation->setType(DataValidation::TYPE_LIST); $validation->setErrorStyle(DataValidation::STYLE_STOP); $validation->setAllowBlank(true); $validation->setShowInputMessage(true); $validation->setPromptTitle('选择' . $prompt); $validation->setPrompt($prompt); $validation->setShowErrorMessage(true); $validation->setErrorTitle('选项无效'); $validation->setError($prompt); $validation->setFormula1($formula); $validation->setShowDropDown(true); for ($row = 2; $row <= self::MAX_ROWS + 1; $row++) { $sheet->getCell("{$column}{$row}")->setDataValidation(clone $validation); } } private function accountTypes(): array { $connection = DB::connection((string) config('upstream_mirrors.connection', 'business_mirror')); $base = $connection->table('fin_accounts') ->whereNotNull('account_type')->where('account_type', '<>', '') ->distinct()->pluck('account_type')->all(); $overrides = $connection->table('fin_account_type_overrides') ->whereNotNull('account_type')->where('account_type', '<>', '') ->distinct()->pluck('account_type')->all(); return FinanceLedgerAccountType::options($base, $overrides); } private function width(int $index): int { return match ($index) { 0, 6 => 30, 1, 5, 7, 24 => 22, 2, 3, 4, 8, 9, 18 => 13, 15, 16, 17 => 16, default => 18, }; } }