加入操作記錄與介面 20250512

This commit is contained in:
allen.yan 2025-05-12 14:13:56 +08:00
parent b0325aef79
commit 78bf426459
19 changed files with 363 additions and 21 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -0,0 +1,97 @@
<?php
namespace App\Livewire\Admin;
use App\Models\ActivityLog;
use Illuminate\Support\Carbon;
use Illuminate\Database\Eloquent\Builder;
use PowerComponents\LivewirePowerGrid\Column;
use PowerComponents\LivewirePowerGrid\Facades\Filter;
use PowerComponents\LivewirePowerGrid\Facades\PowerGrid;
use PowerComponents\LivewirePowerGrid\PowerGridFields;
use PowerComponents\LivewirePowerGrid\PowerGridComponent;
use Spatie\Activitylog\Models\Activity;
final class ActivityLogTable extends PowerGridComponent
{
public string $tableName = 'activity-log-table';
public function setUp(): array
{
$this->showCheckBox();
return [
PowerGrid::header()
->showSearchInput(),
PowerGrid::footer()
->showPerPage()
->showRecordCount(),
];
}
public function datasource(): Builder
{
return Activity::with(['causer'])->latest();
}
public function relationSearch(): array
{
return [];
}
public function fields(): PowerGridFields
{
return PowerGrid::fields()
->add('id')
->add('created_at_formatted', fn (Activity $model) => Carbon::parse($model->created_at)->format('Y-m-d H:i:s'))
->add('causer_name', fn (Activity $model) => optional($model->causer)->name)
->add('subject_type_label', fn (Activity $model) => class_basename($model->subject_type))
->add('subject_type')
->add('subject_id')
->add('description')
->add('log_name')
->add('changes', function (Activity $model) {
$old = $model->properties['old'] ?? [];
$new = $model->properties['attributes'] ?? [];
$changes = [];
foreach ($new as $key => $newValue) {
if (in_array($key, ['updated_at', 'created_at'])) continue;
$oldValue = $old[$key] ?? '(空)';
if ($newValue != $oldValue) {
$changes[] = "<strong>{$key}</strong>: {$oldValue}{$newValue}";
}
}
return implode('<br>', $changes);
})
;
}
public function columns(): array
{
$column=[];
$column[]=Column::make('時間', 'created_at_formatted', 'created_at')->sortable()->searchable();
$column[]=Column::make('操作者', 'causer.name')->sortable()->searchable()->bodyAttribute('whitespace-nowrap');
$column[]=Column::make('模型', 'subject_type_label')->sortable()->searchable();
$column[]=Column::make('模型 ID', 'subject_id')->sortable()->searchable();
$column[]=Column::make('動作', 'description')->sortable()->searchable();
$column[]=Column::make('變更內容', 'changes')->sortable(false)->searchable(false)->bodyAttribute('whitespace-normal text-sm text-gray-700');
$column[]=Column::make('Log 名稱', 'log_name')->sortable()->searchable();
return $column;
}
public function filters(): array
{
return [
Filter::datetimepicker('created_at'),
];
}
}

View File

@ -6,12 +6,12 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use App\Helpers\ChineseNameConverter; use App\Helpers\ChineseNameConverter;
use App\Helpers\ChineseStrokesConverter; use App\Helpers\ChineseStrokesConverter;
use App\Traits\LogsModelActivity;
class Artist extends Model class Artist extends Model
{ {
/** @use HasFactory<\Database\Factories\ArtistFactory> */ /** @use HasFactory<\Database\Factories\ArtistFactory> */
use HasFactory; use HasFactory, LogsModelActivity;
protected $fillable = [ protected $fillable = [
'category', 'category',
@ -29,7 +29,7 @@ class Artist extends Model
'category' => \App\Enums\ArtistCategory::class, 'category' => \App\Enums\ArtistCategory::class,
]; ];
} }
public function songs() { public function songs() {
return $this->belongsToMany(Song::class); return $this->belongsToMany(Song::class);
} }

View File

@ -4,15 +4,17 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use App\Traits\LogsModelActivity;
class Branch extends Model class Branch extends Model
{ {
/** @use HasFactory<\Database\Factories\ArtistFactory> */ /** @use HasFactory<\Database\Factories\ArtistFactory> */
use HasFactory; use HasFactory, LogsModelActivity;
protected $fillable = [ protected $fillable = [
'name', 'name',
'external_ip', 'external_ip',
'enable',
]; ];
public function rooms() { public function rooms() {

View File

@ -4,11 +4,12 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use App\Traits\LogsModelActivity;
class Room extends Model class Room extends Model
{ {
/** @use HasFactory<\Database\Factories\ArtistFactory> */ /** @use HasFactory<\Database\Factories\ArtistFactory> */
use HasFactory; use HasFactory, LogsModelActivity;
protected $casts = [ protected $casts = [
'started_at' => 'datetime', 'started_at' => 'datetime',

View File

@ -6,11 +6,12 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use App\Helpers\ChineseNameConverter; use App\Helpers\ChineseNameConverter;
use App\Helpers\ChineseStrokesConverter; use App\Helpers\ChineseStrokesConverter;
use App\Traits\LogsModelActivity;
class Song extends Model class Song extends Model
{ {
/** @use HasFactory<\Database\Factories\SongFactory> */ /** @use HasFactory<\Database\Factories\SongFactory> */
use HasFactory; use HasFactory, LogsModelActivity;
protected $fillable = [ protected $fillable = [
'id', 'id',

View File

@ -7,11 +7,12 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
use Spatie\Permission\Traits\HasRoles; use Spatie\Permission\Traits\HasRoles;
use App\Traits\LogsModelActivity;
class User extends Authenticatable class User extends Authenticatable
{ {
/** @use HasFactory<\Database\Factories\UserFactory> */ /** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable, HasRoles; use HasFactory, Notifiable, HasRoles, LogsModelActivity;
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.

View File

@ -0,0 +1,23 @@
<?php
namespace App\Traits;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;
trait LogsModelActivity
{
use LogsActivity;
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
->useLogName(strtolower(class_basename(static::class)))
->logOnly($this->getFillable())
->logOnlyDirty()
->dontSubmitEmptyLogs()
->setDescriptionForEvent(function (string $eventName) {
return class_basename(static::class) . "{$eventName}";
});
}
}

View File

@ -16,6 +16,7 @@
"overtrue/php-opencc": "^1.2", "overtrue/php-opencc": "^1.2",
"overtrue/pinyin": "^5.3", "overtrue/pinyin": "^5.3",
"power-components/livewire-powergrid": "^6.3", "power-components/livewire-powergrid": "^6.3",
"spatie/laravel-activitylog": "^4.10",
"spatie/laravel-permission": "^6.17", "spatie/laravel-permission": "^6.17",
"wire-elements/modal": "^2.0", "wire-elements/modal": "^2.0",
"wireui/wireui": "^2.4" "wireui/wireui": "^2.4"

93
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "d5d6344c1be46378bdaa0bfecd2e300e", "content-hash": "54bbfde233e690f726d54157b29f12a3",
"packages": [ "packages": [
{ {
"name": "brick/math", "name": "brick/math",
@ -4346,6 +4346,97 @@
], ],
"time": "2024-04-27T21:32:50+00:00" "time": "2024-04-27T21:32:50+00:00"
}, },
{
"name": "spatie/laravel-activitylog",
"version": "4.10.1",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-activitylog.git",
"reference": "466f30f7245fe3a6e328ad5e6812bd43b4bddea5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-activitylog/zipball/466f30f7245fe3a6e328ad5e6812bd43b4bddea5",
"reference": "466f30f7245fe3a6e328ad5e6812bd43b4bddea5",
"shasum": ""
},
"require": {
"illuminate/config": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0",
"illuminate/database": "^8.69 || ^9.27 || ^10.0 || ^11.0 || ^12.0",
"illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0",
"php": "^8.1",
"spatie/laravel-package-tools": "^1.6.3"
},
"require-dev": {
"ext-json": "*",
"orchestra/testbench": "^6.23 || ^7.0 || ^8.0 || ^9.0 || ^10.0",
"pestphp/pest": "^1.20 || ^2.0 || ^3.0"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Spatie\\Activitylog\\ActivitylogServiceProvider"
]
}
},
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Spatie\\Activitylog\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Freek Van der Herten",
"email": "freek@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
},
{
"name": "Sebastian De Deyne",
"email": "sebastian@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
},
{
"name": "Tom Witkowski",
"email": "dev.gummibeer@gmail.com",
"homepage": "https://gummibeer.de",
"role": "Developer"
}
],
"description": "A very simple activity logger to monitor the users of your website or application",
"homepage": "https://github.com/spatie/activitylog",
"keywords": [
"activity",
"laravel",
"log",
"spatie",
"user"
],
"support": {
"issues": "https://github.com/spatie/laravel-activitylog/issues",
"source": "https://github.com/spatie/laravel-activitylog/tree/4.10.1"
},
"funding": [
{
"url": "https://spatie.be/open-source/support-us",
"type": "custom"
},
{
"url": "https://github.com/spatie",
"type": "github"
}
],
"time": "2025-02-10T15:38:25+00:00"
},
{ {
"name": "spatie/laravel-package-tools", "name": "spatie/laravel-package-tools",
"version": "1.92.4", "version": "1.92.4",

52
config/activitylog.php Normal file
View File

@ -0,0 +1,52 @@
<?php
return [
/*
* If set to false, no activities will be saved to the database.
*/
'enabled' => env('ACTIVITY_LOGGER_ENABLED', true),
/*
* When the clean-command is executed, all recording activities older than
* the number of days specified here will be deleted.
*/
'delete_records_older_than_days' => 365,
/*
* If no log name is passed to the activity() helper
* we use this default log name.
*/
'default_log_name' => 'default',
/*
* You can specify an auth driver here that gets user models.
* If this is null we'll use the current Laravel auth driver.
*/
'default_auth_driver' => null,
/*
* If set to true, the subject returns soft deleted models.
*/
'subject_returns_soft_deleted_models' => false,
/*
* This model will be used to log activity.
* It should implement the Spatie\Activitylog\Contracts\Activity interface
* and extend Illuminate\Database\Eloquent\Model.
*/
'activity_model' => \Spatie\Activitylog\Models\Activity::class,
/*
* This is the name of the table that will be created by the migration and
* used by the Activity model shipped with this package.
*/
'table_name' => env('ACTIVITY_LOGGER_TABLE_NAME', 'activity_log'),
/*
* This is the database connection that will be used by the migration and
* the Activity model shipped with this package. In case it's not set
* Laravel's database.default will be used instead.
*/
'database_connection' => env('ACTIVITY_LOGGER_DB_CONNECTION'),
];

View File

@ -41,17 +41,6 @@ return [
'journal_mode' => null, 'journal_mode' => null,
'synchronous' => null, 'synchronous' => null,
], ],
'KTVsqlite' => [
'driver' => 'sqlite',
'url' => env('DB_URL'),
'database' => env('SQLITE_DB', database_path('KSongDatabase.db')),
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
'busy_timeout' => null,
'journal_mode' => null,
'synchronous' => null,
],
'mysql' => [ 'mysql' => [
'driver' => 'mysql', 'driver' => 'mysql',

View File

@ -0,0 +1,27 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateActivityLogTable extends Migration
{
public function up()
{
Schema::connection(config('activitylog.database_connection'))->create(config('activitylog.table_name'), function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('log_name')->nullable();
$table->text('description');
$table->nullableMorphs('subject', 'subject');
$table->nullableMorphs('causer', 'causer');
$table->json('properties')->nullable();
$table->timestamps();
$table->index('log_name');
});
}
public function down()
{
Schema::connection(config('activitylog.database_connection'))->dropIfExists(config('activitylog.table_name'));
}
}

View File

@ -0,0 +1,22 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddEventColumnToActivityLogTable extends Migration
{
public function up()
{
Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
$table->string('event')->nullable()->after('subject_type');
});
}
public function down()
{
Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
$table->dropColumn('event');
});
}
}

View File

@ -0,0 +1,22 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddBatchUuidColumnToActivityLogTable extends Migration
{
public function up()
{
Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
$table->uuid('batch_uuid')->nullable()->after('properties');
});
}
public function down()
{
Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
$table->dropColumn('batch_uuid');
});
}
}

View File

@ -0,0 +1,4 @@
<x-layouts.admin>
<livewire:admin.activity-log-table />
</x-layouts.admin>

View File

@ -3,6 +3,7 @@
$menus = [ $menus = [
['label' => 'Dashboard', 'route' => 'admin.dashboard', 'icon' => 'home', 'permission' => null], ['label' => 'Dashboard', 'route' => 'admin.dashboard', 'icon' => 'home', 'permission' => null],
['label' => 'ActivityLog', 'route' => 'admin.activity-log', 'icon' => 'clock', 'permission' => null],
['label' => 'Role', 'route' => 'admin.roles', 'icon' => 'user-circle', 'permission' => 'role-list'], ['label' => 'Role', 'route' => 'admin.roles', 'icon' => 'user-circle', 'permission' => 'role-list'],
['label' => 'User', 'route' => 'admin.users', 'icon' => 'user-circle', 'permission' => 'user-list'], ['label' => 'User', 'route' => 'admin.users', 'icon' => 'user-circle', 'permission' => 'user-list'],
['label' => 'Artist', 'route' => 'admin.artists', 'icon' => 'musical-note', 'permission' => 'song-list'], ['label' => 'Artist', 'route' => 'admin.artists', 'icon' => 'musical-note', 'permission' => 'song-list'],

View File

@ -21,7 +21,7 @@ require __DIR__.'/auth.php';
Route::middleware(['auth'])->prefix('admin')->name('admin.')->group(function () { Route::middleware(['auth'])->prefix('admin')->name('admin.')->group(function () {
Route::get('/dashboard', AdminDashboard::class)->name('dashboard'); Route::get('/dashboard', AdminDashboard::class)->name('dashboard');
Route::get('/activity-log', function () {return view('livewire.admin.activity-log');})->name('activity-log');
Route::get('/roles', function () {return view('livewire.admin.roles');})->name('roles'); Route::get('/roles', function () {return view('livewire.admin.roles');})->name('roles');
Route::get('/users', function () {return view('livewire.admin.users');})->name('users'); Route::get('/users', function () {return view('livewire.admin.users');})->name('users');
Route::get('/artists', function () {return view('livewire.admin.artists');})->name('artists'); Route::get('/artists', function () {return view('livewire.admin.artists');})->name('artists');

View File

@ -61,6 +61,7 @@ npm install && npm run build
php artisan convert:unihan-strokes \ php artisan convert:unihan-strokes \
--input=resources/data/Unihan_IRGSources.txt \ --input=resources/data/Unihan_IRGSources.txt \
--output=resources/data/unihan_strokes.php --output=resources/data/unihan_strokes.php
php artisan cache:forget unihan_strokes php artisan cache:forget unihan_strokes
@ -75,6 +76,13 @@ php artisan vendor:publish --tag=livewire-powergrid-config
composer require overtrue/php-opencc -vvv composer require overtrue/php-opencc -vvv
composer require overtrue/pinyin composer require overtrue/pinyin
操作記錄
composer require spatie/laravel-activitylog
php artisan vendor:publish --provider="Spatie\Activitylog\ActivitylogServiceProvider" --tag="activitylog-migrations"
php artisan migrate
php artisan vendor:publish --provider="Spatie\Activitylog\ActivitylogServiceProvider" --tag="activitylog-config"
php artisan make:model ActivityLog
建立分頁table 建立分頁table
php artisan powergrid:create php artisan powergrid:create