rows = $rows; $this->columns = []; $baseFields = is_array($baseFields) ? $baseFields : []; foreach ($rows as $row) if (!empty($row['replaces_sign_id']) || !empty($row['replaced_by_sign_id'])) { $baseFields += ['company_name_snapshot' => '报名公司', 'replacement_text' => '报名更换记录', 'giveup_reason' => '放弃原因', 'replaced_at' => '更换时间']; break; } foreach (['id' => '报名ID'] + $baseFields as $key => $name) $this->columns[] = ['base' => $key, 'name' => $name]; $fields = []; foreach (array_merge($current, ...array_map(fn ($row) => is_array($row['data'] ?? null) ? $row['data'] : [], $rows)) as $field) { if (!is_array($field) || !isset($field['field'])) continue; $field = CourseFormSchema::field($field); // A renamed historical field keeps its own title/column and can never shift another answer. $key = $field['field']."\0".($field['name'] ?? $field['field']); if (!isset($fields[$key])) $fields[$key] = $field; elseif (!empty($field['option_texts']) || !empty($field['other_text'])) $fields[$key]['has_notes'] = true; } foreach ($fields as $field) { $this->columns[] = ['field' => $field['field'], 'title' => $field['name'] ?? $field['field'], 'name' => $field['name'] ?? $field['field']]; if (!empty($field['config']['option_notes']) || !empty($field['config']['other_key']) || !empty($field['option_texts']) || !empty($field['other_text']) || !empty($field['has_notes'])) { $this->columns[] = ['field' => $field['field'], 'title' => $field['name'] ?? $field['field'], 'notes' => true, 'name' => ($field['name'] ?? $field['field']).'-补充说明']; } } $this->sheet = [array_column($this->columns, 'name')]; foreach ($rows as $row) { $answers = CourseFormSchema::display(is_array($row['data'] ?? null) ? $row['data'] : []); $cells = []; foreach ($this->columns as $column) { if (isset($column['base'])) { $value = data_get($row, $column['base']); $cells[] = is_scalar($value) ? (string) $value : ''; continue; } $matches = array_values(array_filter($answers, fn ($answer) => ($answer['field'] ?? null) === $column['field'] && ($answer['name'] ?? $answer['field']) === $column['title'])); $answer = $matches[0] ?? []; $value = !empty($answer['hidden']) ? '' : ($answer[!empty($column['notes']) ? 'supplement_text' : 'display_value'] ?? ''); if (empty($column['notes']) && empty($answer['hidden']) && ($answer['edit_input'] ?? '') === 'checkbox' && isset($answer['selected_keys'])) { $selected = array_map('strval', $answer['selected_keys']); $value = implode("\n", array_column(array_filter($answer['select_item'], fn ($option) => in_array((string) $option['key'], $selected, true)), 'value')); } $cells[] = $value; } $this->sheet[] = $cells; } } public function array(): array { return $this->sheet; } public function registerEvents(): array { return [AfterSheet::class => function (AfterSheet $event) { $sheet = $event->sheet->getDelegate(); $last = Coordinate::stringFromColumnIndex(max(1, count($this->columns))); $range = 'A1:'.$last.count($this->sheet); $sheet->getStyle($range)->getAlignment()->setWrapText(true)->setVertical(Alignment::VERTICAL_TOP); $sheet->getStyle('A1:'.$last.'1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER)->setVertical(Alignment::VERTICAL_CENTER); $sheet->getStyle('A1:'.$last.'1')->getFont()->setBold(true); $sheet->getRowDimension(1)->setRowHeight(42); $sheet->freezePane('A2'); $sheet->setAutoFilter('A1:'.$last.count($this->sheet)); foreach ($this->columns as $index => $column) $sheet->getColumnDimension(Coordinate::stringFromColumnIndex($index + 1))->setWidth(isset($column['base']) ? 20 : (!empty($column['notes']) ? 42 : 30)); foreach ($this->sheet as $index => $row) { foreach ($row as $col => $value) $sheet->setCellValueExplicit(Coordinate::stringFromColumnIndex($col + 1).($index + 1), (string) $value, DataType::TYPE_STRING); if ($index > 0) { $lines = 1; foreach ($row as $value) $lines = max($lines, count(explode("\n", (string) $value)), (int) ceil(mb_strwidth((string) $value) / 28)); $sheet->getRowDimension($index + 1)->setRowHeight(min(300, max(32, 18 * $lines + 10))); } } }]; } }