input('tab', 'machines'); $per_page = $request->input('per_page', 20); $search = $request->input('search'); // 1. 處理機台清單 (Machines Tab) $machineQuery = Machine::query()->with(['machineModel', 'paymentConfig', 'company']); if ($tab === 'machines' && $search) { $machineQuery->where(function ($q) use ($search) { $q->where('name', 'like', "%{$search}%") ->orWhere('serial_no', 'like', "%{$search}%"); }); } $machines = $machineQuery->latest()->paginate($per_page, ['*'], 'machines_page')->withQueryString(); // 2. 處理型號清單 (Models Tab) $modelQuery = MachineModel::query()->withCount('machines'); if ($tab === 'models' && $search) { $modelQuery->where('name', 'like', "%{$search}%"); } $models_list = $modelQuery->latest()->paginate($per_page, ['*'], 'models_page')->withQueryString(); // 3. 基礎下拉資料 (用於新增/編輯機台的彈窗) $models = MachineModel::select('id', 'name')->get(); $paymentConfigs = PaymentConfig::select('id', 'name')->get(); $companies = \App\Models\System\Company::select('id', 'name')->get(); return view('admin.basic-settings.machines.index', compact( 'machines', 'models_list', 'models', 'paymentConfigs', 'companies', 'tab' )); } /** * 儲存新機台 (僅核心欄位) */ public function store(Request $request): RedirectResponse { $validated = $request->validate([ 'name' => 'required|string|max:255', 'serial_no' => 'required|string|unique:machines,serial_no', 'company_id' => 'required|exists:companies,id', 'machine_model_id' => 'required|exists:machine_models,id', 'payment_config_id' => 'nullable|exists:payment_configs,id', 'location' => 'nullable|string|max:255', 'images.*' => 'image|mimes:jpeg,png,jpg,gif|max:2048', ]); $imagePaths = []; if ($request->hasFile('images')) { foreach (array_slice($request->file('images'), 0, 3) as $image) { $imagePaths[] = $this->processAndStoreImage($image); } } $machine = Machine::create(array_merge($validated, [ 'status' => 'offline', 'creator_id' => auth()->id(), 'updater_id' => auth()->id(), 'card_reader_seconds' => 30, // 預設值 'card_reader_checkout_time_1' => '22:30:00', 'card_reader_checkout_time_2' => '23:45:00', 'payment_buffer_seconds' => 5, 'images' => $imagePaths, ])); return redirect()->route('admin.basic-settings.machines.index') ->with('success', __('Machine created successfully.')); } /** * 顯示詳細編輯頁面 */ public function edit(Machine $machine): View { $models = MachineModel::select('id', 'name')->get(); $paymentConfigs = PaymentConfig::select('id', 'name')->get(); $companies = \App\Models\System\Company::select('id', 'name')->get(); return view('admin.basic-settings.machines.edit', compact('machine', 'models', 'paymentConfigs', 'companies')); } /** * 更新機台詳細參數 */ public function update(Request $request, Machine $machine): RedirectResponse { Log::info('Machine Update Request', ['machine_id' => $machine->id, 'data' => $request->all()]); try { $validated = $request->validate([ 'name' => 'required|string|max:255', 'card_reader_seconds' => 'required|integer|min:0', 'payment_buffer_seconds' => 'required|integer|min:0', 'card_reader_checkout_time_1' => 'nullable|string', 'card_reader_checkout_time_2' => 'nullable|string', 'heating_start_time' => 'nullable|string', 'heating_end_time' => 'nullable|string', 'card_reader_no' => 'nullable|string|max:255', 'key_no' => 'nullable|string|max:255', 'invoice_status' => 'required|integer|in:0,1,2', 'welcome_gift_enabled' => 'boolean', 'is_spring_slot_1_10' => 'boolean', 'is_spring_slot_11_20' => 'boolean', 'is_spring_slot_21_30' => 'boolean', 'is_spring_slot_31_40' => 'boolean', 'is_spring_slot_41_50' => 'boolean', 'is_spring_slot_51_60' => 'boolean', 'member_system_enabled' => 'boolean', 'machine_model_id' => 'required|exists:machine_models,id', 'payment_config_id' => 'nullable|exists:payment_configs,id', 'location' => 'nullable|string|max:255', ]); Log::info('Machine Update Validated Data', ['data' => $validated]); } catch (\Illuminate\Validation\ValidationException $e) { Log::error('Machine Update Validation Failed', ['errors' => $e->errors()]); throw $e; } $machine->update(array_merge($validated, [ 'updater_id' => auth()->id(), ])); // 處理圖片更新 (若有上傳新圖片,則替換或附加,這裡採簡單邏輯:若有傳 images 則全換) if ($request->hasFile('images')) { // 刪除舊圖 if (!empty($machine->images)) { foreach ($machine->images as $oldPath) { Storage::disk('public')->delete($oldPath); } } $imagePaths = []; foreach (array_slice($request->file('images'), 0, 3) as $image) { $imagePaths[] = $this->processAndStoreImage($image); } $machine->update(['images' => $imagePaths]); } return redirect()->route('admin.basic-settings.machines.index') ->with('success', __('Machine settings updated successfully.')); } /** * 處理圖片並轉換為 WebP */ private function processAndStoreImage($file): string { $filename = Str::random(40) . '.webp'; $path = 'machines/' . $filename; // 建立圖資源 $image = null; $extension = strtolower($file->getClientOriginalExtension()); switch ($extension) { case 'jpeg': case 'jpg': $image = imagecreatefromjpeg($file->getRealPath()); break; case 'png': $image = imagecreatefrompng($file->getRealPath()); break; case 'gif': $image = imagecreatefromgif($file->getRealPath()); break; case 'webp': $image = imagecreatefromwebp($file->getRealPath()); break; } if ($image) { // 確保目錄存在 Storage::disk('public')->makeDirectory('machines'); $fullPath = Storage::disk('public')->path($path); // 轉換並儲存 imagewebp($image, $fullPath, 80); // 品質 80 imagedestroy($image); return $path; } // Fallback to standard store if GD fails return $file->store('machines', 'public'); } }