完善字典表管理
parent
58954f3851
commit
e09755a197
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin;
|
||||||
|
|
||||||
|
use Slowlyo\OwlAdmin\Renderers\BaseRenderer;
|
||||||
|
|
||||||
|
class Components extends BaseRenderer {
|
||||||
|
|
||||||
|
public function parentControl($apiUrl = '', $name ='parent_id', $labelField = 'name', $valueField = 'id')
|
||||||
|
{
|
||||||
|
return amisMake()->TreeSelectControl()
|
||||||
|
->name('parent_id')->label('父级')
|
||||||
|
->showIcon(false)
|
||||||
|
->labelField($labelField)
|
||||||
|
->valueField($valueField)
|
||||||
|
->value(0)->source($apiUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,8 @@ use Slowlyo\OwlAdmin\Renderers\TableColumn;
|
||||||
use Slowlyo\OwlAdmin\Renderers\TextControl;
|
use Slowlyo\OwlAdmin\Renderers\TextControl;
|
||||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||||
use App\Services\Admin\KeywordService;
|
use App\Services\Admin\KeywordService;
|
||||||
|
use App\Admin\Components;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class KeywordController extends AdminController
|
class KeywordController extends AdminController
|
||||||
{
|
{
|
||||||
|
|
@ -18,16 +20,24 @@ class KeywordController extends AdminController
|
||||||
public function list(): Page
|
public function list(): Page
|
||||||
{
|
{
|
||||||
$crud = $this->baseCRUD()
|
$crud = $this->baseCRUD()
|
||||||
|
//关闭查询
|
||||||
->filterTogglable(false)
|
->filterTogglable(false)
|
||||||
|
//去掉分页-start
|
||||||
|
->loadDataOnce(true)
|
||||||
|
->footerToolbar([])
|
||||||
|
//去掉分页-end
|
||||||
->headerToolbar([
|
->headerToolbar([
|
||||||
$this->createButton(true),
|
$this->createButton(true),
|
||||||
...$this->baseHeaderToolBar(),
|
amis('reload')->align('right'),
|
||||||
|
amis('filter-toggler')->align('right'),
|
||||||
])
|
])
|
||||||
->columns([
|
->columns([
|
||||||
TableColumn::make()->name('id')->label('ID')->sortable(true),
|
// TableColumn::make()->name('id')->label('ID')->sortable(true),
|
||||||
|
TableColumn::make()->name('name')->label('名称'),
|
||||||
|
TableColumn::make()->name('key')->label('KEY'),
|
||||||
|
TableColumn::make()->name('value')->label('值'),
|
||||||
TableColumn::make()->name('created_at')->label('创建时间')->type('datetime')->sortable(true),
|
TableColumn::make()->name('created_at')->label('创建时间')->type('datetime')->sortable(true),
|
||||||
TableColumn::make()->name('updated_at')->label('更新时间')->type('datetime')->sortable(true),
|
$this->rowActionsOnlyEditAndDelete(true),
|
||||||
$this->rowActions(true),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->baseList($crud);
|
return $this->baseList($crud);
|
||||||
|
|
@ -36,18 +46,15 @@ class KeywordController extends AdminController
|
||||||
public function form(): Form
|
public function form(): Form
|
||||||
{
|
{
|
||||||
return $this->baseForm()->body([
|
return $this->baseForm()->body([
|
||||||
TextControl::make()->name('name')->label('名称')->required(),
|
Components::make()->parentControl(admin_url('api/keywords/tree-list')),
|
||||||
|
TextControl::make()->name('name')->label('名称')->required(true),
|
||||||
|
TextControl::make()->name('key')->label('KEY')->required(true),
|
||||||
|
TextControl::make()->name('value')->label('值')->required(true),
|
||||||
|
amisMake()->NumberControl()->name('sort')->value(0)->min()->label('排序'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function detail(): Form
|
public function getTreeList(Request $request){
|
||||||
{
|
return $this->service->getTree();
|
||||||
return $this->baseDetail()->body([
|
}
|
||||||
TextControl::make()->static(true)->name('id')->label('ID'),
|
|
||||||
TextControl::make()->static(true)->name('created_at')->label('创建时间'),
|
|
||||||
TextControl::make()->static(true)->name('updated_at')->label('更新时间')
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,11 @@ Route::group([
|
||||||
'prefix' => config('admin.route.prefix'),
|
'prefix' => config('admin.route.prefix'),
|
||||||
'middleware' => config('admin.route.middleware'),
|
'middleware' => config('admin.route.middleware'),
|
||||||
], function (Router $router) {
|
], function (Router $router) {
|
||||||
|
$router->group([
|
||||||
|
'prefix' => 'api',
|
||||||
|
], function (Router $router) {
|
||||||
|
$router->get('keywords/tree-list', '\App\Admin\Controllers\KeywordController@getTreeList')->name('api.keywords.tree-list');
|
||||||
|
});
|
||||||
|
|
||||||
$router->resource('dashboard', \App\Admin\Controllers\HomeController::class);
|
$router->resource('dashboard', \App\Admin\Controllers\HomeController::class);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,44 @@ 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 EloquentFilter\Filterable;
|
||||||
|
|
||||||
class Keyword extends Model
|
class Keyword extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
use Filterable;
|
||||||
|
|
||||||
|
protected $fillable = ['name', 'key', 'value', 'parent_id', 'type_key', 'path', 'sort', 'level'];
|
||||||
|
|
||||||
|
protected static function boot()
|
||||||
|
{
|
||||||
|
parent::boot();
|
||||||
|
// 监听 Keyword 的创建事件,用于初始化 path 和 level 字段值
|
||||||
|
static::creating(function ($keyword) {
|
||||||
|
// 如果创建的是一个根类目
|
||||||
|
if (! $keyword->parent_id) {
|
||||||
|
// 将层级设为 1
|
||||||
|
$keyword->level = 1;
|
||||||
|
// 将 path 设为 -
|
||||||
|
$keyword->path = '-';
|
||||||
|
} else {
|
||||||
|
// 将层级设为父类目的层级 + 1
|
||||||
|
$keyword->level = $keyword->parent->level + 1;
|
||||||
|
$keyword->type_key = $keyword->parent->key;
|
||||||
|
// 将 path 值设为父类目的 path 追加父类目 ID 以及最后跟上一个 - 分隔符
|
||||||
|
$keyword->path = $keyword->parent->path.$keyword->parent_id.'-';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parent()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(static::class, 'parent_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function children()
|
||||||
|
{
|
||||||
|
return $this->hasMany(static::class, 'parent_id');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Database\Events\QueryExecuted;
|
||||||
|
use Illuminate\Database\Events\TransactionBeginning;
|
||||||
|
use Illuminate\Database\Events\TransactionCommitted;
|
||||||
|
use Illuminate\Database\Events\TransactionRolledBack;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
|
class QueryLoggerServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
if (! config('app.debug')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->app['events']->listen([
|
||||||
|
QueryExecuted::class,
|
||||||
|
TransactionBeginning::class,
|
||||||
|
TransactionCommitted::class,
|
||||||
|
TransactionRolledBack::class,
|
||||||
|
], function ($event) {
|
||||||
|
Log::debug(match (true) {
|
||||||
|
$event instanceof TransactionBeginning => 'begin transaction',
|
||||||
|
$event instanceof TransactionCommitted => 'commit transaction',
|
||||||
|
$event instanceof TransactionRolledBack => 'rollback transaction',
|
||||||
|
default => $this->prepareSql($event),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Illuminate\Database\Events\QueryExecuted $query
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function prepareSql(QueryExecuted $query): string
|
||||||
|
{
|
||||||
|
$sql = str_replace(['%', '?'], ['%%', '%s'], $query->sql);
|
||||||
|
|
||||||
|
$bindings = $query->connection->prepareBindings($query->bindings);
|
||||||
|
|
||||||
|
if (count($bindings)) {
|
||||||
|
$sql = vsprintf($sql, array_map([$query->connection->getPdo(), 'quote'], $bindings));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf('[%s] %s', $this->formatDuration($query->time), $sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param float $milliseconds
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function formatDuration($milliseconds): string
|
||||||
|
{
|
||||||
|
return match (true) {
|
||||||
|
$milliseconds >= 1000 => round($milliseconds / 1000, 2).'s',
|
||||||
|
$milliseconds < 0.01 => round($milliseconds * 1000).'μs',
|
||||||
|
default => $milliseconds.'ms',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Services\Admin;
|
namespace App\Services\Admin;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
use App\Models\Keyword;
|
use App\Models\Keyword;
|
||||||
use Slowlyo\OwlAdmin\Services\AdminService;
|
use Slowlyo\OwlAdmin\Services\AdminService;
|
||||||
|
|
||||||
|
|
@ -12,4 +13,102 @@ use Slowlyo\OwlAdmin\Services\AdminService;
|
||||||
class KeywordService extends AdminService
|
class KeywordService extends AdminService
|
||||||
{
|
{
|
||||||
protected string $modelName = Keyword::class;
|
protected string $modelName = Keyword::class;
|
||||||
|
|
||||||
|
public function getTree()
|
||||||
|
{
|
||||||
|
$list = $this->query()->orderByDesc('sort')->get()->toArray();
|
||||||
|
return array2tree($list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parentIsChild($id, $parent_id): bool
|
||||||
|
{
|
||||||
|
$parent = $this->query()->find($parent_id);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if ($parent->parent_id == $id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 如果没有parent 则为顶级 退出循环
|
||||||
|
$parent = $parent->parent;
|
||||||
|
} while ($parent);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function list()
|
||||||
|
{
|
||||||
|
return ['items' => $this->getTree()];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store($data): bool
|
||||||
|
{
|
||||||
|
if ($this->hasRepeated($data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$columns = $this->getTableColumns();
|
||||||
|
|
||||||
|
$model = $this->getModel();
|
||||||
|
|
||||||
|
foreach ($data as $k => $v) {
|
||||||
|
if (!in_array($k, $columns)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$model->setAttribute($k, $v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $model->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update($primaryKey, $data): bool
|
||||||
|
{
|
||||||
|
if ($this->hasRepeated($data, $primaryKey)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$columns = $this->getTableColumns();
|
||||||
|
|
||||||
|
$parent_id = Arr::get($data, 'parent_id');
|
||||||
|
if ($parent_id != 0) {
|
||||||
|
if ($this->parentIsChild($primaryKey, $parent_id)) {
|
||||||
|
$this->setError('父级不允许设置为当前子权限');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$model = $this->query()->whereKey($primaryKey)->first();
|
||||||
|
|
||||||
|
foreach ($data as $k => $v) {
|
||||||
|
if (!in_array($k, $columns)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$model->setAttribute($k, $v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $model->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasRepeated($data, $id = 0): bool
|
||||||
|
{
|
||||||
|
$query = $this->query()->when($id, fn($query) => $query->where('id', '<>', $id));
|
||||||
|
|
||||||
|
if ((clone $query)->where('key', $data['key'])->exists()) {
|
||||||
|
$this->setError('KEY重复');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(string $ids): mixed
|
||||||
|
{
|
||||||
|
$ids = explode(',', $ids);
|
||||||
|
if(count($ids) == 1){
|
||||||
|
$this->query()->where('path', 'like', '%-'.$ids[0].'-%')->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->query()->whereIn('id', $ids)->delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@
|
||||||
"laravel/framework": "^9.19",
|
"laravel/framework": "^9.19",
|
||||||
"laravel/sanctum": "^3.0",
|
"laravel/sanctum": "^3.0",
|
||||||
"laravel/tinker": "^2.7",
|
"laravel/tinker": "^2.7",
|
||||||
"slowlyo/owl-admin": "^2.1"
|
"slowlyo/owl-admin": "^2.1",
|
||||||
|
"tucker-eric/eloquentfilter": "^3.2"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"fakerphp/faker": "^1.9.1",
|
"fakerphp/faker": "^1.9.1",
|
||||||
|
|
@ -66,7 +67,7 @@
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"packagist": {
|
"packagist": {
|
||||||
"type": "composer",
|
"type": "composer",
|
||||||
"url": "https://packagist.phpcomposer.com"
|
"url": "https://mirrors.aliyun.com/composer/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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": "a5b72c385ebe487317edadb68fb376ab",
|
"content-hash": "0955c00d24fb997149c86a6cf19f653a",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
|
|
@ -3983,6 +3983,77 @@
|
||||||
"homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
|
"homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
|
||||||
"time": "2023-01-03T09:29:04+00:00"
|
"time": "2023-01-03T09:29:04+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "tucker-eric/eloquentfilter",
|
||||||
|
"version": "3.2.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/Tucker-Eric/EloquentFilter.git",
|
||||||
|
"reference": "faaad783b7f23af7ba7e23baaa56d71af51504a9"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/Tucker-Eric/EloquentFilter/zipball/faaad783b7f23af7ba7e23baaa56d71af51504a9",
|
||||||
|
"reference": "faaad783b7f23af7ba7e23baaa56d71af51504a9",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"illuminate/config": "~6.0|~7.0|~8.0|~9.0|~10.0",
|
||||||
|
"illuminate/console": "~6.0|~7.0|~8.0|~9.0|~10.0",
|
||||||
|
"illuminate/database": "~6.0|~7.0|~8.0|~9.0|~10.0",
|
||||||
|
"illuminate/filesystem": "~6.0|~7.0|~8.0|~9.0|~10.0",
|
||||||
|
"illuminate/pagination": "~6.0|~7.0|~8.0|~9.0|~10.0",
|
||||||
|
"illuminate/support": "~6.0|~7.0|~8.0|~9.0|~10.0",
|
||||||
|
"php": ">=7.2"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"mockery/mockery": "^1.3",
|
||||||
|
"phpunit/phpunit": "^8"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"EloquentFilter\\ServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"EloquentFilter\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Eric Tucker",
|
||||||
|
"email": "tucker.ericm@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "An Eloquent way to filter Eloquent Models",
|
||||||
|
"keywords": [
|
||||||
|
"eloquent",
|
||||||
|
"filter",
|
||||||
|
"laravel",
|
||||||
|
"model",
|
||||||
|
"query",
|
||||||
|
"search"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/Tucker-Eric/EloquentFilter/issues",
|
||||||
|
"source": "https://github.com/Tucker-Eric/EloquentFilter/tree/3.2.0"
|
||||||
|
},
|
||||||
|
"time": "2023-02-07T18:34:53+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "vlucas/phpdotenv",
|
"name": "vlucas/phpdotenv",
|
||||||
"version": "v5.5.0",
|
"version": "v5.5.0",
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ return [
|
||||||
// App\Providers\BroadcastServiceProvider::class,
|
// App\Providers\BroadcastServiceProvider::class,
|
||||||
App\Providers\EventServiceProvider::class,
|
App\Providers\EventServiceProvider::class,
|
||||||
App\Providers\RouteServiceProvider::class,
|
App\Providers\RouteServiceProvider::class,
|
||||||
|
App\Providers\QueryLoggerServiceProvider::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ return new class extends Migration
|
||||||
$table->unsignedInteger('sort')->default(0)->comment('排序');
|
$table->unsignedInteger('sort')->default(0)->comment('排序');
|
||||||
$table->unsignedBigInteger('parent_id')->default(0)->comment('上级ID');
|
$table->unsignedBigInteger('parent_id')->default(0)->comment('上级ID');
|
||||||
$table->unsignedInteger('level')->default(1)->comment('层级');
|
$table->unsignedInteger('level')->default(1)->comment('层级');
|
||||||
|
$table->string('path')->default('-')->comment('所有的父级ID');
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue