分店介面 與包廂設定 20250514
This commit is contained in:
parent
023c00d20a
commit
5359037500
@ -9,12 +9,13 @@ use Livewire\Component;
|
||||
use WireUi\Traits\WireUiActions;
|
||||
|
||||
use App\Models\Branch;
|
||||
use App\Models\Room;
|
||||
|
||||
class BranchForm extends Component
|
||||
{
|
||||
use WireUiActions;
|
||||
|
||||
protected $listeners = ['openModal','closeModal', 'deleteArtist'];
|
||||
protected $listeners = ['openModal','closeModal', 'deleteBranch'];
|
||||
|
||||
public bool $canCreate;
|
||||
public bool $canEdit;
|
||||
@ -27,6 +28,7 @@ class BranchForm extends Component
|
||||
'name' =>'',
|
||||
'external_ip' =>'',
|
||||
'enable' => true,
|
||||
'roomNotes' =>''
|
||||
];
|
||||
|
||||
|
||||
@ -70,7 +72,29 @@ class BranchForm extends Component
|
||||
}
|
||||
} else {
|
||||
if ($this->canCreate) {
|
||||
$branch = Branch::create($this->fields);
|
||||
$branch = Branch::create([
|
||||
'name' => $this->fields['name'],
|
||||
'external_ip' => $this->fields['external_ip'],
|
||||
'enable' => $this->fields['enable'],
|
||||
]);
|
||||
|
||||
// 解析 roomNotes
|
||||
$roomLines = explode("\n", trim($this->fields['roomNotes']));
|
||||
$rooms = [];
|
||||
|
||||
foreach ($roomLines as $line) {
|
||||
[$floor, $roomList] = explode(';', $line);
|
||||
$roomNames = array_map('trim', explode(',', $roomList));
|
||||
|
||||
foreach ($roomNames as $roomName) {
|
||||
$rooms[] = new Room([
|
||||
'name' => $roomName,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// 儲存所有 Room
|
||||
$branch->rooms()->saveMany($rooms);
|
||||
$this->notification()->send([
|
||||
'icon' => 'success',
|
||||
'title' => '成功',
|
||||
@ -83,7 +107,7 @@ class BranchForm extends Component
|
||||
$this->dispatch('pg:eventRefresh-branch-table');
|
||||
}
|
||||
|
||||
public function deleteArtist($id)
|
||||
public function deleteBranch($id)
|
||||
{
|
||||
if ($this->canDelect) {
|
||||
Branch::findOrFail($id)->delete();
|
||||
|
@ -183,6 +183,11 @@ final class BranchTable extends PowerGridComponent
|
||||
public function actions(Branch $row): array
|
||||
{
|
||||
$actions = [];
|
||||
$actions[] = Button::add('room-settings')
|
||||
->slot('包廂設定')
|
||||
->icon('solid-cog')
|
||||
->class('inline-flex items-center gap-1 px-3 py-1 rounded bg-amber-200 text-black')
|
||||
->dispatchTo('admin.room-grid', 'openModal', ['branch_id' => $row->id]);
|
||||
if ($this->canEdit) {
|
||||
$actions[] =Button::add('edit')
|
||||
->slot(__('branches.edit'))
|
||||
|
28
app/Livewire/Admin/RoomDetailModal.php
Normal file
28
app/Livewire/Admin/RoomDetailModal.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Admin;
|
||||
|
||||
use App\Models\Room;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class RoomDetailModal extends Component
|
||||
{
|
||||
|
||||
|
||||
protected $listeners = ['openModal'];
|
||||
|
||||
public $room;
|
||||
public bool $showModal = false;
|
||||
|
||||
public function openModal($roomId)
|
||||
{
|
||||
$this->room = Room::find($roomId);
|
||||
$this->showModal = true;
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.admin.room-detail-modal');
|
||||
}
|
||||
}
|
35
app/Livewire/Admin/RoomGrid.php
Normal file
35
app/Livewire/Admin/RoomGrid.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Admin;
|
||||
|
||||
use App\Models\Room;
|
||||
use App\Models\Branch;
|
||||
|
||||
use Livewire\Component;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
|
||||
class RoomGrid extends Component
|
||||
{
|
||||
protected $listeners = ['openModal','closeModal'];//,'refreshRooms' => '$refresh'
|
||||
|
||||
public bool $showModal = false;
|
||||
public $branchName="";
|
||||
public Collection $rooms;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->rooms = new Collection();
|
||||
}
|
||||
public function openModal($branch_id = null)
|
||||
{
|
||||
$this->branchName=Branch::where('id',$branch_id)->first()->name;
|
||||
$this->rooms = Room::where('branch_id',$branch_id)->get();
|
||||
$this->showModal = true;
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.admin.room-grid');
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ class Artist extends Model
|
||||
protected static function booted()
|
||||
{
|
||||
// 無論是 creating 或 updating,都執行這段共用的邏輯
|
||||
static::saving(function ($artist) {
|
||||
static::saving(function (Artist $artist) {
|
||||
$simplified=ChineseNameConverter::convertToSimplified($artist->name);// 繁體轉簡體
|
||||
$artist->simplified = $simplified;
|
||||
$artist->phonetic_abbr = ChineseNameConverter::getKTVZhuyinAbbr($simplified);// 注音符號
|
||||
@ -47,6 +47,11 @@ class Artist extends Model
|
||||
$firstChar = $chars[0] ?? null;
|
||||
$artist->strokes_abbr=( $firstChar && preg_match('/\p{Han}/u', $firstChar) ) ? ChineseStrokesConverter::getStrokes($firstChar) : 0;
|
||||
});
|
||||
|
||||
static::deleting(function (Artist $artist) {
|
||||
// 解除與歌曲的多對多關聯
|
||||
$artist->songs()->detach();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,4 +25,10 @@ class Branch extends Model
|
||||
->withPivot('counts')
|
||||
->withTimestamps();
|
||||
}
|
||||
protected static function booted()
|
||||
{
|
||||
static::deleting(function (Branch $branch) {
|
||||
$branch->rooms()->delete();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -11,11 +11,41 @@ class Room extends Model
|
||||
/** @use HasFactory<\Database\Factories\ArtistFactory> */
|
||||
use HasFactory, LogsModelActivity;
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'internal_ip',
|
||||
'port',
|
||||
'status',
|
||||
'started_at',
|
||||
'ended_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'name' => 'string',
|
||||
'internal_ip' =>'string',
|
||||
'port' => 'int',
|
||||
'status' => \App\Enums\RoomStatus::class,
|
||||
'started_at' => 'datetime',
|
||||
'ended_at' => 'datetime',
|
||||
|
||||
];
|
||||
|
||||
public function str_started_at(){
|
||||
$str ="Not Set";
|
||||
if($this->started_at !=null){
|
||||
$str = $this->started_at;
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
public function str_ended_at(){
|
||||
$str ="Not Set";
|
||||
if($this->ended_at !=null){
|
||||
$str = $this->ended_at;
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
public function branch() {
|
||||
return $this->belongsTo(Branch::class);
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ class Song extends Model
|
||||
protected static function booted()
|
||||
{
|
||||
// 無論是 creating 或 updating,都執行這段共用的邏輯
|
||||
static::saving(function ($song) {
|
||||
static::saving(function (Song $song) {
|
||||
$simplified=ChineseNameConverter::convertToSimplified($song->name);// 繁體轉簡體
|
||||
$song->simplified = $simplified;
|
||||
$song->phonetic_abbr = ChineseNameConverter::getKTVZhuyinAbbr($simplified);// 注音符號
|
||||
@ -83,5 +83,12 @@ class Song extends Model
|
||||
$song->strokes_abbr=($firstChar && preg_match('/\p{Han}/u', $firstChar)) ? ChineseStrokesConverter::getStrokes($firstChar) : 0;
|
||||
$song->song_number = mb_strlen($song->name, 'UTF-8');
|
||||
});
|
||||
static::deleting(function (Song $song) {
|
||||
// Detach 關聯資料
|
||||
$song->artists()->detach();
|
||||
$song->categories()->detach();
|
||||
$song->branches()->detach();
|
||||
$song->users()->detach();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,10 @@ return new class extends Migration
|
||||
$table->id();
|
||||
$table->foreignId('branch_id')->constrained()->onDelete('cascade')->comment('關聯分店');
|
||||
$table->string('name')->comment('包廂名稱');
|
||||
$table->string('internal_ip')->comment('內部 IP');
|
||||
$table->unsignedSmallInteger('port')->comment('通訊 Port');
|
||||
$table->enum('status', ['active', 'closed', 'error', 'maintenance'])->comment('狀態'); // :啟用中 / 已結束
|
||||
$table->dateTime('started_at')->comment('開始時間'); //
|
||||
$table->string('internal_ip')->nullable()->comment('內部 IP');
|
||||
$table->unsignedSmallInteger('port')->nullable()->comment('通訊 Port');
|
||||
$table->enum('status', ['active', 'closed', 'error', 'maintenance'])->default('error')->comment('狀態'); // :啟用中 / 已結束
|
||||
$table->dateTime('started_at')->nullable()->comment('開始時間'); //
|
||||
$table->dateTime('ended_at')->nullable()->comment('結束時間'); //
|
||||
$table->timestamps();
|
||||
});
|
||||
|
@ -23,4 +23,9 @@ return [
|
||||
'user.status.Active' => '正常',
|
||||
'user.status.Suspended' => '停權',
|
||||
'user.status.Deleting' => '刪除中',
|
||||
|
||||
'room.status.Active' => '已占用',
|
||||
'room.status.Closed' => '可用',
|
||||
'room.status.Error' => '異常',
|
||||
'room.status.Maintenance' => '維修',
|
||||
];
|
20
resources/views/components/room-card.blade.php
Normal file
20
resources/views/components/room-card.blade.php
Normal file
@ -0,0 +1,20 @@
|
||||
@php
|
||||
use App\Enums\RoomStatus;
|
||||
$statusColors = [
|
||||
RoomStatus::Active->value => 'green-600',
|
||||
RoomStatus::Closed->value => 'gray-600',
|
||||
RoomStatus::Error->value => 'red-600',
|
||||
RoomStatus::Maintenance->value => 'yellow-600',
|
||||
];
|
||||
@endphp
|
||||
|
||||
<div class="border p-2 rounded shadow-md h-32 relative cursor-pointer bg-amber-50"
|
||||
wire:click="$dispatchTo('admin.room-detail-modal','openModal', { roomId: {{ $room->id }} })">
|
||||
<div class="font-bold">{{ $room->name }}</div>
|
||||
<div class="text-sm text-{{ $statusColors[$room->status->value] ?? 'gray-500' }} text-center">
|
||||
{{ $room->status->labels() }}
|
||||
</div>
|
||||
<div class="text-xs text-center whitespace-nowrap ">{{ $room->str_started_at() }}</div>
|
||||
<div class="text-xs text-center whitespace-nowrap ">{{ $room->str_ended_at() }}</div>
|
||||
|
||||
</div>
|
@ -4,7 +4,9 @@
|
||||
<x-wireui:input label="{{__('branches.external_ip')}}" wire:model.defer="fields.external_ip" />
|
||||
<x-wireui:toggle label="{{__('branches.enable')}}" wire:model.defer="fields.enable" />
|
||||
</div>
|
||||
|
||||
@unless($branchId)
|
||||
<x-wireui:textarea label="包廂資料" placeholder="請寫下包廂...範利<br>1樓;pc101,pc102,pc103" wire:model.defer="fields.roomNotes"/>
|
||||
@endunless
|
||||
<x-slot name="footer">
|
||||
<div class="flex justify-between w-full">
|
||||
<x-wireui:button flat label="{{__('branches.cancel')}}" wire:click="closeModal" />
|
||||
|
@ -2,6 +2,7 @@
|
||||
<x-layouts.admin>
|
||||
<x-wireui:notifications/>
|
||||
<livewire:admin.branch-table />
|
||||
<livewire:admin.room-grid />
|
||||
<livewire:admin.branch-form />
|
||||
<livewire:admin.branch-import-data />
|
||||
</x-layouts.admin>
|
29
resources/views/livewire/admin/room-detail-modal.blade.php
Normal file
29
resources/views/livewire/admin/room-detail-modal.blade.php
Normal file
@ -0,0 +1,29 @@
|
||||
<x-wireui:modal id="room-detail-modal" wire:model.defer="showModal" persistent>
|
||||
<x-wireui:card class="border border-gray-200">
|
||||
<x-slot name="action">
|
||||
<button class="cursor-pointer p-1 rounded-full focus:outline-none focus:outline-hidden focus:ring-2 focus:ring-secondary-200 text-secondary-300"
|
||||
x-on:click="close"
|
||||
tabindex="-1"
|
||||
>
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
name="x-mark"
|
||||
class="w-5 h-5"
|
||||
/>
|
||||
</button>
|
||||
</x-slot>
|
||||
<x-slot name="title">
|
||||
{{ $room->name ?? '未選擇' }}包廂設定
|
||||
</x-slot>
|
||||
<div class="grid grid-cols-3 gap-4 mb-4">
|
||||
<x-wireui:button wire:click="startMachine" >開機</x-wireui:button>
|
||||
<x-wireui:button wire:click="stopMachine" >關機</x-wireui:button>
|
||||
<x-wireui:button wire:click="closeModal" >取消</x-wireui:button>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4 mb-4">
|
||||
<x-wireui:button wire:click="openAccount" >包廂開帳</x-wireui:button>
|
||||
<x-wireui:button wire:click="closeAccount" >包廂關帳</x-wireui:button>
|
||||
</div>
|
||||
|
||||
</x-wireui:card>
|
||||
</x-wireui:modal>
|
22
resources/views/livewire/admin/room-grid.blade.php
Normal file
22
resources/views/livewire/admin/room-grid.blade.php
Normal file
@ -0,0 +1,22 @@
|
||||
<x-wireui:modal id="room-grid-modal" wire:model.defer="showModal" persistent >
|
||||
<x-wireui:card class="border border-gray-200 w-full">
|
||||
<x-slot name="action">
|
||||
<button class="cursor-pointer p-1 rounded-full focus:outline-none focus:outline-hidden focus:ring-2 focus:ring-secondary-200 text-secondary-300"
|
||||
x-on:click="close"
|
||||
tabindex="-1"
|
||||
>
|
||||
<x-dynamic-component :component="WireUi::component('icon')" name="x-mark" class="w-5 h-5"/>
|
||||
</button>
|
||||
</x-slot>
|
||||
<x-slot name="title">{{$branchName}} - 包廂設定</x-slot>
|
||||
|
||||
<div class="grid grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4">
|
||||
@forelse($rooms as $room)
|
||||
<x-room-card :room="$room" />
|
||||
@empty
|
||||
<div class="col-span-full text-center text-gray-500">尚無包廂資料</div>
|
||||
@endforelse
|
||||
</div>
|
||||
<livewire:admin.room-detail-modal />
|
||||
</x-wireui:card>
|
||||
</x-wireui:modal>
|
Loading…
x
Reference in New Issue
Block a user