[RELEASE] 晉升 demo 到 main 正式上線商品軟刪除與圖片破圖優化

1. 於 DispenseRecord、OrderItem 及 MachineStockMovement 模型之 product 關聯中加入 withTrashed(),使軟刪除商品的歷史快照仍可正常讀取。
2. 將列表、側邊詳情面板與報表導出中的 product->name 統一修改為 product->localized_name ?? __('Unknown Product'),支援多語系切換與翻譯並更正為 '未知商品'。
3. 在銷售明細的商品圖片區塊整合 Alpine.js x-on:error 動態防呆,一旦破圖自動無縫退回 3D 箱子預設占位 SVG,解決與 Blade 內建 @error 衝突引起的編譯錯誤。
This commit is contained in:
sky121113 2026-05-19 17:27:20 +08:00
commit 0f020afa6c
6 changed files with 17 additions and 13 deletions

View File

@ -833,7 +833,7 @@ class SalesController extends Controller
$row = [
$log->machine->name ?? 'Unknown',
$log->machine->serial_no ?? '---',
$log->product->name ?? 'Unknown Product',
$log->product->localized_name ?? __('Unknown Product'),
$log->slot_no,
number_format($log->amount, 2, '.', ''),
$statusLabel,

View File

@ -76,7 +76,7 @@ class MachineStockMovement extends Model
public function product(): BelongsTo
{
return $this->belongsTo(Product::class);
return $this->belongsTo(Product::class)->withTrashed();
}
public function creator(): BelongsTo

View File

@ -45,6 +45,6 @@ class DispenseRecord extends Model
public function product()
{
return $this->belongsTo(Product::class);
return $this->belongsTo(Product::class)->withTrashed();
}
}

View File

@ -34,6 +34,6 @@ class OrderItem extends Model
public function product()
{
return $this->belongsTo(Product::class);
return $this->belongsTo(Product::class)->withTrashed();
}
}

View File

@ -126,15 +126,19 @@
<div class="space-y-3">
@foreach($order->items as $item)
<div class="luxury-card p-4 rounded-2xl flex items-center gap-4 group/item transition-all hover:border-cyan-500/30">
<div class="w-16 h-16 rounded-xl bg-slate-100 dark:bg-slate-800 flex-shrink-0 overflow-hidden border border-slate-200 dark:border-slate-700 group-hover:scale-105 transition-transform flex items-center justify-center">
<div class="luxury-card p-4 rounded-2xl flex items-center gap-4 group/item transition-all hover:border-cyan-500/30"
x-data="{ imgFailed: {{ (isset($item->product->image_url) && $item->product->image_url) ? 'false' : 'true' }} }">
<div class="w-16 h-16 rounded-xl bg-slate-100 dark:bg-slate-800 flex-shrink-0 overflow-hidden border border-slate-200 dark:border-slate-700 group-hover:scale-105 transition-transform flex items-center justify-center relative">
@if(isset($item->product->image_url) && $item->product->image_url)
<img src="{{ $item->product->image_url }}" class="w-full h-full object-cover">
@else
<svg class="w-8 h-8 text-slate-300 group-hover:text-cyan-500 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<img src="{{ $item->product->image_url }}" class="w-full h-full object-cover" x-show="!imgFailed" x-on:error="imgFailed = true">
@endif
<div x-show="imgFailed"
class="absolute inset-0 flex items-center justify-center bg-slate-50 dark:bg-slate-800/50">
<svg class="w-8 h-8 text-slate-300 dark:text-slate-600 group-hover/item:text-cyan-500 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"/>
</svg>
@endif
</div>
</div>
<div class="flex-1 min-w-0">
<p class="text-base font-extrabold text-slate-800 dark:text-white truncate tracking-tight">{{ $item->product_name }}</p>
@ -198,7 +202,7 @@
<span class="text-xs font-black text-cyan-600 dark:text-cyan-400 font-mono">#{{ str_pad($dispense->slot_no, 2, '0', STR_PAD_LEFT) }}</span>
</td>
<td class="px-6 py-4">
<p class="text-sm font-bold text-slate-700 dark:text-slate-200">{{ $dispense->product->name ?? __('Unknown') }}</p>
<p class="text-sm font-bold text-slate-700 dark:text-slate-200">{{ $dispense->product->localized_name ?? __('Unknown Product') }}</p>
</td>
<td class="px-6 py-4 text-right">
@php

View File

@ -188,7 +188,7 @@
</td>
<td class="px-6 py-6">
<div class="text-sm font-black text-cyan-600 dark:text-cyan-400 mb-0.5">
{{ $log->product->name ?? 'Unknown Product' }}
{{ $log->product->localized_name ?? __('Unknown Product') }}
</div>
<div class="text-xs font-bold text-slate-400 uppercase tracking-widest">
{{ __('Slot') }}: {{ $log->slot_no }}
@ -287,7 +287,7 @@
__('Product / Slot') }}</p>
<div class="flex flex-col gap-0.5 min-w-0">
<p class="text-sm font-black text-cyan-600 dark:text-cyan-400 truncate">
{{ $log->product->name ?? 'Unknown' }}
{{ $log->product->localized_name ?? __('Unknown Product') }}
</p>
<p class="text-[10px] font-bold text-slate-400 uppercase tracking-widest">
{{ __('Slot') }}: {{ $log->slot_no }}