From 611721b700b278b2d7e85f4fd67ccd50c60694c9 Mon Sep 17 00:00:00 2001 From: panliang <1163816051@qq.com> Date: Mon, 8 Jul 2024 09:47:23 +0800 Subject: [PATCH] admin keywords.resource --- .env.example | 4 +- app/Admin/Components.php | 21 +++ app/Admin/Controllers/HomeController.php | 16 ++ app/Admin/Controllers/KeywordController.php | 126 +++++++++---- app/Admin/routes.php | 16 +- app/Casts/StorageFile.php | 51 ++++++ app/ModelFilters/KeywordFilter.php | 56 ++++++ app/Models/Keyword.php | 13 +- app/Providers/QueryLogServiceProvider.php | 93 ++++++++++ app/Services/KeywordService.php | 35 ++++ app/Traits/TreePath.php | 84 +++++++++ bootstrap/providers.php | 1 + composer.json | 9 +- composer.lock | 185 ++++++++++++++++++-- config/admin.php | 14 +- config/filesystems.php | 2 +- database/seeders/AdminPermissionSeeder.php | 5 +- database/seeders/DatabaseSeeder.php | 1 + database/seeders/KeywordSeeder.php | 64 +++++++ lang/zh_CN/keywords.php | 3 +- 20 files changed, 722 insertions(+), 77 deletions(-) create mode 100644 app/Admin/Components.php create mode 100644 app/Casts/StorageFile.php create mode 100644 app/ModelFilters/KeywordFilter.php create mode 100644 app/Providers/QueryLogServiceProvider.php create mode 100644 app/Traits/TreePath.php create mode 100644 database/seeders/KeywordSeeder.php diff --git a/.env.example b/.env.example index b5b72d5..8ec9442 100644 --- a/.env.example +++ b/.env.example @@ -33,8 +33,8 @@ SESSION_PATH=/ SESSION_DOMAIN=null BROADCAST_CONNECTION=log -FILESYSTEM_DISK=local -QUEUE_CONNECTION=database +FILESYSTEM_DISK=public +QUEUE_CONNECTION=sync CACHE_STORE=file CACHE_PREFIX= diff --git a/app/Admin/Components.php b/app/Admin/Components.php new file mode 100644 index 0000000..0517ee9 --- /dev/null +++ b/app/Admin/Components.php @@ -0,0 +1,21 @@ +BaseApi()->url(admin_url('api/keywords/tree-list'))->data($params); + return amis()->TreeSelectControl()->source($url)->labelField('name')->valueField('id'); + } +} diff --git a/app/Admin/Controllers/HomeController.php b/app/Admin/Controllers/HomeController.php index 6af3b02..aef999f 100644 --- a/app/Admin/Controllers/HomeController.php +++ b/app/Admin/Controllers/HomeController.php @@ -5,6 +5,7 @@ namespace App\Admin\Controllers; use Slowlyo\OwlAdmin\Admin; use Illuminate\Http\JsonResponse; use Illuminate\Http\Resources\Json\JsonResource; +use Illuminate\Support\Facades\Storage; use Slowlyo\OwlAdmin\Controllers\AdminController; class HomeController extends AdminController @@ -16,6 +17,21 @@ class HomeController extends AdminController return $this->response()->success($page); } + protected function upload($type = 'file') + { + $file = request()->file('file'); + + if (! $file) { + return $this->response()->fail(__('admin.upload_file_error')); + } + + $disk = request()->input('disk', Admin::config('admin.upload.disk')); + + $path = $file->store(Admin::config('admin.upload.directory.'.$type), $disk); + + return $this->response()->success(['value' => Storage::disk($disk)->url($path), 'path' => $path]); + } + public function frameworkInfo() { $link = function ($label, $link) { diff --git a/app/Admin/Controllers/KeywordController.php b/app/Admin/Controllers/KeywordController.php index 2756077..a7fd9b5 100644 --- a/app/Admin/Controllers/KeywordController.php +++ b/app/Admin/Controllers/KeywordController.php @@ -2,7 +2,10 @@ namespace App\Admin\Controllers; +use App\Admin\Components; +use App\Models\Keyword; use App\Services\KeywordService; +use Illuminate\Http\Request; use Slowlyo\OwlAdmin\Admin; use Slowlyo\OwlAdmin\Controllers\AdminController; @@ -16,49 +19,100 @@ class KeywordController extends AdminController protected string $serviceName = KeywordService::class; public function list() - { + { $user = Admin::user(); - $crud = $this->baseCRUD() - ->filterTogglable(false) - ->bulkActions([]) - ->columnsTogglable(false) + $crud = $this->baseCRUD() + ->filterTogglable(false) + ->columnsTogglable(false) + ->loadDataOnce(true) + ->footerToolbar([]) ->headerToolbar([ - $this->createButton()->visible($user->can('keywords.create')), - ...$this->baseHeaderToolBar(), + $this->createButton()->visible($user->can('keywords.create')), + $this->exportAction()->visible($user->can('keywords.export')), + ...$this->baseHeaderToolBar(), ]) - ->columns([ - amis()->TableColumn('id', 'ID')->sortable(), - amis()->TableColumn('key', 'KEY'), - amis()->TableColumn('name', '名称'), - amis()->TableColumn('value', '值'), - $this->rowActions([ + ->filter($this->baseFilter()->body([ + amis()->GroupControl()->mode('horizontal')->body([ + Components::make()->keywordTreeSelectControl()->name('path')->label(__('keywords.parent_id'))->columnRatio(3)->clearable(), + ]) + ])) + ->columns([ + amis()->TableColumn()->name('id')->label(__('keywords.id')), + amis()->TableColumn()->name('name')->label(__('keywords.name')), + amis()->TableColumn()->name('key')->label(__('keywords.key')), + amis()->TableColumn()->name('value')->label(__('keywords.value')), + $this->rowActions([ $this->rowShowButton()->visible($user->can('keywords.show')), $this->rowEditButton()->visible($user->can('keywords.edit')), $this->rowDeleteButton()->visible($user->can('keywords.delete')), - ]) - ]); + ]), + ]); - return $this->baseList($crud); - } + return $this->baseList($crud); + } - public function form($isEdit = false) - { - return $this->baseForm()->body([ - amis()->TextControl('key', 'KEY'), - amis()->TextControl('name', '名称'), - amis()->TextControl('value', '值'), - ]); - } + public function form() + { + return $this->baseForm()->mode('horizontal')->body([ + Components::make()->keywordTreeSelectControl()->name('parent_id')->label(__('keywords.parent_id')), + amis()->TextControl()->name('name')->label(__('keywords.name'))->required(true), + amis()->TextControl()->name('key')->label(__('keywords.key'))->required(true), + amis()->TextControl()->name('value')->label(__('keywords.value')), + amis()->ImageControl()->name('image')->label(__('keywords.image'))->autoUpload(true), + amis()->ImageControl()->name('images')->label(__('keywords.images'))->multiple()->autoUpload(true), + amis()->NumberControl()->min(0)->precision(0)->name('sort')->label(__('keywords.sort')), + amis()->TextareaControl()->name('description')->label(__('keywords.description')), + amis()->inputKV()->name('options')->label(__('keywords.options')), + amis()->WangEditor()->height('auto')->name('content')->label(__('keywords.content')), + ]); + } - public function detail() - { - return $this->baseDetail()->body([ - amis()->TextControl('id', 'ID')->static(), - amis()->TextControl('key', 'KEY')->static(), - amis()->TextControl('name', '名称')->static(), - amis()->TextControl('value', '值')->static(), - amis()->TextControl('created_at', admin_trans('admin.created_at'))->static(), - amis()->TextControl('updated_at', admin_trans('admin.updated_at'))->static(), - ]); - } + public function detail() + { + return $this->baseDetail()->body(amis()->Property()->items([ + ['label' => __('keywords.parent_id'), 'content' => '${parent.name}', 'span' => 3], + + ['label' => __('keywords.name'), 'content' => '${name}'], + ['label' => __('keywords.key'), 'content' => '${key}'], + ['label' => __('keywords.value'), 'content' => '${value}'], + + ['label' => __('keywords.image'), 'content' => amis()->Image()->name('image'), 'span' => 3], + ['label' => __('keywords.images'), 'content' => amis()->Images()->name('images'), 'span' => 3], + ['label' => __('keywords.sort'), 'content' => '${sort}', 'span' => 3], + ['label' => __('keywords.description'), 'content' => '${description}', 'span' => 3], + ['label' => __('keywords.options'), 'content' => amis()->Json()->name('options'), 'span' => 3], + ['label' => __('keywords.content'), 'content' => amis()->WangEditor()->height('auto')->name('content')->label(false)->static(), 'span' => 3], + ])); + } + + public function update(Request $request) + { + $result = $this->service->update($request->route()->parameter('keyword'), $request->all()); + + return $this->autoResponse($result, __('admin.save')); + } + + public function getTreeList(Request $request) + { + return $this->service->getTree($request->all()); + } + + public function getList(Request $request) + { + $list = Keyword::filter($request->all())->select(['id as value', 'name as label'])->get(); + + return $this->response()->success($list); + } + + // protected function exportFileName() + // { + // return '此处为导出文件名'; + // } + + // protected function exportMap($row) + // { + // return [ + // 'id' => __('keywords.id') + // ]; + // } } \ No newline at end of file diff --git a/app/Admin/routes.php b/app/Admin/routes.php index 99a318f..b48b8ca 100644 --- a/app/Admin/routes.php +++ b/app/Admin/routes.php @@ -1,5 +1,7 @@ config('admin.route.prefix'), 'middleware' => config('admin.route.middleware'), ], function (Router $router) { - $router->get('dashboard', [\App\Admin\Controllers\HomeController::class, 'index'])->name('dashboard'); + $router->group([ + 'prefix' => 'api', + ], function (Router $router) { + $router->get('keywords/tree-list', [KeywordController::class, 'getTreeList']); + $router->get('keywords/list', [KeywordController::class, 'getList']); + }); + + $router->get('dashboard', [HomeController::class, 'index'])->name('dashboard'); $router->resource('system/settings', \App\Admin\Controllers\SettingController::class)->only(['index', 'store']); - $router->resource('system/keywords', \App\Admin\Controllers\KeywordController::class); + $router->any('upload_image', [HomeController::class, 'uploadImage']); + $router->any('upload_file', [HomeController::class, 'uploadFile']); + + $router->resource('system/keywords', KeywordController::class); }); diff --git a/app/Casts/StorageFile.php b/app/Casts/StorageFile.php new file mode 100644 index 0000000..0150df5 --- /dev/null +++ b/app/Casts/StorageFile.php @@ -0,0 +1,51 @@ +disk = $disk; + } + + /** + * Cast the given value. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @param string $key + * @param mixed $value + * @param array $attributes + * @return array + */ + public function get($model, $key, $value, $attributes) + { + return $value ? (Str::startsWith($value, ['http://', 'https://']) ? $value : Storage::disk($this->disk)->url($value)) : null; + } + + /** + * Prepare the given value for storage. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @param string $key + * @param array $value + * @param array $attributes + * @return string + */ + public function set($model, $key, $value, $attributes) + { + return $value; + } +} diff --git a/app/ModelFilters/KeywordFilter.php b/app/ModelFilters/KeywordFilter.php new file mode 100644 index 0000000..7b8dad5 --- /dev/null +++ b/app/ModelFilters/KeywordFilter.php @@ -0,0 +1,56 @@ + [input_key1, input_key2]]. + * + * @var array + */ + public $relations = []; + + public function parent($id) + { + $this->where('parent_id', $id); + } + + public function key($key) + { + $this->whereLike('key', $key); + } + + public function name($key) + { + $this->whereLike('name', $key); + } + + public function parentKey($key) + { + $id = Keyword::where('key', $key)->value('id'); + $this->path($id); + } + + public function parentName($key) + { + $id = Keyword::where('name', $key)->value('id'); + if ($id) { + $this->parent($id); + } + } + + public function path($key) + { + $this->whereLike('path', '-'.$key.'-'); + } + + public function level($key) + { + $this->where('level', $key); + } +} diff --git a/app/Models/Keyword.php b/app/Models/Keyword.php index 4c2ed03..1d38b2a 100644 --- a/app/Models/Keyword.php +++ b/app/Models/Keyword.php @@ -2,11 +2,22 @@ namespace App\Models; -use Slowlyo\OwlAdmin\Models\BaseModel as Model; +use Illuminate\Database\Eloquent\Model; +use Slowlyo\OwlAdmin\Traits\DatetimeFormatterTrait; +use EloquentFilter\Filterable; +use App\Traits\TreePath; /** * 字典管理 */ class Keyword extends Model { + use DatetimeFormatterTrait, Filterable, TreePath; + + protected $fillable = ['id', 'name', 'key', 'value', 'parent_id', 'path', 'sort', 'level', 'options', 'image', 'images', 'description', 'content']; + + protected $casts = [ + 'options' => 'array', + 'images' => 'array', + ]; } \ No newline at end of file diff --git a/app/Providers/QueryLogServiceProvider.php b/app/Providers/QueryLogServiceProvider.php new file mode 100644 index 0000000..9d07cdb --- /dev/null +++ b/app/Providers/QueryLogServiceProvider.php @@ -0,0 +1,93 @@ +app['events']->listen([ + QueryExecuted::class, + TransactionBeginning::class, + TransactionCommitted::class, + TransactionRolledBack::class, + ], function ($event) { + if ($event instanceof TransactionBeginning) { + return $this->writeLog('begin transaction'); + } elseif ($event instanceof TransactionCommitted) { + return $this->writeLog('commit transaction'); + } elseif ($event instanceof TransactionRolledBack) { + return $this->writeLog('rollback transaction'); + } + + $this->writeLog($this->prepareSql($event)); + }); + } + + /** + * 准备 SQL 语句 + */ + protected function prepareSql(QueryExecuted $query): string + { + $sql = str_replace(['%', '?'], ['%%', '%s'], $query->sql); + + if (count($bindings = $query->connection->prepareBindings($query->bindings))) { + $sql = vsprintf($sql, array_map([$query->connection->getPdo(), 'quote'], $bindings)); + } + + return sprintf('[%s] %s', $this->formatDuration($query->time), $sql); + } + + /** + * 格式化时间 + * + * @param float $milliseconds + */ + protected function formatDuration($milliseconds): string + { + if ($milliseconds < 0.01) { + return round($milliseconds * 1000).'μs'; + } elseif ($milliseconds >= 1000) { + return round($milliseconds / 1000, 2).'s'; + } + + return $milliseconds.'ms'; + } + + /** + * 将消息记录到日志中 + * + * @param string $message + * @return void + */ + protected function writeLog($message, array $context = []) + { + Log::debug($message, $context); + } +} diff --git a/app/Services/KeywordService.php b/app/Services/KeywordService.php index dd84bfe..6dcac18 100644 --- a/app/Services/KeywordService.php +++ b/app/Services/KeywordService.php @@ -14,4 +14,39 @@ use Slowlyo\OwlAdmin\Services\AdminService; class KeywordService extends AdminService { protected string $modelName = Keyword::class; + + public function getTree($filters = []) + { + $list = $this->query()->filter($filters)->sort()->get(); + + return array2tree($list->toArray(), $list->min('parent_id') ?: 0); + } + + public function list() + { + return ['items' => $this->getTree(request()->all())]; + } + + public function addRelations($query, string $scene = 'list') + { + if ($scene == 'detail') { + $query->with(['parent']); + } + } + + public function query() + { + return $this->modelName::query()->sort(); + } + + public function deleted($ids) + { + $ids = explode(',', $ids); + + $this->query()->where(function ($q) use ($ids) { + foreach ($ids as $id) { + $q->orWhere('path', 'like', '%-'.$id.'-%'); + } + })->delete(); + } } \ No newline at end of file diff --git a/app/Traits/TreePath.php b/app/Traits/TreePath.php new file mode 100644 index 0000000..61aff97 --- /dev/null +++ b/app/Traits/TreePath.php @@ -0,0 +1,84 @@ +parent_id) { + $parent = static::query()->findOrFail($model->parent_id); + $model->path = $parent->path . $parent->id . '-'; + } else { + $model->parent_id = 0; + $model->path = '-'; + } + }); + + static::updating(function (Model $model) { + if ($model->parent_id) { + $parent = static::query()->findOrFail($model->parent_id); + $model->path = $parent->path . $parent->id . '-'; + } else { + $model->parent_id = 0; + $model->path = '-'; + } + }); + + static::deleting(function (Model $model) { + static::query()->allChildren($model->id)->delete(); + }); + } + protected function parentIds(): Attribute + { + return Attribute::make( + get: fn () => explode('-', substr($this->path, 1, -1)), + ); + } + + public function children() + { + return $this->hasMany(static::class, 'parent_id'); + } + + public function parent() + { + return $this->belongsTo(static::class, 'parent_id'); + } + + public function scopeAllChildren($q, $pid) + { + return $q->where('path', 'like', '%-'.$pid.'-%'); + } + + public function scopeSort($q) + { + return $q->orderBy('parent_id')->orderBy('sort'); + } + + public static function getTreeList($list, $pid = 0) + { + $tree = collect(); + foreach ($list->where('parent_id', $pid)->all() as $item) { + $item->children = []; + $children = $list->where('parent_id', $item->id); + if ($children->count() > 0) { + $item->children = static::getTreeList($list, $item->id); + } + $tree->push($item); + } + + return $tree; + } +} diff --git a/bootstrap/providers.php b/bootstrap/providers.php index 38b258d..679a2c4 100644 --- a/bootstrap/providers.php +++ b/bootstrap/providers.php @@ -2,4 +2,5 @@ return [ App\Providers\AppServiceProvider::class, + App\Providers\QueryLogServiceProvider::class, ]; diff --git a/composer.json b/composer.json index 60decc4..ab2b5ed 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,7 @@ "laravel/framework": "^11.9", "laravel/sanctum": "^4.0", "laravel/tinker": "^2.9", + "rap2hpoutre/fast-excel": "^5.5", "slowlyo/owl-admin": "^3.8", "tucker-eric/eloquentfilter": "^3.4" }, @@ -64,11 +65,5 @@ } }, "minimum-stability": "stable", - "prefer-stable": true, - "repositories": { - "packagist": { - "type": "composer", - "url": "https://mirrors.aliyun.com/composer/" - } - } + "prefer-stable": true } diff --git a/composer.lock b/composer.lock index 6f95be6..2e5dda7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c49fb7d62386a65cbd1917fa5cdcd780", + "content-hash": "897c9f6897a2d847033ffd18be088aef", "packages": [ { "name": "brick/math", @@ -2386,6 +2386,99 @@ ], "time": "2024-03-06T16:17:14+00:00" }, + { + "name": "openspout/openspout", + "version": "v4.24.2", + "source": { + "type": "git", + "url": "https://github.com/openspout/openspout.git", + "reference": "24272c1f7d073cc64fa3ecc2a863dc5d13be33a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/openspout/openspout/zipball/24272c1f7d073cc64fa3ecc2a863dc5d13be33a8", + "reference": "24272c1f7d073cc64fa3ecc2a863dc5d13be33a8", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-fileinfo": "*", + "ext-filter": "*", + "ext-libxml": "*", + "ext-xmlreader": "*", + "ext-zip": "*", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" + }, + "require-dev": { + "ext-zlib": "*", + "friendsofphp/php-cs-fixer": "^3.59.3", + "infection/infection": "^0.29.5", + "phpbench/phpbench": "^1.2.15", + "phpstan/phpstan": "^1.11.4", + "phpstan/phpstan-phpunit": "^1.4.0", + "phpstan/phpstan-strict-rules": "^1.6.0", + "phpunit/phpunit": "^10.5.20 || ^11.2.2" + }, + "suggest": { + "ext-iconv": "To handle non UTF-8 CSV files (if \"php-mbstring\" is not already installed or is too limited)", + "ext-mbstring": "To handle non UTF-8 CSV files (if \"iconv\" is not already installed)" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3.x-dev" + } + }, + "autoload": { + "psr-4": { + "OpenSpout\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Adrien Loison", + "email": "adrien@box.com" + } + ], + "description": "PHP Library to read and write spreadsheet files (CSV, XLSX and ODS), in a fast and scalable way", + "homepage": "https://github.com/openspout/openspout", + "keywords": [ + "OOXML", + "csv", + "excel", + "memory", + "odf", + "ods", + "office", + "open", + "php", + "read", + "scale", + "spreadsheet", + "stream", + "write", + "xlsx" + ], + "support": { + "issues": "https://github.com/openspout/openspout/issues", + "source": "https://github.com/openspout/openspout/tree/v4.24.2" + }, + "funding": [ + { + "url": "https://paypal.me/filippotessarotto", + "type": "custom" + }, + { + "url": "https://github.com/Slamdunk", + "type": "github" + } + ], + "time": "2024-06-17T08:53:37+00:00" + }, { "name": "phpoption/phpoption", "version": "1.9.2", @@ -3177,6 +3270,76 @@ ], "time": "2024-04-27T21:32:50+00:00" }, + { + "name": "rap2hpoutre/fast-excel", + "version": "v5.5.0", + "source": { + "type": "git", + "url": "https://github.com/rap2hpoutre/fast-excel.git", + "reference": "83604f2a16fbb0374747299173abe691b24916da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rap2hpoutre/fast-excel/zipball/83604f2a16fbb0374747299173abe691b24916da", + "reference": "83604f2a16fbb0374747299173abe691b24916da", + "shasum": "" + }, + "require": { + "illuminate/support": "^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0", + "openspout/openspout": "^4.24", + "php": "^8.0" + }, + "require-dev": { + "illuminate/database": "^6.20.12 || ^7.30.4 || ^8.24.0 || ^9.0 || ^10.0 || ^11.0", + "phpunit/phpunit": "^9.5 || ^10.1", + "squizlabs/php_codesniffer": "3.*" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Rap2hpoutre\\FastExcel\\Providers\\FastExcelServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/functions/fastexcel.php" + ], + "psr-4": { + "Rap2hpoutre\\FastExcel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "rap2h", + "email": "raphaelht@gmail.com" + } + ], + "description": "Fast Excel import/export for Laravel", + "keywords": [ + "csv", + "excel", + "laravel", + "xls", + "xlsx" + ], + "support": { + "issues": "https://github.com/rap2hpoutre/fast-excel/issues", + "source": "https://github.com/rap2hpoutre/fast-excel/tree/v5.5.0" + }, + "funding": [ + { + "url": "https://github.com/rap2hpoutre", + "type": "github" + } + ], + "time": "2024-06-03T08:00:43+00:00" + }, { "name": "slowlyo/laravel-support", "version": "v0.0.3", @@ -3238,23 +3401,17 @@ }, { "name": "slowlyo/owl-admin", - "version": "v3.8.0", + "version": "v3.9.0", "source": { "type": "git", "url": "https://github.com/slowlyo/owl-admin.git", - "reference": "002a00fe5d27a9eb5b13bc8f827bd199fcfeae61" + "reference": "760513c1db8d3df96d9e1897791d53d7f79dcd5f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slowlyo/owl-admin/zipball/002a00fe5d27a9eb5b13bc8f827bd199fcfeae61", - "reference": "002a00fe5d27a9eb5b13bc8f827bd199fcfeae61", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "url": "https://api.github.com/repos/slowlyo/owl-admin/zipball/760513c1db8d3df96d9e1897791d53d7f79dcd5f", + "reference": "760513c1db8d3df96d9e1897791d53d7f79dcd5f", + "shasum": "" }, "require": { "ext-gd": "*", @@ -3305,10 +3462,10 @@ "email": "slowlyo_email@qq.com", "forum": "https://github.com/orgs/owl-admin/discussions", "issues": "https://github.com/Slowlyo/owl-admin/issues", - "source": "https://github.com/slowlyo/owl-admin/tree/v3.8.0", + "source": "https://github.com/slowlyo/owl-admin/tree/v3.9.0", "wiki": "https://doc.owladmin.com/" }, - "time": "2024-06-09T17:39:14+00:00" + "time": "2024-07-07T16:34:23+00:00" }, { "name": "symfony/clock", diff --git a/config/admin.php b/config/admin.php index ff62962..b3600b3 100644 --- a/config/admin.php +++ b/config/admin.php @@ -23,15 +23,7 @@ return [ 'prefix' => 'admin-api', 'domain' => null, 'namespace' => 'App\\Admin\\Controllers', - 'middleware' => [ - // 'admin.autoSetLocale', - // 'admin.auth', - // 'admin.bootstrap', - // AdminPermission::class, - // 'sanctum', - // 'substitute', - 'admin' - ], + 'middleware' => ['admin'], // 不包含额外路由, 配置后, 不会追加新增/详情/编辑页面路由 'without_extra_routes' => [ '/dashboard', @@ -78,10 +70,10 @@ return [ 'https' => env('ADMIN_HTTPS', false), // 是否显示 [开发者工具] - 'show_development_tools' => env('ADMIN_SHOW_DEVELOPMENT_TOOLS', true), + 'show_development_tools' => env('ADMIN_SHOW_DEVELOPMENT_TOOLS', false), // 是否显示 [权限] 功能中的自动生成按钮 - 'show_auto_generate_permission_button' => env('ADMIN_SHOW_AUTO_GENERATE_PERMISSION_BUTTON', true), + 'show_auto_generate_permission_button' => env('ADMIN_SHOW_AUTO_GENERATE_PERMISSION_BUTTON', false), // 扩展 'extension' => [ diff --git a/config/filesystems.php b/config/filesystems.php index c5f244d..23b3d13 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -13,7 +13,7 @@ return [ | */ - 'default' => env('FILESYSTEM_DISK', 'local'), + 'default' => env('FILESYSTEM_DISK', 'public'), /* |-------------------------------------------------------------------------- diff --git a/database/seeders/AdminPermissionSeeder.php b/database/seeders/AdminPermissionSeeder.php index e021d4f..3ef5717 100644 --- a/database/seeders/AdminPermissionSeeder.php +++ b/database/seeders/AdminPermissionSeeder.php @@ -55,7 +55,6 @@ class AdminPermissionSeeder extends Seeder $this->createPermission([ 'name' => $name, 'slug' => $item['slug'].'.'.$key, - 'url' => null ], $permission->id, $menu); } } @@ -77,12 +76,14 @@ class AdminPermissionSeeder extends Seeder { $slug = data_get($item, 'slug'); $url = data_get($item, 'url'); + $method = data_get($item, 'method'); $permission = AdminPermission::create([ 'parent_id' => $pid, 'custom_order' => data_get($item, 'custom_order', 0), 'name' => data_get($item, 'name'), 'slug' => $slug, - 'http_path' => $url ? [$url] : null, + 'http_path' => $url ? (is_array($url) ? $url : [$url]) : null, + 'http_method' => $method ? (is_array($method) ? $method : [$method]) : null, ]); if ($menu) { $menu_has_permissions = [ diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 8157788..7b02ba3 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -15,5 +15,6 @@ class DatabaseSeeder extends Seeder { $this->call(AdminPermissionSeeder::class); $this->call(AdminSeeder::class); + $this->call(KeywordSeeder::class); } } diff --git a/database/seeders/KeywordSeeder.php b/database/seeders/KeywordSeeder.php new file mode 100644 index 0000000..34a2b46 --- /dev/null +++ b/database/seeders/KeywordSeeder.php @@ -0,0 +1,64 @@ + 'product_category', 'name' => '商品分类', 'children' => [ + ['name' => '家用电器', 'children' => [ + ['name' => '电视'], + ['name' => '冰箱'], + ['name' => '洗衣机'], + ]], + ['name' => '手机数码', 'children' => [ + ['name' => '手机'], + ['name' => '手机配件'], + ['name' => '摄影摄像'], + ]], + ['name' => '电脑办公', 'children' => [ + ['name' => '电脑整机'], + ['name' => '外设产品'], + ['name' => '网络产品'], + ]] + ]], + ['key' => 'article_category', 'name' => '文章分类', 'children' => [ + ['name' => '后端'], + ['name' => '前端'], + ['name' => 'Android'], + ['name' => 'IOS'], + ['name' => '开发工具'], + ['name' => '人工智能'], + ]] + ]; + + $this->createByTree($list); + } + + protected function createByTree($list, $parent = null) + { + foreach ($list as $index => $item) { + $params = Arr::except($item, ['children']); + $params['sort'] = $index + 1; + $params['parent_id'] = $parent ? $parent->id : 0; + $params['key'] = data_get($item, 'key', data_get($parent, 'key') . '_' . $index); + $model = Keyword::create($params); + + if ($children = data_get($item, 'children')) { + $this->createByTree($children, $model); + } + } + } +} diff --git a/lang/zh_CN/keywords.php b/lang/zh_CN/keywords.php index 85c2ad8..e1f1156 100644 --- a/lang/zh_CN/keywords.php +++ b/lang/zh_CN/keywords.php @@ -8,10 +8,11 @@ return [ 'parent_id' => '上级', 'type_key' => '上级', 'path' => '上级', - 'sort' => '排序(倒序)', + 'sort' => '排序', 'level' => '层级', 'options' => '扩展', 'image' => '图片', + 'images' => '图片集', 'description' => '描述', 'content' => '内容', ];