資料庫調整
包廂加入樓層與 type 20250521
This commit is contained in:
parent
9746d57c89
commit
039a1f3595
31
app/Enums/RoomType.php
Normal file
31
app/Enums/RoomType.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use App\Enums\Traits\HasLabels;
|
||||
|
||||
/**
|
||||
* @OA\Schema(
|
||||
* schema="RoomType",
|
||||
* type="string",
|
||||
* enum={"unset", "pc", "svr"},
|
||||
* example="error"
|
||||
* )
|
||||
*/
|
||||
enum RoomType: string {
|
||||
use HasLabels;
|
||||
|
||||
case Unset = 'unset';
|
||||
case PC = 'pc';
|
||||
case SVR ='svr';
|
||||
|
||||
// 返回對應的顯示文字
|
||||
public function labels(): string
|
||||
{
|
||||
return match($this) {
|
||||
self::Unset => __('enums.room.status.Unset'),
|
||||
self::PC => "PC",
|
||||
self::SVR => "SVR",
|
||||
};
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ use WireUi\Traits\WireUiActions;
|
||||
|
||||
use App\Models\Branch;
|
||||
use App\Models\Room;
|
||||
use App\Enums\RoomType;
|
||||
|
||||
class BranchForm extends Component
|
||||
{
|
||||
@ -84,11 +85,20 @@ class BranchForm extends Component
|
||||
|
||||
foreach ($roomLines as $line) {
|
||||
[$floor, $roomList] = explode(';', $line);
|
||||
$floor = (int) filter_var($floor, FILTER_SANITIZE_NUMBER_INT); // 抽出 1F 的數字
|
||||
|
||||
$roomNames = array_map('trim', explode(',', $roomList));
|
||||
|
||||
foreach ($roomNames as $roomName) {
|
||||
$type = match (true) {
|
||||
str_starts_with($roomName, 'svr') => RoomType::SVR,
|
||||
str_starts_with($roomName, 'pc') => RoomType::PC,
|
||||
default => RoomType::Unset,
|
||||
};
|
||||
$rooms[] = new Room([
|
||||
'name' => $roomName,
|
||||
'floor' => $floor,
|
||||
'type' => $type->value,
|
||||
'name' => preg_replace('/^(pc|svr)/', '', $roomName),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ namespace App\Livewire\Admin;
|
||||
|
||||
use App\Models\Room;
|
||||
use App\Models\Branch;
|
||||
use App\Enums\RoomType;
|
||||
|
||||
use Livewire\Component;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
@ -14,22 +15,33 @@ class RoomGrid extends Component
|
||||
protected $listeners = ['openModal','closeModal'];//,'refreshRooms' => '$refresh'
|
||||
|
||||
public bool $showModal = false;
|
||||
public int $branch_id = 0;
|
||||
public $branchName="";
|
||||
public Collection $rooms;
|
||||
public array $roomTypes;
|
||||
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->rooms = new Collection();
|
||||
$this->roomTypes = ['all' => '全部'] + collect(RoomType::cases())->mapWithKeys(fn($e) => [$e->value => $e->labels()])->toArray();
|
||||
}
|
||||
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->branch_id = $branch_id;
|
||||
$branch = Branch::find($branch_id);
|
||||
$this->branchName = Branch::find($branch_id)?->name ?? '';
|
||||
$this->showModal = true;
|
||||
}
|
||||
public function closeModal(){
|
||||
$this->showModal = false;
|
||||
$this->branch_id = 0;
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.admin.room-grid');
|
||||
$rooms = Room::where('branch_id', $this->branch_id)->get();
|
||||
// 取得樓層
|
||||
$floors = $rooms->pluck('floor')->unique()->sort()->values()->toArray();
|
||||
|
||||
return view('livewire.admin.room-grid',['rooms' =>$rooms,'floors' =>$floors]);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ use App\Traits\LogsModelActivity;
|
||||
* schema="Room",
|
||||
* type="object",
|
||||
* @OA\Property(property="id", type="integer", example=16),
|
||||
* @OA\Property(property="floor", type="integer", example="1"),
|
||||
* @OA\Property(property="type", ref="#/components/schemas/RoomType"),
|
||||
* @OA\Property(property="name", type="string", example="pc102"),
|
||||
* @OA\Property(property="internal_ip", type="string", example="192.168.11.7"),
|
||||
* @OA\Property(property="port", type="int", example="9000"),
|
||||
@ -25,6 +27,8 @@ class Room extends Model
|
||||
use HasFactory, LogsModelActivity;
|
||||
|
||||
protected $fillable = [
|
||||
'floor',
|
||||
'type',
|
||||
'name',
|
||||
'internal_ip',
|
||||
'port',
|
||||
@ -39,6 +43,8 @@ class Room extends Model
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'floor' => 'int',
|
||||
'type' => \App\Enums\RoomType::class,
|
||||
'name' => 'string',
|
||||
'internal_ip' =>'string',
|
||||
'port' => 'int',
|
||||
|
@ -52,6 +52,7 @@ class User extends Authenticatable
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
'api_plain_token',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -22,6 +22,7 @@ return new class extends Migration
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
$table->string('password');
|
||||
$table->rememberToken();
|
||||
$table->text('api_plain_token')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('FavoriteSongs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('songNumber');
|
||||
$table->string('userPhone', 10);
|
||||
$table->timestamps();
|
||||
});
|
||||
// 預塞一筆資料
|
||||
DB::table('FavoriteSongs')->insert([
|
||||
'songNumber' => 999996,
|
||||
'userPhone' => '0912345678',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('FavoriteSongs');
|
||||
}
|
||||
};
|
@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('user_song', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('song_id');
|
||||
$table->unsignedBigInteger('user_id');
|
||||
|
||||
$table->foreign('song_id')->references('id')->on('songs')->restrictOnDelete()->restrictOnUpdate();
|
||||
$table->foreign('user_id')->references('id')->on('users')->restrictOnDelete()->restrictOnUpdate();
|
||||
$table->primary(['song_id', 'user_id']);
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('user_song');
|
||||
}
|
||||
};
|
@ -14,6 +14,8 @@ return new class extends Migration
|
||||
Schema::create('rooms', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('branch_id')->constrained()->onDelete('cascade')->comment('關聯分店');
|
||||
$table->unsignedTinyInteger('floor')->default(1)->comment('樓層'); // 可根據實際狀況決定預設值
|
||||
$table->enum('type',['unset', 'pc','svr'])->default('unset')->comment('包廂類別');
|
||||
$table->string('name')->comment('包廂名稱');
|
||||
$table->string('internal_ip')->nullable()->comment('內部 IP');
|
||||
$table->unsignedSmallInteger('port')->nullable()->comment('通訊 Port');
|
||||
|
@ -21,10 +21,19 @@ class CreateAdminUserSeeder extends Seeder
|
||||
'password' => bcrypt('aa1234')
|
||||
]);
|
||||
$user->assignRole('Admin');
|
||||
$user = User::create([
|
||||
'name' => 'Allen Yan(machine)',
|
||||
'email' => 'MachineKTV@gmail.com',
|
||||
'phone' => '0900000001',
|
||||
'birthday' => now()->toDateString(),
|
||||
'password' => bcrypt('aa147258-')
|
||||
]);
|
||||
$user->assignRole('Machine');
|
||||
|
||||
$user = User::create([
|
||||
'name' => 'Allen Yan(User)',
|
||||
'email' => 'allen.yan@gmail.com',
|
||||
'phone' => '0900000001',
|
||||
'phone' => '0900000002',
|
||||
'birthday' => now()->toDateString(),
|
||||
'password' => bcrypt('aa1234')
|
||||
]);
|
||||
|
@ -40,6 +40,12 @@ class PermissionTableSeeder extends Seeder
|
||||
$adminRole = Role::firstOrCreate(['name' => 'Admin']);
|
||||
$adminRole->syncPermissions(Permission::all());
|
||||
|
||||
$machineRole = Role::firstOrCreate(['name' => 'Machine']);
|
||||
$machineRole->syncPermissions([
|
||||
'room-list',
|
||||
'room-create',
|
||||
'room-edit',
|
||||
]);
|
||||
// 建立 User 角色,不給任何權限
|
||||
Role::firstOrCreate(['name' => 'User']);
|
||||
}
|
||||
|
16
resources/views/components/room-card-svr.blade.php
Normal file
16
resources/views/components/room-card-svr.blade.php
Normal file
@ -0,0 +1,16 @@
|
||||
@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">
|
||||
<div class="font-bold">{{ $room->type->labels().".".$room->name }}</div>
|
||||
<div class="text-sm text-{{ $statusColors[$room->status->value] ?? 'gray-500' }} text-center">
|
||||
{{ $room->status->labels() }}
|
||||
</div>
|
||||
</div>
|
@ -10,7 +10,7 @@
|
||||
|
||||
<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="font-bold">{{ $room->type->labels().".".$room->name }}</div>
|
||||
<div class="text-sm text-{{ $statusColors[$room->status->value] ?? 'gray-500' }} text-center">
|
||||
{{ $room->status->labels() }}
|
||||
</div>
|
||||
|
@ -1,22 +1,59 @@
|
||||
<x-wireui:modal id="room-grid-modal" wire:model.defer="showModal" persistent >
|
||||
<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"
|
||||
>
|
||||
<button class="cursor-pointer p-1 rounded-full text-secondary-300 focus:ring-2 focus:ring-secondary-200" wire:click="closeModal">
|
||||
<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>
|
||||
<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 x-data="{ floor: '{{ $floors[0] ?? 1 }}', type: 'all' }">
|
||||
{{-- 樓層 Tab --}}
|
||||
<div class="flex gap-2 mb-2">
|
||||
@foreach($floors as $fl)
|
||||
<button
|
||||
class="px-3 py-1 rounded border"
|
||||
:class="floor === '{{ $fl }}' ? 'bg-blue-500 text-white' : 'bg-white text-gray-700'"
|
||||
x-on:click="floor = '{{ $fl }}'"
|
||||
>
|
||||
{{ $fl }}F
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
{{-- 類別 Tab --}}
|
||||
<div class="flex gap-2 mb-4">
|
||||
@foreach(['all' => '全部', 'pc' => 'PC', 'svr' => 'SVR'] as $value => $label)
|
||||
<button
|
||||
class="px-3 py-1 rounded border"
|
||||
:class="type === '{{ $value }}' ? 'bg-green-500 text-white' : 'bg-white text-gray-700'"
|
||||
x-on:click="type = '{{ $value }}'"
|
||||
>
|
||||
{{ $label }}
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
{{-- 房間卡片列表 --}}
|
||||
<div @if($showModal) wire:poll.5s @endif>
|
||||
<div class="grid grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4">
|
||||
@forelse($rooms as $room)
|
||||
<template x-if="floor == '{{ $room->floor }}' && (type == 'all' || type == '{{ $room->type }}')">
|
||||
<div>
|
||||
@if($room->type->value === \App\Enums\RoomType::SVR->value)
|
||||
<x-room-card-svr :room="$room" />
|
||||
@else
|
||||
<x-room-card :room="$room" />
|
||||
@endif
|
||||
</div>
|
||||
</template>
|
||||
@empty
|
||||
<div class="col-span-full text-center text-gray-500">尚無包廂資料</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<livewire:admin.room-detail-modal />
|
||||
</x-wireui:card>
|
||||
</x-wireui:modal>
|
@ -24,6 +24,8 @@ new class extends Component
|
||||
$token = $user->createToken($this->tokenName, $abilities);
|
||||
|
||||
$this->token = $token->plainTextToken;
|
||||
$user->api_plain_token=$this->token;
|
||||
$user->save();
|
||||
$this->loadTokens();
|
||||
|
||||
session()->flash('status', 'Token created!');
|
||||
|
Loading…
x
Reference in New Issue
Block a user