feat(output_view): 导出页面重构 - 长图卡片化展示、缩略图增大、预览优化、纯图片页原图保存

- output_view.html: 长图改为固定高度卡片(70px),Blob URL查看,缩略图minmax(280px,1fr),
  竖图预览优先填充视口高度,下载功能完整保留
- phone-image.js: renderPureImageToCanvas()使用naturalWidth/naturalHeight保持原图分辨率,
  新增长图生成和保存功能
- Post.php: 新增outputView()方法提供导出页面渲染数据
- PhoneImage.php: 图片数据改为DB存储,新增saveLongImage()方法
- phone_image.html: 添加导出页面入口按钮
- 新增数据库迁移: post_output_file表添加image_data字段
This commit is contained in:
augushong
2026-05-14 23:22:19 +08:00
parent f60d1abff6
commit 10879a8037
6 changed files with 784 additions and 83 deletions

View File

@@ -70,40 +70,69 @@ class PhoneImage implements PostOutputManagerInterface
*/
public function savePageImage(int $outputId, int $page, string $imageData)
{
$imageData = str_replace('data:image/jpeg;base64,', '', $imageData);
$imageData = str_replace('data:image/png;base64,', '', $imageData);
$imageData = str_replace(' ', '+', $imageData);
$decoded = base64_decode($imageData);
// 保留原始base64用于存储
$rawBase64 = str_replace('data:image/jpeg;base64,', '', $imageData);
$rawBase64 = str_replace('data:image/png;base64,', '', $rawBase64);
$rawBase64 = str_replace(' ', '+', $rawBase64);
$decoded = base64_decode($rawBase64);
if ($decoded === false) {
return false;
}
$dateDir = date('Ymd');
$relativeDir = '/upload/post_output/' . $dateDir;
$fileName = $outputId . '_' . $page . '.jpg';
$relativePath = $relativeDir . '/' . $fileName;
$fullDir = App::getRootPath() . '/public' . $relativeDir;
$fullPath = $fullDir . '/' . $fileName;
if (!is_dir($fullDir)) {
mkdir($fullDir, 0777, true);
}
file_put_contents($fullPath, $decoded);
$imageInfo = getimagesize($fullPath);
$width = $imageInfo[0] ?? 0;
$height = $imageInfo[1] ?? 0;
$imageInfo = @getimagesizefromstring($decoded);
$width = is_array($imageInfo) ? $imageInfo[0] : 0;
$height = is_array($imageInfo) ? $imageInfo[1] : 0;
$fileRecord = PostOutputFile::create([
'output_id' => $outputId,
'page' => $page,
'file_path' => $relativePath,
'file_url' => $relativePath,
'file_path' => '',
'file_url' => '',
'file_size' => strlen($decoded),
'width' => $width,
'height' => $height,
'image_data' => $rawBase64,
]);
return $fileRecord;
}
/**
* 保存长图
* @param int $outputId 输出记录ID
* @param string $imageData base64编码的长图数据
* @return PostOutputFile|false
*/
public function saveLongImage(int $outputId, string $imageData)
{
$rawBase64 = str_replace('data:image/jpeg;base64,', '', $imageData);
$rawBase64 = str_replace('data:image/png;base64,', '', $rawBase64);
$rawBase64 = str_replace(' ', '+', $rawBase64);
$decoded = base64_decode($rawBase64);
if ($decoded === false) {
return false;
}
$imageInfo = @getimagesizefromstring($decoded);
$width = is_array($imageInfo) ? $imageInfo[0] : 0;
$height = is_array($imageInfo) ? $imageInfo[1] : 0;
// 先删除该output_id下已有的长图(page=0)
PostOutputFile::where('output_id', $outputId)
->where('page', 0)
->delete();
$fileRecord = PostOutputFile::create([
'output_id' => $outputId,
'page' => 0,
'file_path' => '',
'file_url' => '',
'file_size' => strlen($decoded),
'width' => $width,
'height' => $height,
'image_data' => $rawBase64,
]);
return $fileRecord;
@@ -137,22 +166,42 @@ class PhoneImage implements PostOutputManagerInterface
$zipFileName = 'post_output_' . $outputId . '.zip';
$zipPath = $tempDir . '/' . $zipFileName;
$tempFiles = [];
$zip = new \ZipArchive();
if ($zip->open($zipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
throw new \Exception('无法创建ZIP文件');
}
try {
$zip = new \ZipArchive();
if ($zip->open($zipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
throw new \Exception('无法创建ZIP文件');
}
foreach ($files as $file) {
$filePath = App::getRootPath() . '/public' . $file->file_path;
if (file_exists($filePath)) {
foreach ($files as $file) {
$pageName = str_pad((string) $file->page, 2, '0', STR_PAD_LEFT) . '.jpg';
$zip->addFile($filePath, $pageName);
if (!empty($file->image_data)) {
$decoded = base64_decode($file->image_data);
if ($decoded !== false) {
$tempFilePath = $tempDir . '/page_' . $file->page . '_' . $outputId . '.jpg';
file_put_contents($tempFilePath, $decoded);
$tempFiles[] = $tempFilePath;
$zip->addFile($tempFilePath, $pageName);
}
} else {
$filePath = App::getRootPath() . '/public' . $file->file_path;
if (file_exists($filePath)) {
$zip->addFile($filePath, $pageName);
}
}
}
$zip->close();
} finally {
foreach ($tempFiles as $tempFile) {
if (file_exists($tempFile)) {
@unlink($tempFile);
}
}
}
$zip->close();
return $zipPath;
}