歌曲列表 20250425

This commit is contained in:
allen.yan 2025-04-25 18:21:20 +08:00
parent d7240465af
commit cbc3fc9d34
6 changed files with 231 additions and 39 deletions

View File

@ -9,7 +9,7 @@ use App\Models\ArtistCategory;
class ArtistForm extends Component
{
protected $listeners = ['openCreateArtistModal','openEditArtistModal', 'deleteArtist','bulkDeleteArtist'];
protected $listeners = ['openCreateArtistModal','openEditArtistModal', 'deleteArtist'];
public bool $showCreateModal = false;
public ?int $artistId = null;
@ -53,14 +53,14 @@ class ArtistForm extends Component
'name' => $this->name,
]);
session()->flash('message', '使用者已更新');
session()->flash('message', '歌手已更新');
} else {
$role = Artist::create([
'category_id' => $this->selectedCategory,
'name' => $this->name,
]);
session()->flash('message', '使用者已新增');
session()->flash('message', '歌手已新增');
}
$this->resetFields();
@ -70,7 +70,7 @@ class ArtistForm extends Component
public function deleteArtist($id)
{
Artist::findOrFail($id)->delete();
session()->flash('message', '使用者已刪除');
session()->flash('message', '歌手已刪除');
}
public function resetFields()

View File

@ -4,8 +4,98 @@ namespace App\Livewire\Admin;
use Livewire\Component;
use App\Models\Song;
use App\Models\SongCategory;
use App\Models\SongLanguage;
class SongForm extends Component
{
protected $listeners = ['openCreateSongModal','openEditSongModal', 'deleteSong'];
public bool $showCreateModal = false;
public $songCategories =[];
public $songLanguage =[];
public $selectedCategories=[];
public $selectedArtists=[];
public ?int $songId = null;
public array $fields = [
'id' =>'',
'name' => '',
'adddate' => '',
'filename' => '',
'language_type' => '',
'db_change' => '',
'vocal' => '',
'situation' => '',
'copyright01' => '',
'copyright02' => '',
'note01' => '',
'note02' => '',
'note03' => '',
'note04' => '',
'enable' => true,
];
protected $rules = [
'name' => 'required|string|max:255',
];
public function mount()
{
$this->songCategories = SongCategory::all();
$this->songLanguage = SongLanguage::all();
}
public function openCreateSongModal()
{
$this->resetFields();
$this->showCreateModal = true;
}
public function openEditSongModal($id)
{
$song = Song::findOrFail($id);
$this->songId = $song->id;
$this->fields = $song->only(array_keys($this->fields));
$this->showCreateModal = true;
}
public function save()
{
$this->validate();
if ($this->songId) {
$song = Song::findOrFail($this->songId);
$song->update($this->fields);
session()->flash('message', '歌曲已更新');
} else {
Song::create($this->fields);
session()->flash('message', '歌曲已新增');
}
$this->resetFields();
$this->showCreateModal = false;
}
public function deleteSong($id)
{
Song::findOrFail($id)->delete();
session()->flash('message', '歌曲已刪除');
}
public function resetFields()
{
foreach ($this->fields as $key => $value) {
$this->fields[$key] = is_bool($value) ? false : '';
}
$this->songId = null;
}
public function render()
{
return view('livewire.admin.song-form');

View File

@ -3,6 +3,7 @@
namespace App\Livewire\Admin;
use App\Models\Song;
use App\Models\SongLanguage;
use Illuminate\Support\Carbon;
use Illuminate\Database\Eloquent\Builder;
use PowerComponents\LivewirePowerGrid\Button;
@ -20,6 +21,10 @@ final class SongTable extends PowerGridComponent
{
public string $tableName = 'song-table';
/* public bool $deferLoading = true;
public string $loadingComponent = 'components.my-custom-loading'; */
public bool $showFilters = false;
public function boot(): void
@ -57,7 +62,10 @@ final class SongTable extends PowerGridComponent
public function datasource(): Builder
{
return Song::query();
return Song::query()->with([
'language:id,name',
'artists:id,name'
]);
}
public function relationSearch(): array
@ -70,9 +78,14 @@ final class SongTable extends PowerGridComponent
return PowerGrid::fields()
->add('id')
->add('name')
->add('adddate_formatted', fn (Song $model) => Carbon::parse($model->adddate)->format('d/m/Y'))
->add('simplified')
->add('phonetic_abbr')
->add('pinyin_abbr')
->add('filename')
->add('language_type')
->add('adddate_formatted', fn (Song $model) => Carbon::parse($model->adddate)->format('Y-m-d'))
->add('song_artists' ,fn(Song $model)=> $model->str_artists())
->add('language_type', fn (Song $model) => optional($model->language)->name)
->add('song_categories', fn(Song $model) => $model->str_categories())
->add('db_change')
->add('vocal')
->add('situation')
@ -82,12 +95,9 @@ final class SongTable extends PowerGridComponent
->add('note02')
->add('note03')
->add('note04')
->add('enable')
->add('enable', fn ($item) => $item->enable ? '✅' : '❌')
->add('unnamed_21')
->add('simplified')
->add('phonetic_abbr')
->add('pinyin_abbr')
->add('created_at_formatted', fn (Song $model) => Carbon::parse($model->created_at)->format('d/m/Y H:i:s'));
->add('created_at_formatted', fn (Song $model) => Carbon::parse($model->created_at)->format('Y-m-d H:i:s'));
}
public function columns(): array
@ -98,14 +108,32 @@ final class SongTable extends PowerGridComponent
->sortable()
->searchable(),
Column::make(__('songs.adddate'), 'adddate_formatted', 'adddate')
->sortable(),
Column::make(__('songs.simplified'), 'simplified')
->sortable()
->searchable()
->hidden(true, false),
Column::make(__('songs.name.phinetic'), 'phonetic_abbr')
->sortable()
->searchable()
->hidden(true, false),
Column::make(__('songs.name.pinyin'), 'pinyin_abbr')
->sortable()
->searchable()
->hidden(true, false),
Column::make(__('songs.filename'), 'filename')
->sortable()
->searchable(),
Column::make(__('songs.language_type'), 'language_type'),
Column::make(__('songs.adddate'), 'adddate_formatted', 'adddate')
->sortable(),
//歌手
Column::make(__('songs.artists'), 'song_artists'),
Column::make(__('songs.language_type'),'language_type', 'language.name')
->searchable(),
//分類
Column::make(__('songs.categorys'), 'song_categories'),
//點播次數
Column::make('Db change', 'db_change'),
Column::make(__('songs.vocal'), 'vocal'),
Column::make(__('songs.situation'), 'situation')
@ -118,7 +146,8 @@ final class SongTable extends PowerGridComponent
Column::make(__('songs.copyright02'), 'copyright02')
->sortable()
->searchable(),
->searchable()
->hidden(true, false),
Column::make(__('songs.note01'), 'note01')
->sortable()
@ -130,29 +159,19 @@ final class SongTable extends PowerGridComponent
Column::make(__('songs.note03'), 'note03')
->sortable()
->searchable(),
->searchable()
->hidden(true, false),
Column::make(__('songs.note04'), 'note04')
->sortable()
->searchable(),
->searchable()
->hidden(true, false),
Column::make(__('songs.enable'), 'enable'),
Column::make('Unnamed 21', 'unnamed_21')
->sortable()
->searchable(),
Column::make(__('songs.simplified'), 'simplified')
->sortable()
->searchable(),
Column::make(__('songs.name.phinetic'), 'phonetic_abbr')
->sortable()
->searchable(),
Column::make(__('songs.name.pinyin'), 'pinyin_abbr')
->sortable()
->searchable(),
->searchable()
->hidden(true, false),
Column::make('Created at', 'created_at_formatted', 'created_at')
->sortable(),
@ -163,7 +182,22 @@ final class SongTable extends PowerGridComponent
public function filters(): array
{
return [
Filter::inputText('name')->placeholder(__('songs.name')),
Filter::inputText('phonetic_abbr')->placeholder(__('songs.name.phinetic')),
Filter::inputText('pinyin_abbr')->placeholder(__('songs.name.pinyin_abbr')),
Filter::inputText('filename')->placeholder(__('songs.filename')),
Filter::datepicker('adddate'),
Filter::select('language_type', 'language_type')
->dataSource(SongLanguage::all())
->optionValue('id')
->optionLabel('name'),
Filter::inputText('copyright01')->placeholder(__('songs.copyright01')),
Filter::inputText('copyright02')->placeholder(__('songs.copyright02')),
Filter::inputText('note01')->placeholder(__('songs.note01')),
Filter::inputText('note02')->placeholder(__('songs.note02')),
Filter::inputText('note03')->placeholder(__('songs.note03')),
Filter::inputText('note04')->placeholder(__('songs.note04')),
Filter::boolean('enable')->label('✅', '❌'),
Filter::datetimepicker('created_at'),
];
}

View File

@ -32,17 +32,21 @@ class Song extends Model
'pinyin_abbr',
];
public function str_artists(){
return $this->artists->pluck('name')->implode(', ');
}
public function artists(){
return $this->belongsToMany(Artist::class);
}
public function str_categories(){
return $this->categories->pluck('name')->implode(', ');
}
public function categories(){
return $this->belongsToMany(SongCategory::class);
}
public function language(){
return $this->belongsTo(SongLanguage::class, 'languageType', 'id');
return $this->belongsTo(SongLanguage::class, 'language_type', 'id');
}
protected static function booted()
{

View File

@ -1,3 +1,59 @@
<div>
{{-- Be like water. --}}
</div>
<x-wireui:modal-card title="{{ $songId ? '編輯歌曲' : '新增歌曲' }}" wire:model.defer="showCreateModal">
<form wire:submit.prevent="save">
<x-wireui:toggle label="啟用" wire:model.defer="fields.enable" />
<div class="grid grid-cols-3 gap-4 sm:grid-cols-3">
<x-wireui:input label="歌曲編號" wire:model.defer="fields.id" required />
<x-wireui:input label="歌曲名稱" wire:model.defer="fields.name" required />
<x-wireui:input label="檔名" wire:model.defer="fields.filename" />
</div>
<div class="grid grid-cols-3 gap-4 sm:grid-cols-3">
<x-wireui:select
label="歌手"
wire:model.defer="selectedArtists"
placeholder="輸入搜尋歌手"
:async-data="route('api.artists.search')"
option-label="name"
option-value="id"
multiselect
hide-empty-message
/>
<x-wireui:input label="語言類型" wire:model.defer="fields.language_type" />
<x-wireui:select
label="分類"
wire:model.defer="selectedCategories"
:options="$songCategories->map(fn($c) => ['label' => $c->name, 'value' => $c->id])->toArray()"
option-label="label"
option-value="value"
multiselect
placeholder="選擇分類"
/>
</div>
<div class="grid grid-cols-3 gap-4 sm:grid-cols-3">
<x-wireui:input label="配唱" wire:model.defer="fields.vocal" />
<x-wireui:input label="情境" wire:model.defer="fields.situation" />
<x-wireui:input label="資料庫更新" wire:model.defer="fields.db_change" />
<x-wireui:input label="新增日期" wire:model.defer="fields.adddate" type="date" />
</div>
<div class="grid grid-cols-2 gap-4 sm:grid-cols-2">
<x-wireui:input label="版權1" wire:model.defer="fields.copyright01" />
<x-wireui:input label="版權2" wire:model.defer="fields.copyright02" />
</div>
<div class="grid grid-cols-2 gap-4 sm:grid-cols-4">
<x-wireui:textarea label="備註1" wire:model.defer="fields.note01" />
<x-wireui:textarea label="備註2" wire:model.defer="fields.note02" />
<x-wireui:textarea label="備註3" wire:model.defer="fields.note03" />
<x-wireui:textarea label="備註4" wire:model.defer="fields.note04" />
</div>
{{-- Footer --}}
<x-slot name="footer">
<div class="flex justify-between w-full">
<x-wireui:button flat label="取消" @click="$wire.showCreateModal = false" />
<x-wireui:button primary type="submit" label="儲存" />
</div>
</x-slot>
</form>
</x-wireui:modal-card>

View File

@ -19,6 +19,13 @@ Route::view('profile', 'profile')
->name('profile');
require __DIR__.'/auth.php';
Route::get('/api/artists/search', function (Request $request) {
return \App\Models\Artist::query()
->where('name', 'like', "%{$request->input('search')}%")
->limit(20)
->get(['id', 'name']);
})->name('api.artists.search');
Route::middleware(['auth'])->prefix('admin')->name('admin.')->group(function () {
Route::get('/dashboard', AdminDashboard::class)->name('dashboard');
@ -36,6 +43,7 @@ Route::middleware(['auth'])->prefix('admin')->name('admin.')->group(function ()
Route::get('/artists', function () {
return view('livewire.admin.artists');
})->name('artists');
Route::get('/artists-table', ArtistTable::class)->name('artists-table');
Route::get('/songs', function () {