errorThrow('请上传表格文件'); } $path = is_string($file) ? $file : $file->getRealPath(); $raw = file_get_contents($path); if ($raw === false) { UtilsService::getInstance()->errorThrow('文件读取失败'); } if (str_starts_with($raw, "\xEF\xBB\xBF")) { $raw = substr($raw, 3); } $fp = fopen('php://temp', 'r+'); fwrite($fp, $raw); rewind($fp); $header = fgetcsv($fp); if (empty($header)) { fclose($fp); UtilsService::getInstance()->errorThrow('表格没有表头'); } $header = array_map(static fn ($v) => trim((string) $v), $header); $rows = []; while (($cols = fgetcsv($fp)) !== false) { if ($this->rowEmpty($cols)) { continue; } $item = []; foreach ($header as $i => $key) { $item[$key] = isset($cols[$i]) ? trim((string) $cols[$i]) : ''; } $rows[] = $item; } fclose($fp); return $rows; } /** * 清单报价:后台用 ExcelJS 画 xlsx,小程序继续用 text 分享 */ public function quoteFromList(array $info): array { $rows = []; $lines = []; $name = (string) ($info['name'] ?? $info['list_no'] ?? '报价'); $lines[] = $name; foreach ($info['items'] ?? [] as $item) { $title = (string) ($item['title'] ?? $item['catalogue']['title'] ?? $item['product']['title'] ?? ''); $spec = (string) ($item['specification'] ?? $item['price_sheet']['specification'] ?? ''); $qty = (int) ($item['quantity'] ?? 1); $unit = (string) ($item['unit_price_text'] ?? ''); $total = (string) ($item['total_price_text'] ?? ''); $rows[] = [ 'title' => $title, 'specification' => $spec, 'quantity' => $qty, 'unit_price' => $unit, 'total_price' => $total, ]; $lines[] = trim($title . ' ' . $spec . ' x' . $qty . ($unit !== '' ? ' ¥' . $unit : '')); } $payable = (string) ($info['payable_amount_text'] ?? $info['total_amount_text'] ?? ''); if ($payable !== '') { $lines[] = '合计 ¥' . $payable; } $no = (string) ($info['list_no'] ?? 'quote'); return [ 'filename' => 'quote-' . $no . '.xlsx', 'title' => $name, 'sheet' => '报价', 'subtitle' => '清单号 ' . $no, 'columns' => [ ['key' => 'title', 'header' => '商品', 'width' => 28], ['key' => 'specification', 'header' => '规格', 'width' => 22], ['key' => 'quantity', 'header' => '数量', 'width' => 10, 'align' => 'center'], ['key' => 'unit_price', 'header' => '单价', 'width' => 14, 'align' => 'right'], ['key' => 'total_price', 'header' => '小计', 'width' => 14, 'align' => 'right'], ], 'rows' => $rows, 'summary' => [ 'title' => '合计', 'total_price' => $payable === '' ? '' : ('¥' . $payable), ], 'text' => implode("\n", $lines), ]; } private function rowEmpty(array $cols): bool { foreach ($cols as $col) { if (trim((string) $col) !== '') { return false; } } return true; } }