[FIX] 整合機台效期管理功能並優化 UI 比例
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 1m2s

- 修正 Alpine.js 作用域問題,恢復效期編輯彈窗功能
- 整合機台日誌與效期管理至主列表頁 (Index)
- 優化大螢幕貨道格線佈局,解決日期折行問題
- 縮小彈窗字體與內距,調整為極簡奢華風 UI
- 新增貨道效期與批號欄位之 Migration 與模型關聯
- 補齊中、英、日三語系翻譯檔
This commit is contained in:
2026-03-24 16:46:04 +08:00
parent 38770b080b
commit 87ef247a48
22 changed files with 2539 additions and 647 deletions

View File

@@ -9,11 +9,13 @@ use Illuminate\View\View;
class MachineController extends AdminController
{
/**
* 顯示所有機台列表
* 顯示所有機台列表或效期管理
*/
public function index(Request $request): View
{
$per_page = $request->input('per_page', 10);
$tab = $request->input('tab', 'list');
$per_page = $tab === 'list' ? $request->input('per_page', 10) : $request->input('per_page', 12);
$query = Machine::query();
// 搜尋:名稱或序號
@@ -24,14 +26,33 @@ class MachineController extends AdminController
});
}
$machines = $query->when($request->status, function ($query, $status) {
return $query->where('status', $status);
})
->latest()
->paginate($per_page)
->withQueryString();
if ($tab === 'list') {
$machines = $query->when($request->status, function ($query, $status) {
return $query->where('status', $status);
})
->latest()
->paginate($per_page)
->withQueryString();
return view('admin.machines.index', compact('machines'));
return view('admin.machines.index', compact('machines', 'tab'));
} else {
// 效期管理模式:獲取機台及其貨道統計
$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()]);
}])
->latest()
->paginate($per_page)
->withQueryString();
return view('admin.machines.index', compact('machines', 'tab'));
}
}
/**
@@ -46,25 +67,38 @@ class MachineController extends AdminController
return view('admin.machines.show', compact('machine'));
}
/**
* 顯示所有機台日誌列表
* AJAX: 取得機台抽屜面板所需的歷程日誌
*/
public function logs(Request $request): View
public function logsAjax(Request $request, Machine $machine)
{
$per_page = $request->input('per_page', 10);
$logs = \App\Models\Machine\MachineLog::with('machine')
$per_page = $request->input('per_page', 20);
$startDate = $request->get('start_date', now()->format('Y-m-d'));
$endDate = $request->get('end_date', now()->format('Y-m-d'));
$logs = $machine->logs()
->when($request->level, function ($query, $level) {
return $query->where('level', $level);
})
->when($request->machine_id, function ($query, $machineId) {
return $query->where('machine_id', $machineId);
->whereDate('created_at', '>=', $startDate)
->whereDate('created_at', '<=', $endDate)
->when($request->type, function ($query, $type) {
return $query->where('type', $type);
})
->latest()
->paginate($per_page)->withQueryString();
->paginate($per_page);
$machines = Machine::select('id', 'name')->get();
return view('admin.machines.logs', compact('logs', 'machines'));
return response()->json([
'success' => true,
'data' => $logs->items(),
'pagination' => [
'total' => $logs->total(),
'current_page' => $logs->currentPage(),
'last_page' => $logs->lastPage(),
]
]);
}
@@ -132,19 +166,77 @@ class MachineController extends AdminController
}
/**
* 機台使用率統計 (開發中)
* 機台使用率統計
*/
public function utilization(Request $request): View
{
return view('admin.machines.index', ['machines' => Machine::paginate(1)]); // Placeholder
// 取得當前使用者有權限的所有機台 (已透過 Global Scope 過濾)
$machines = Machine::all();
return view('admin.machines.utilization', [
'machines' => $machines
]);
}
/**
* 機台到期管理 (開發中)
* AJAX: 取得機台所有貨道資訊 (供效期管理視覺化圖表使用)
*/
public function expiry(Request $request): View
public function slotsAjax(Machine $machine)
{
return view('admin.machines.index', ['machines' => Machine::paginate(1)]); // Placeholder
$slots = $machine->slots()->with('product:id,name,image')->orderByRaw('CAST(slot_no AS UNSIGNED) ASC')->get();
return response()->json([
'success' => true,
'machine' => $machine->only(['id', 'name', 'serial_no']),
'slots' => $slots
]);
}
/**
* AJAX: 更新貨道效期
*/
public function updateSlotExpiry(Request $request, Machine $machine)
{
$request->validate([
'slot_no' => 'required|integer',
'expiry_date' => 'nullable|date',
'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]);
}
return response()->json([
'success' => true,
'message' => __('Expiry updated successfully.')
]);
}
/**
* 取得機台統計數據 (AJAX)
*/
public function utilizationData(int $id, Request $request)
{
$machine = Machine::findOrFail($id);
$date = $request->get('date', now()->toDateString());
$service = app(\App\Services\Machine\MachineService::class);
$stats = $service->getUtilizationStats($machine, $date);
return response()->json([
'success' => true,
'data' => $stats
]);
}
/**