You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
90 lines
5.4 KiB
90 lines
5.4 KiB
<?php
|
|
|
|
namespace App\Exports;
|
|
|
|
use App\Services\Course\CourseFormSchema;
|
|
use Maatwebsite\Excel\Concerns\FromArray;
|
|
use Maatwebsite\Excel\Concerns\WithEvents;
|
|
use Maatwebsite\Excel\Events\AfterSheet;
|
|
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
|
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
|
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
|
|
|
/** Registration-only export: union schemas instead of deriving headers from the first answer. */
|
|
class CourseRegistrationExport implements FromArray, WithEvents
|
|
{
|
|
private array $rows;
|
|
private array $columns;
|
|
private array $sheet;
|
|
|
|
public function __construct(array $rows, $baseFields, array $current = [])
|
|
{
|
|
$this->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)));
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
}
|