diff --git a/.env.example b/.env.example index 478972c..073016f 100644 --- a/.env.example +++ b/.env.example @@ -4,7 +4,7 @@ APP_KEY= APP_DEBUG=true APP_URL=http://localhost -LOG_CHANNEL=stack +LOG_CHANNEL=daily LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=debug @@ -17,7 +17,7 @@ DB_PASSWORD= BROADCAST_DRIVER=log CACHE_DRIVER=file -FILESYSTEM_DISK=local +FILESYSTEM_DISK=public QUEUE_CONNECTION=sync SESSION_DRIVER=file SESSION_LIFETIME=120 diff --git a/app/Admin/Controllers/ArticleCategoryController.php b/app/Admin/Controllers/ArticleCategoryController.php index 3fce5d7..682f590 100644 --- a/app/Admin/Controllers/ArticleCategoryController.php +++ b/app/Admin/Controllers/ArticleCategoryController.php @@ -9,26 +9,36 @@ use Slowlyo\OwlAdmin\Renderers\TextControl; use Slowlyo\OwlAdmin\Controllers\AdminController; use App\Services\Admin\ArticleCategoryService; use App\Admin\Components; +use Illuminate\Http\Request; class ArticleCategoryController extends AdminController { protected string $serviceName = ArticleCategoryService::class; - protected string $pageTitle = '文章分类';//待完善-todo - public function list(): Page { $crud = $this->baseCRUD() ->filterTogglable(false) + ->loadDataOnce(true) + ->footerToolbar([]) ->headerToolbar([ $this->createButton(true), - ...$this->baseHeaderToolBar(), + amis('reload')->align('right'), + amis('filter-toggler')->align('right'), ]) + ->quickSaveItemApi(admin_url('quick-edit/article-categories/$id')) ->columns([ - TableColumn::make()->name('id')->label('ID')->sortable(true), - TableColumn::make()->name('name')->label('名称'), - TableColumn::make()->name('created_at')->label('创建时间')->type('datetime')->sortable(true), - TableColumn::make()->name('updated_at')->label('更新时间')->type('datetime')->sortable(true), + ['name' => 'id', 'label' => __('article-category.id')], + ['name' => 'name', 'label' => __('article-category.name')], + ['name' => 'icon', 'label' => __('article-category.icon'), 'type' => 'image', 'width' => 60], + ['name' => 'sort', 'label' => __('article-category.sort')], + ['name' => 'is_enable', 'label' => __('article-category.is_enable'), 'type' => 'switch', 'quickEdit' => [ + 'type' => 'switch', + 'mode' => 'inline', + 'onText' => __('admin.switch.on'), + 'offText' => __('admin.switch.off'), + 'saveImmediately' => true, + ]], $this->rowActions(true), ]); @@ -40,23 +50,30 @@ class ArticleCategoryController extends AdminController return $this->baseForm()->body([ TextControl::make()->name('name')->label('名称')->required(true), amisMake()->ImageControl()->name('icon')->label('icon')->autoUpload(true), - Components::make()->parentControl(), + Components::make()->parentControl(admin_url('api/article-categories/tree-list')), Components::make()->sortControl(), - amisMake()->SwitchControl()->name('is_enable')->label('显示'), + amisMake()->SwitchControl()->name('is_enable')->value(true)->label('显示'), ]); } public function detail(): Form { return $this->baseDetail()->body([ - TextControl::make()->static(true)->name('id')->label('ID'), - TextControl::make()->static(true)->name('name')->label('名称'), - TextControl::make()->static(true)->name('created_at')->label('创建时间'), - TextControl::make()->static(true)->name('updated_at')->label('更新时间') + ['name' => 'id', 'type' => 'static', 'label' => __('article-category.id')], + ['name' => 'name', 'type' => 'static', 'label' => __('article-category.name')], + ['name' => 'created_at', 'type' => 'static', 'label' => __('article-category.created_at')], ]); } - public function getTreeList(Request $request){ + public function getTreeList(Request $request) + { return $this->service->getTree(); } + + public function quickSave(Request $request) + { + logger('1', $request->all()); + + return $this->response()->success(); + } } diff --git a/app/Admin/routes.php b/app/Admin/routes.php index 964474b..68af157 100644 --- a/app/Admin/routes.php +++ b/app/Admin/routes.php @@ -13,6 +13,7 @@ Route::group([ 'prefix' => 'api', ], function (Router $router) { $router->get('keywords/tree-list', '\App\Admin\Controllers\KeywordController@getTreeList')->name('api.keywords.tree-list'); + $router->get('article-categories/tree-list', [\App\Admin\Controllers\ArticleCategoryController::class, 'getTreeList'])->name('api.article-categories.tree-list'); $router->get('region-categories/tree-list', '\App\Admin\Controllers\RegionCategoryController@getTreeList')->name('api.region-categories.tree-list'); }); @@ -20,8 +21,9 @@ Route::group([ //公告管理 $router->resource('admin-notices', \App\Admin\Controllers\AdminNoticeController::class); - //文章分类 + // 文章分类 $router->resource('article-categories', \App\Admin\Controllers\ArticleCategoryController::class); + $router->post('quick-edit/article-categories/{article_category}', [\App\Admin\Controllers\ArticleCategoryController::class, 'update']); //文章管理 $router->resource('articles', \App\Admin\Controllers\ArticleController::class); //图片位置 diff --git a/app/Console/Commands/ModelFillable.php b/app/Console/Commands/ModelFillable.php new file mode 100644 index 0000000..fef65fd --- /dev/null +++ b/app/Console/Commands/ModelFillable.php @@ -0,0 +1,55 @@ +argument('table'); + $ignore = ['id', 'created_at', 'updated_at', 'deleted_at']; + $connection = $this->option('connection'); + if (!$connection) { + $connection = config('database.default'); + } + + if (Schema::connection($connection)->hasTable($table)) { + $list = Schema::connection($connection)->getColumnListing($table); + $list = array_filter($list, function ($value) use ($ignore) { + return !in_array($value, $ignore); + }); + $this->info("protected \$fillable = ['".implode('\', \'', $list)."'];"); + } + } +} diff --git a/app/Models/ArticleCategory.php b/app/Models/ArticleCategory.php index dacce63..c83da40 100644 --- a/app/Models/ArticleCategory.php +++ b/app/Models/ArticleCategory.php @@ -2,18 +2,38 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use EloquentFilter\Filterable; +use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Str; class ArticleCategory extends Model { - use HasFactory; use Filterable; - protected $fillable = []; + protected $casts = [ + 'created_at' => 'datetime:Y-m-d H:i:s', + 'updated_at' => 'datetime:Y-m-d H:i:s', + 'is_enable' => 'boolean', + ]; - protected function serializeDate(\DateTimeInterface $date){ - return $date->format('Y-m-d H:i:s'); + protected $fillable = ['icon', 'is_enable', 'level', 'name', 'parent_id', 'path', 'sort']; + + protected function icon(): Attribute + { + return Attribute::make( + get: fn($value) => $value ? (Str::startsWith($value, ['http://', 'https://']) ? $value : Storage::url($value)) : '', + ); + } + + public function parent() + { + return $this->belongsTo(static::class, 'parent_id'); + } + + public function children() + { + return $this->hasMany(static::class, 'parent_id'); } } diff --git a/app/Services/Admin/ArticleCategoryService.php b/app/Services/Admin/ArticleCategoryService.php index fa9e3f1..52fc6fd 100644 --- a/app/Services/Admin/ArticleCategoryService.php +++ b/app/Services/Admin/ArticleCategoryService.php @@ -4,6 +4,7 @@ namespace App\Services\Admin; use App\Models\ArticleCategory; use Slowlyo\OwlAdmin\Services\AdminService; +use Illuminate\Http\Request; /** * @method ArticleCategory getModel() @@ -18,4 +19,27 @@ class ArticleCategoryService extends AdminService $list = $this->query()->orderByDesc('sort')->get()->toArray(); return array2tree($list); } + + public function list() + { + return ['items' => $this->getTree()]; + } + + public function store($data): bool + { + $pid = data_get($data, 'parent_id'); + if ($pid && $parent = ArticleCategory::find($pid)) { + $data['level'] = $parent->level + 1; + $data['path'] = $parent->path . $parent->id . '-'; + } else { + $data['level'] = 1; + $data['path'] = '-'; + } + return parent::store($data); + } + + public function delete(string $ids): mixed + { + return $this->query()->whereIn($this->primaryKey(), explode(',', $ids))->delete(); + } } diff --git a/database/migrations/2023_03_20_114120_create_articles_table.php b/database/migrations/2023_03_20_114120_create_articles_table.php index 855f1f0..be9b0b8 100644 --- a/database/migrations/2023_03_20_114120_create_articles_table.php +++ b/database/migrations/2023_03_20_114120_create_articles_table.php @@ -24,9 +24,10 @@ return new class extends Migration $table->unsignedTinyInteger('is_enable')->default(1)->comment('显示开关'); $table->unsignedInteger('sort')->default(0)->comment('排序'); - //可能用到的额外字段 + // 可能用到的额外字段 $table->string('cover')->nullable()->comment('封面'); $table->string('author')->nullable()->comment('作者/来源'); + $table->string('category_path')->nullable()->comment('上级分类'); $table->timestamps(); }); } diff --git a/lang/zh_CN/admin.php b/lang/zh_CN/admin.php index d9a106c..06fb18d 100644 --- a/lang/zh_CN/admin.php +++ b/lang/zh_CN/admin.php @@ -188,4 +188,8 @@ return [ 'selected_rows_no_data' => '请选择要导出的数据', 'please_install_laravel_excel' => '请先安装 laravel-excel 扩展', ], + 'switch' => [ + 'on' => '开启', + 'off' => '关闭', + ] ]; diff --git a/lang/zh_CN/article-category.php b/lang/zh_CN/article-category.php new file mode 100644 index 0000000..dab6310 --- /dev/null +++ b/lang/zh_CN/article-category.php @@ -0,0 +1,10 @@ + 'ID', + 'name' => '名称', + 'icon' => '图片', + 'sort' => '排序', + 'is_enable' => '状态', + 'created_at' => '创建时间', +];