Merge branch 'develop'
# Conflicts: # config/route.php
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
namespace app\common\utils\excel;
|
||||
|
||||
use support\Db;
|
||||
|
||||
/**
|
||||
* ExcelImportTrait
|
||||
*
|
||||
* 把“读取 Sheet → 映射字段 → 规范化数据 → 打时间戳 → 批量入库”
|
||||
* 这条通用导入链路抽成 Trait,便于在多个 Service 里复用。
|
||||
*
|
||||
* 依赖:
|
||||
* - ExcelReader:提供 readSheet / readSheetFromUrl / remap / toDecimal / toBool / toDate
|
||||
* - 你的 OssService:通过 generatePrivateUrl($objectKey) 生成 OSS 私有签名 URL
|
||||
*/
|
||||
trait ExcelImportTrait
|
||||
{
|
||||
/**
|
||||
* 读取某个 Sheet 的“行数组”
|
||||
* - $file 可为本地路径或 URL(由 $isUrl 控制)
|
||||
* - headerRow:表头所在行(Excel 行号,从 1 开始)
|
||||
* - startRow:数据起始行(不传则为 headerRow+1)
|
||||
*/
|
||||
protected function readSheetRows(string $file, string $sheet, bool $isUrl, int $headerRow = 1, ?int $startRow = null): array
|
||||
{
|
||||
$ret = $isUrl
|
||||
? ExcelReader::readSheetFromUrl($file, $sheet, $headerRow, $startRow)
|
||||
: ExcelReader::readSheet($file, $sheet, $headerRow, $startRow);
|
||||
return $ret['rows'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取某个 Sheet(传入 OSS 对象键),内部会用你的 OssService 生成签名 URL 再读取
|
||||
*/
|
||||
protected function readSheetRowsFromOssKey(string $objectKey, string $sheet, int $headerRow = 1, ?int $startRow = null): array
|
||||
{
|
||||
$signedUrl = $this->ossSignedUrl($objectKey);
|
||||
$ret = ExcelReader::readSheetFromUrl($signedUrl, $sheet, $headerRow, $startRow);
|
||||
return $ret['rows'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用你的 OssService 生成私有签名 URL
|
||||
* - 兼容你的现有写法:(new \app\common\service\OssService)->generatePrivateUrl($objectKey)
|
||||
*/
|
||||
protected function ossSignedUrl(string $objectKey): string
|
||||
{
|
||||
return (new \app\common\service\OssService())->generatePrivateUrl($objectKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一行做字段映射 + 基础类型规范化(decimal/bool/date)
|
||||
* - $headerMap:表头映射(源列名 => 目标字段名)
|
||||
* - $decimalFields/$boolFields/$dateFields:需要做类型规范化的目标字段名列表
|
||||
*/
|
||||
protected function mapAndNormalizeRow(
|
||||
array $row,
|
||||
array $headerMap,
|
||||
array $decimalFields = [],
|
||||
array $boolFields = [],
|
||||
array $dateFields = []
|
||||
): array {
|
||||
$rec = ExcelReader::remap($row, $headerMap);
|
||||
|
||||
foreach ($decimalFields as $f) {
|
||||
if (array_key_exists($f, $rec)) {
|
||||
$rec[$f] = ExcelReader::toDecimal($rec[$f]); // 建议已升级为“去货币符号/千分位/括号负数”的版本
|
||||
}
|
||||
}
|
||||
foreach ($boolFields as $f) {
|
||||
if (array_key_exists($f, $rec)) {
|
||||
$rec[$f] = ExcelReader::toBool($rec[$f]);
|
||||
}
|
||||
}
|
||||
foreach ($dateFields as $f) {
|
||||
if (array_key_exists($f, $rec)) {
|
||||
$rec[$f] = ExcelReader::toDate($rec[$f]);
|
||||
}
|
||||
}
|
||||
return $rec;
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于 VIN 的跳过规则:VIN 为空、或是“合计/总计”行
|
||||
* - $vinAliases:VIN 列的候选表头(如 ['车架号','VIN NO'])
|
||||
*/
|
||||
protected function shouldSkipByVinAliases(array $row, array $vinAliases = ['车架号', 'VIN NO']): bool
|
||||
{
|
||||
$vin = $this->pickFirstNonEmpty($row, $vinAliases);
|
||||
if ($vin === null) return true;
|
||||
return (bool)preg_match('/^(合计|总计)/u', $vin);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在多个候选键里取第一个非空字符串
|
||||
*/
|
||||
protected function pickFirstNonEmpty(array $row, array $keys): ?string
|
||||
{
|
||||
foreach ($keys as $k) {
|
||||
if (isset($row[$k])) {
|
||||
$v = trim((string)$row[$k]);
|
||||
if ($v !== '') return $v;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一补齐 created_at / updated_at 时间戳(int(11))
|
||||
*/
|
||||
protected function stamp(array &$rec): void
|
||||
{
|
||||
$now = time();
|
||||
$rec['created_at'] = $now;
|
||||
$rec['updated_at'] = $now;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量入库
|
||||
* - 返回 ['inserted'=>N, 'error'=>null|string]
|
||||
* - 发生异常时不抛出,返回 error 字符串,便于上层统一处理
|
||||
*/
|
||||
protected function batchInsert(string $table, array $rows): array
|
||||
{
|
||||
if (!$rows) return ['inserted' => 0, 'error' => null];
|
||||
try {
|
||||
Db::table($table)->insert($rows);
|
||||
return ['inserted' => count($rows), 'error' => null];
|
||||
} catch (\Throwable $e) {
|
||||
return ['inserted' => 0, 'error' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
namespace app\common\utils\excel;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date as ExcelDate;
|
||||
|
||||
class ExcelReader
|
||||
{
|
||||
/**
|
||||
* (可选)环境自检:避免因缺少扩展导致读取失败
|
||||
* - simplexml_load_string 存在性(ext-simplexml)
|
||||
* - ext-zip
|
||||
*/
|
||||
public static function assertRequirements(): void
|
||||
{
|
||||
if (!function_exists('simplexml_load_string')) {
|
||||
throw new \RuntimeException('缺少 PHP 扩展:ext-simplexml。请安装/启用后再试。');
|
||||
}
|
||||
if (!extension_loaded('zip')) {
|
||||
throw new \RuntimeException('缺少 PHP 扩展:ext-zip。请安装/启用后再试。');
|
||||
}
|
||||
// 可按需继续检查 ext-mbstring/ext-dom 等
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取指定 sheet,按某一行作为表头
|
||||
* 返回:['headers'=>[], 'rows'=>[]]
|
||||
* - $headerRow:从 1 开始计数(Excel 行号)
|
||||
* - $startRow:数据起始行(默认 headerRow+1)
|
||||
* - 注意:$filePath 必须是“本地文件路径”;若为 URL,请改用 readSheetFromUrl()
|
||||
*/
|
||||
public static function readSheet(string $filePath, string $sheetName, int $headerRow = 1, ?int $startRow = null): array
|
||||
{
|
||||
self::assertRequirements();
|
||||
|
||||
if (!is_file($filePath)) {
|
||||
throw new \RuntimeException("File not found: {$filePath}");
|
||||
}
|
||||
|
||||
$reader = IOFactory::createReaderForFile($filePath);
|
||||
$reader->setReadDataOnly(true);
|
||||
$spreadsheet = $reader->load($filePath);
|
||||
|
||||
$sheet = $spreadsheet->getSheetByName($sheetName);
|
||||
if (!$sheet) {
|
||||
$names = implode(' / ', $spreadsheet->getSheetNames());
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
throw new \RuntimeException("Sheet '{$sheetName}' not found. Available: {$names}");
|
||||
}
|
||||
|
||||
$highestRow = $sheet->getHighestDataRow();
|
||||
$highestCol = $sheet->getHighestDataColumn();
|
||||
$highestColIndex = Coordinate::columnIndexFromString($highestCol);
|
||||
|
||||
// 读取表头(去除可能的 UTF-8 BOM)
|
||||
$headers = [];
|
||||
for ($col = 1; $col <= $highestColIndex; $col++) {
|
||||
$coord = Coordinate::stringFromColumnIndex($col) . $headerRow;
|
||||
$cell = $sheet->getCell($coord);
|
||||
$raw = $cell ? $cell->getCalculatedValue() : null;
|
||||
$text = is_null($raw) ? '' : trim((string)$raw);
|
||||
// 去掉 BOM
|
||||
if ($col === 1) {
|
||||
$text = preg_replace('/^\xEF\xBB\xBF/u', '', $text);
|
||||
}
|
||||
$headers[$col] = $text === '' ? "COL_{$col}" : $text;
|
||||
}
|
||||
|
||||
// 读取数据
|
||||
$rows = [];
|
||||
$firstDataRow = $startRow ?? ($headerRow + 1);
|
||||
for ($row = $firstDataRow; $row <= $highestRow; $row++) {
|
||||
$record = [];
|
||||
$isEmpty = true;
|
||||
|
||||
for ($col = 1; $col <= $highestColIndex; $col++) {
|
||||
$coord = Coordinate::stringFromColumnIndex($col) . $row;
|
||||
$cell = $sheet->getCell($coord);
|
||||
$value = $cell ? $cell->getCalculatedValue() : null;
|
||||
|
||||
// 日期单元格转换(使用 PhpSpreadsheet 的日期检测)
|
||||
if ($cell && \PhpOffice\PhpSpreadsheet\Shared\Date::isDateTime($cell) && is_numeric($value)) {
|
||||
$dt = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($value);
|
||||
$value = $dt ? $dt->format('Y-m-d') : $value;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$value = trim($value);
|
||||
}
|
||||
if ($value !== null && $value !== '') {
|
||||
$isEmpty = false;
|
||||
}
|
||||
$record[$headers[$col]] = $value;
|
||||
}
|
||||
|
||||
if (!$isEmpty) {
|
||||
$rows[] = $record;
|
||||
}
|
||||
}
|
||||
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
return ['headers' => array_values($headers), 'rows' => $rows];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 URL 读取指定 sheet(内部先下载到临时文件)
|
||||
*/
|
||||
public static function readSheetFromUrl(string $url, string $sheetName, int $headerRow = 1, ?int $startRow = null): array
|
||||
{
|
||||
$tmp = self::downloadToTemp($url);
|
||||
try {
|
||||
return self::readSheet($tmp, $sheetName, $headerRow, $startRow);
|
||||
} finally {
|
||||
@unlink($tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按映射表将“源表头”=>“目标字段名”(snake_case)
|
||||
* $map 形如:['车架号' => 'vin', '实际采购价' => 'actual_purchase_price', ...]
|
||||
*/
|
||||
public static function remap(array $row, array $map): array
|
||||
{
|
||||
$out = [];
|
||||
foreach ($map as $from => $to) {
|
||||
$out[$to] = $row[$from] ?? null;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/** 将一批行做 remap */
|
||||
public static function remapRows(array $rows, array $map): array
|
||||
{
|
||||
$out = [];
|
||||
foreach ($rows as $row) {
|
||||
$out[] = self::remap($row, $map);
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将“金额字符串”解析成 float(适配多币种/多地区写法)
|
||||
* - 支持:¥ ¥ $ € £ …、AED/Dirham:dh / dhs / aed、阿语 د.إ
|
||||
* - 支持:中文/全角标点、阿拉伯小数/千分位、细空格、撇号千分位
|
||||
* - 支持:括号负数 (xxx) 与显式负号
|
||||
* - 返回 null 表示无法解析
|
||||
*/
|
||||
public static function toDecimal($v): ?float
|
||||
{
|
||||
if ($v === null || $v === '') return null;
|
||||
if (is_numeric($v)) return (float)$v;
|
||||
|
||||
$s = trim((string)$v);
|
||||
|
||||
// 1) 统一常见 unicode 标点(全角/阿拉伯)与空格
|
||||
$s = strtr($s, [
|
||||
',' => ',', '.' => '.',
|
||||
'٫' => '.', '٬' => ',',
|
||||
"\xC2\xA0" => ' ', // NBSP
|
||||
"\xE2\x80\xAF" => ' ', // NNBSP(窄不换行空格)
|
||||
]);
|
||||
|
||||
// 2) 括号负数,如 (dh1,234.56)
|
||||
$negative = false;
|
||||
if (preg_match('/^\s*\((.*)\)\s*$/u', $s, $m)) {
|
||||
$negative = true;
|
||||
$s = $m[1];
|
||||
}
|
||||
|
||||
// 3) 去币种/符号:AED/Dirham(dh/dhs/aed/د.إ)& 其它常见符号/字母
|
||||
$s = preg_replace('/\b(dhs?|aed)\b/i', '', $s); // dh / dhs / aed
|
||||
$s = str_replace(['د.إ', 'د', 'إ'], '', $s); // 常见阿语形式简化处理
|
||||
$s = preg_replace('/[A-Za-z¥¥$€£₽₺₩₹﷼]+/u', '', $s); // 其它字母/货币符号
|
||||
|
||||
// 4) 只保留 数字/点/逗号/减号/空格/撇号,然后去掉空格与撇号(千分位)
|
||||
$s = preg_replace('/[^\d\.,\-\s\']+/u', '', $s);
|
||||
$s = str_replace([' ', "'"], '', $s);
|
||||
|
||||
// 5) 统一小数分隔符(判断最后出现的分隔符)
|
||||
$lastComma = strrpos($s, ',');
|
||||
$lastDot = strrpos($s, '.');
|
||||
|
||||
if ($lastComma !== false && $lastDot !== false) {
|
||||
// 同时存在,最后出现者视为小数点
|
||||
if ($lastComma > $lastDot) {
|
||||
// 欧式 1.234,56
|
||||
$s = str_replace('.', '', $s); // 去千分点
|
||||
$s = substr_replace($s, '.', $lastComma, 1); // 最后一个逗号改为小数点
|
||||
$s = str_replace(',', '', $s); // 其余逗号清空
|
||||
} else {
|
||||
// 美式 1,234.56
|
||||
$s = str_replace(',', '', $s);
|
||||
}
|
||||
} elseif ($lastComma !== false) {
|
||||
// 只有逗号
|
||||
$digitsAfter = strlen($s) - $lastComma - 1;
|
||||
if (substr_count($s, ',') > 1) {
|
||||
// 多个逗号基本都是千分位
|
||||
$s = str_replace(',', '', $s);
|
||||
} else {
|
||||
// 一个逗号:若逗号后 1~2 位,视为小数点,否则千分位
|
||||
$s = ($digitsAfter >= 1 && $digitsAfter <= 2)
|
||||
? str_replace(',', '.', $s)
|
||||
: str_replace(',', '', $s);
|
||||
}
|
||||
} elseif ($lastDot !== false && substr_count($s, '.') > 1) {
|
||||
// 多个点:保留最后一个为小数点,其他去掉(当作千分位)
|
||||
$digitsAfter = strlen($s) - $lastDot - 1;
|
||||
if ($digitsAfter >= 1 && $digitsAfter <= 2) {
|
||||
$s = preg_replace('/\.(?=.*\.)/', '', $s); // 移除除最后一个外的点
|
||||
} else {
|
||||
$s = str_replace('.', '', $s);
|
||||
}
|
||||
}
|
||||
|
||||
// 6) 处理显式负号
|
||||
if (strpos($s, '-') !== false) {
|
||||
$negative = true;
|
||||
$s = str_replace('-', '', $s);
|
||||
}
|
||||
|
||||
// 7) 最终校验并转 float
|
||||
if ($s === '' || !preg_match('/^\d+(\.\d+)?$/', $s)) {
|
||||
return null; // 无法解析
|
||||
}
|
||||
|
||||
$num = (float)$s;
|
||||
return $negative ? -$num : $num;
|
||||
}
|
||||
|
||||
/** 规范化布尔:'是/否'、'Y/N'、'1/0'、'true/false' */
|
||||
public static function toBool($v): ?int
|
||||
{
|
||||
if ($v === null || $v === '') return null;
|
||||
$s = mb_strtolower(trim((string)$v));
|
||||
if (in_array($s, ['是','y','yes','true','1','√','✅'], true)) return 1;
|
||||
if (in_array($s, ['否','n','no','false','0','×','❌'], true)) return 0;
|
||||
return null;
|
||||
}
|
||||
|
||||
/** 规范化日期(readSheet 已对 Excel 日期做过处理,这里再兜底) */
|
||||
public static function toDate($v): ?string
|
||||
{
|
||||
if ($v === null || $v === '') return null;
|
||||
if (is_numeric($v)) {
|
||||
// Excel 序列号兜底
|
||||
$ts = ExcelDate::excelToTimestamp($v);
|
||||
return $ts ? date('Y-m-d', $ts) : null;
|
||||
}
|
||||
// 简单清洗
|
||||
$s = trim((string)$v);
|
||||
// 常见格式直接返回(如 2025-11-04 / 2025/11/04)
|
||||
if (preg_match('/^\d{4}[-\/]\d{1,2}[-\/]\d{1,2}$/', $s)) {
|
||||
return str_replace('/', '-', $s);
|
||||
}
|
||||
return $s; // 留给上层决定是否再严格校验
|
||||
}
|
||||
|
||||
/**
|
||||
* 把远程 URL 流式下载到临时文件;返回临时路径
|
||||
*/
|
||||
protected static function downloadToTemp(string $url, ?string $ext = null): string
|
||||
{
|
||||
$ext = $ext ? ('.' . ltrim($ext, '.')) : '';
|
||||
$tmp = sys_get_temp_dir() . '/excel_' . uniqid() . $ext;
|
||||
$fp = fopen($tmp, 'w');
|
||||
if (!$fp) {
|
||||
throw new \RuntimeException("Cannot open temp file: {$tmp}");
|
||||
}
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_FILE => $fp,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_TIMEOUT => 300,
|
||||
CURLOPT_CONNECTTIMEOUT => 15,
|
||||
CURLOPT_SSL_VERIFYPEER => true,
|
||||
CURLOPT_SSL_VERIFYHOST => 2,
|
||||
CURLOPT_USERAGENT => 'Webman-ExcelReader/1.0',
|
||||
]);
|
||||
$ok = curl_exec($ch);
|
||||
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$err = curl_error($ch);
|
||||
curl_close($ch);
|
||||
fclose($fp);
|
||||
if ($ok === false || $http >= 400) {
|
||||
@unlink($tmp);
|
||||
throw new \RuntimeException("Download failed: HTTP {$http} {$err}");
|
||||
}
|
||||
return $tmp;
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@
|
||||
<div class="container">
|
||||
<!-- 头部 -->
|
||||
<div class="header">
|
||||
<h1 class="title">合塑实业-汽车事业部-备货单(客户下定)</h1>
|
||||
<h1 class="title">合塑实业-汽车事业部-备货单<?php if ($data['is_final'] == 0) { echo '(客户下定)'; } else { echo '(终版)'; } ?></h1>
|
||||
<div>
|
||||
<span class="sub-title">备货单号: <?php echo htmlspecialchars($data['order_no'] ?? 'N/A'); ?></span>
|
||||
<span class="sub-title">业务员: <?php echo htmlspecialchars($data['salesperson_snapshot'] ?? ' - '); ?></span>
|
||||
@@ -126,7 +126,7 @@
|
||||
<th>车型</th>
|
||||
<th>数量</th>
|
||||
<th>颜色</th>
|
||||
<th colspan="7">费用明细</th>
|
||||
<th colspan="8">费用明细</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@@ -142,9 +142,12 @@
|
||||
|
||||
<!-- 车型 跨4行(不含备注行) -->
|
||||
<td rowspan="4">
|
||||
<?php echo htmlspecialchars($vehicle['model_snapshot'] ?? ' - '); ?>
|
||||
<?php echo htmlspecialchars($vehicle['model_snapshot'] ?? ' - '); ?><br/><br/>
|
||||
<?php if (!empty($vehicle['mileage']) && $vehicle['mileage'] > 0): ?>
|
||||
<span class="nowrap">(<?php echo htmlspecialchars($vehicle['mileage']); ?>km)</span>
|
||||
<span class="nowrap">里程:<?php echo htmlspecialchars($vehicle['mileage']); ?>km</span>
|
||||
<?php endif; ?><br/>
|
||||
<?php if ($vehicle['vin']): ?>
|
||||
<span class="nowrap">VIN:<?php echo htmlspecialchars($vehicle['vin']); ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
||||
@@ -160,6 +163,7 @@
|
||||
</td>
|
||||
|
||||
<!-- 费用明细(第1组)标题 -->
|
||||
<th>手续费</th>
|
||||
<th>改装费</th>
|
||||
<th>配件费</th>
|
||||
<th>收款价</th>
|
||||
@@ -171,6 +175,7 @@
|
||||
|
||||
<!-- 行2:费用明细(第1组)数据 -->
|
||||
<tr>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($vehicle['handling_fee']['currency_info'] ?? ' - '); ?></td>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($vehicle['retrofit_fee']['currency_info'] ?? ' - '); ?></td>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($vehicle['accessory_price']['currency_info'] ?? ' - '); ?></td>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($vehicle['unit_price']['currency_info'] ?? ' - '); ?></td>
|
||||
@@ -191,6 +196,7 @@
|
||||
|
||||
<!-- 行3:费用明细(第2组)标题 -->
|
||||
<tr>
|
||||
<th>加价</th>
|
||||
<th>报关价</th>
|
||||
<th>FOB运费</th>
|
||||
<th>车价</th>
|
||||
@@ -202,6 +208,7 @@
|
||||
|
||||
<!-- 行4:费用明细(第2组)数据 -->
|
||||
<tr>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($vehicle['increase_price']['currency_info'] ?? ' - '); ?></td>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($vehicle['clearance_price']['currency_info'] ?? ' - '); ?></td>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($vehicle['fob_transport_fee']['currency_info'] ?? ' - '); ?></td>
|
||||
<td class="nowrap"><?php echo htmlspecialchars($vehicle['car_price']['currency_info'] ?? ' - '); ?></td>
|
||||
@@ -214,7 +221,7 @@
|
||||
<!-- 行5:备注(因为“编号”已跨到本行,所以这里合并剩余10列) -->
|
||||
<tr>
|
||||
<td colspan="1"><strong>备注</strong></td>
|
||||
<td colspan="9" style="text-align:left;">
|
||||
<td colspan="10" style="text-align:left;">
|
||||
<span class="info-value">
|
||||
<?php echo nl2br($vehicle['remark'] ?? ''); ?>
|
||||
</span>
|
||||
@@ -231,7 +238,7 @@
|
||||
<span class="info-label">合同汇率:</span>
|
||||
<span class="info-value"><?php echo htmlspecialchars($data['contract_exchange_rate'] ?? ' - '); ?></span>
|
||||
</td>
|
||||
<td colspan="7" style="text-align:left;">
|
||||
<td colspan="8" style="text-align:left;">
|
||||
<span class="info-label">合计到岸价:</span>
|
||||
<span class="info-value"><?php echo htmlspecialchars($data['cif_total']['currency_info'] ?? ' - '); ?></span>
|
||||
</td>
|
||||
@@ -261,7 +268,8 @@
|
||||
|
||||
<td><span class="info-label">出运方式:</span> <span class="info-value"><?php echo htmlspecialchars($data['shipping_method_snapshot'] ?? ' - '); ?></span></td>
|
||||
<td><span class="info-label">国内收货港:</span> <span class="info-value"><?php echo htmlspecialchars($data['domestic_port_snapshot'] ?? ' - '); ?></span></td>
|
||||
<td><span class="info-label">贸易条款/清关目的港:</span> <span class="info-value"><?php echo htmlspecialchars($data['trade_term_snapshot'] ?? ' - '); ?>/<?php echo htmlspecialchars($data['clearance_port_snapshot'] ?? ' - '); ?></span></td>
|
||||
<td><span class="info-label">贸易条款:</span> <span class="info-value"><?php echo htmlspecialchars($data['trade_term_snapshot'] ?? ' - '); ?></span></td>
|
||||
<td><span class="info-label">清关目的港:</span> <span class="info-value"><?php echo htmlspecialchars($data['clearance_port_snapshot'] ?? ' - '); ?></span></td>
|
||||
|
||||
<?php if ($data['is_final'] == 1): ?>
|
||||
<td><span class="info-label">提车日期:</span> <span class="info-value nowrap"><?php echo htmlspecialchars($data['pick_up_date'] ?? ' - '); ?></span></td>
|
||||
|
||||
Reference in New Issue
Block a user