[STYLE] 修復機台庫存管理功能並全面升級極簡奢華風 UI
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 56s

1. [FIX] 修復 MachineController 500 錯誤:注入缺失的 MachineService 執行個體。
2. [STYLE] 貨道卡片重構:改為垂直堆疊佈局,移除冗餘標籤,並優化庫存 (x/y) 與效期格式。
3. [STYLE] 極致化間距調優:壓縮全域 Padding 與 Gap,並將貨道編號絕對定位於頂部,提升顯示密度。
4. [FIX] 穩定性修復:解決 Alpine.js 在返回列表時的 selectedMachine 空值存取報錯。
5. [STYLE] UI 細節修飾:隱藏輸入框微調箭頭,強化編號字體粗細與位置精準度。
6. [DOCS] 翻譯同步:更新 zh_TW, en, ja 翻譯檔中關於庫存與貨道的語系 Key。
7. [FEAT] 整合遠端管理模組:新增並導航至 resources/views/admin/remote/stock.blade.php。
This commit is contained in:
2026-04-01 15:26:21 +08:00
parent 7c47ad67fa
commit 969e4df629
10 changed files with 1139 additions and 987 deletions

View File

@@ -3,14 +3,16 @@
namespace App\Http\Controllers\Admin;
use App\Models\Machine\Machine;
use App\Services\Machine\MachineService;
use Illuminate\Http\Request;
use Illuminate\View\View;
class MachineController extends AdminController
{
public function __construct(protected MachineService $machineService) {}
public function index(Request $request): View
{
$tab = $request->input('tab', 'list');
$per_page = $request->input('per_page', 10);
$query = Machine::query();
@@ -23,27 +25,13 @@ class MachineController extends AdminController
});
}
// 統一預加載貨道統計資料 (無論在哪一個頁籤)
$machines = $query->withCount(['slots as total_slots'])
->withCount(['slots as expired_count' => function ($q) {
$q->where('expiry_date', '<', now()->toDateString());
}])
->withCount(['slots as pending_count' => function ($q) {
$q->whereNull('expiry_date');
}])
->withCount(['slots as warning_count' => function ($q) {
$q->whereBetween('expiry_date', [now()->toDateString(), now()->addDays(7)->toDateString()]);
}])
// 只有在機台列表且有狀態篩選時才套用狀態過濾
->when($request->status && $tab === 'list', function ($q, $status) {
return $q->where('status', $status);
})
->orderBy("last_heartbeat_at", "desc")
// 預加載統計資料
$machines = $query->orderBy("last_heartbeat_at", "desc")
->orderBy("id", "desc")
->paginate($per_page)
->withQueryString();
return view('admin.machines.index', compact('machines', 'tab'));
return view('admin.machines.index', compact('machines'));
}
/**
@@ -132,32 +120,23 @@ class MachineController extends AdminController
}
/**
* AJAX: 更新貨道效期
* AJAX: 更新貨道資訊 (庫存、效期、批號)
*/
public function updateSlotExpiry(Request $request, Machine $machine)
{
$request->validate([
$validated = $request->validate([
'slot_no' => 'required|integer',
'stock' => 'nullable|integer|min:0',
'expiry_date' => 'nullable|date',
'batch_no' => 'nullable|string|max:50',
'apply_all_same_product' => 'boolean'
]);
$slotNo = $request->slot_no;
$expiryDate = $request->expiry_date;
$applyAll = $request->apply_all_same_product ?? false;
$slot = $machine->slots()->where('slot_no', $slotNo)->firstOrFail();
$slot->update(['expiry_date' => $expiryDate]);
if ($applyAll && $slot->product_id) {
$machine->slots()
->where('product_id', $slot->product_id)
->update(['expiry_date' => $expiryDate]);
}
$this->machineService->updateSlot($machine, $validated);
return response()->json([
'success' => true,
'message' => __('Expiry updated successfully.')
'message' => __('Slot updated successfully.')
]);
}

View File

@@ -8,11 +8,23 @@ use Illuminate\Http\Request;
class RemoteController extends Controller
{
// 機台庫存
public function stock()
public function stock(Request $request)
{
return view('admin.placeholder', [
'title' => '遠端修改機台庫存',
'description' => '遠端修改機台庫存數量',
$machines = \App\Models\Machine\Machine::withCount([
'slots as slots_count',
'slots as low_stock_count' => function ($query) {
$query->where('stock', '<=', 5);
}
])->orderBy('name')->get();
$selectedMachine = null;
if ($request->has('machine_id')) {
$selectedMachine = \App\Models\Machine\Machine::find($request->machine_id);
}
return view('admin.remote.stock', [
'machines' => $machines,
'selectedMachine' => $selectedMachine,
]);
}

View File

@@ -91,6 +91,43 @@ class MachineService
});
}
/**
* Update machine slot stock, expiry, and batch.
*
* @param Machine $machine
* @param array $data
* @return void
*/
public function updateSlot(Machine $machine, array $data): void
{
DB::transaction(function () use ($machine, $data) {
$slotNo = $data['slot_no'];
$stock = $data['stock'] ?? null;
$expiryDate = $data['expiry_date'] ?? null;
$batchNo = $data['batch_no'] ?? null;
$applyAllSame = $data['apply_all_same_product'] ?? false;
$slot = $machine->slots()->where('slot_no', $slotNo)->with('product')->firstOrFail();
if ($applyAllSame && $slot->product_id) {
// 更新該機台內所有相同商品的貨道
$machine->slots()->where('product_id', $slot->product_id)->update([
'stock' => $stock !== null ? (int)$stock : DB::raw('stock'),
'expiry_date' => $expiryDate,
'batch_no' => $batchNo,
]);
} else {
// 僅更新單一貨道
$updateData = [
'expiry_date' => $expiryDate,
'batch_no' => $batchNo,
];
if ($stock !== null) $updateData['stock'] = (int)$stock;
$slot->update($updateData);
}
});
}
/**
* Update machine slot stock (single slot).
* Legacy support for recordLog (Existing code).