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(), ])); // 處理圖片更新 (支援 3 個獨立槽位) if ($request->hasFile('images')) { $currentImages = $machine->images ?? []; $newImages = $request->file('images'); $updated = false; foreach ($newImages as $index => $file) { // 限制 3 個槽位 (0, 1, 2) if ($index < 0 || $index > 2) continue; // 刪除該槽位的舊圖 if (isset($currentImages[$index]) && !empty($currentImages[$index])) { \Illuminate\Support\Facades\Storage::disk('public')->delete($currentImages[$index]); } // 處理並儲存新圖 $currentImages[$index] = $this->processAndStoreImage($file); $updated = true; } if ($updated) { ksort($currentImages); $machine->update(['images' => array_values($currentImages)]); } } return redirect()->route('admin.basic-settings.machines.index') ->with('success', __('Machine settings updated successfully.')); } /** * 處理並儲存圖片 (轉換為 WebP 並調整大小) */ protected function processAndStoreImage($file) { $path = 'machines/' . \Illuminate\Support\Str::random(40) . '.webp'; // 載入原圖 $imageInfo = getimagesize($file->getRealPath()); $mime = $imageInfo['mime']; switch ($mime) { case 'image/jpeg': $image = imagecreatefromjpeg($file->getRealPath()); break; case 'image/png': $image = imagecreatefrompng($file->getRealPath()); break; case 'image/gif': $image = imagecreatefromgif($file->getRealPath()); break; default: return $file->store('machines', 'public'); } if ($image) { // [修正] imagewebp(): Palette image not supported by webp // 若為 Palette 圖片 (例如 GIF),轉換為 Truecolor if (!imageistruecolor($image)) { imagepalettetotruecolor($image); } \Illuminate\Support\Facades\Storage::disk('public')->makeDirectory('machines'); $fullPath = \Illuminate\Support\Facades\Storage::disk('public')->path($path); // 轉換並儲存 (品質 80) imagewebp($image, $fullPath, 80); imagedestroy($image); return $path; } return $file->store('machines', 'public'); } }