From 0970c590b4ba7f5d87944c5586a9960b87bf385c Mon Sep 17 00:00:00 2001 From: vine_liutk <961510893@qq.com> Date: Thu, 4 Aug 2022 17:24:00 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=87=E7=AB=A0=E6=89=A9?= =?UTF-8?q?=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/article/.gitignore | 7 + packages/article/README.md | 3 + packages/article/composer.json | 31 +++++ .../resources/lang/en/article-category.php | 3 + .../article/resources/lang/en/article.php | 3 + .../resources/lang/zh_CN/article-category.php | 18 +++ .../article/resources/lang/zh_CN/article.php | 28 ++++ .../article/src/ArticleServiceProvider.php | 27 ++++ .../Controllers/ArticleCategoryController.php | 125 ++++++++++++++++++ .../Http/Controllers/ArticleController.php | 108 +++++++++++++++ packages/article/src/Http/routes.php | 7 + packages/article/src/Models/Article.php | 70 ++++++++++ .../article/src/Models/ArticleCategory.php | 70 ++++++++++ packages/article/src/Setting.php | 14 ++ .../updates/ArticlePermissionSeeder.php | 70 ++++++++++ .../article/updates/CreateArticleTable.php | 66 +++++++++ packages/article/version.php | 9 ++ 17 files changed, 659 insertions(+) create mode 100644 packages/article/.gitignore create mode 100644 packages/article/README.md create mode 100644 packages/article/composer.json create mode 100644 packages/article/resources/lang/en/article-category.php create mode 100644 packages/article/resources/lang/en/article.php create mode 100644 packages/article/resources/lang/zh_CN/article-category.php create mode 100644 packages/article/resources/lang/zh_CN/article.php create mode 100644 packages/article/src/ArticleServiceProvider.php create mode 100644 packages/article/src/Http/Controllers/ArticleCategoryController.php create mode 100644 packages/article/src/Http/Controllers/ArticleController.php create mode 100644 packages/article/src/Http/routes.php create mode 100644 packages/article/src/Models/Article.php create mode 100644 packages/article/src/Models/ArticleCategory.php create mode 100644 packages/article/src/Setting.php create mode 100644 packages/article/updates/ArticlePermissionSeeder.php create mode 100644 packages/article/updates/CreateArticleTable.php create mode 100644 packages/article/version.php diff --git a/packages/article/.gitignore b/packages/article/.gitignore new file mode 100644 index 0000000..9d4b362 --- /dev/null +++ b/packages/article/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +phpunit.phar +/vendor +composer.phar +composer.lock +*.project +.idea/ \ No newline at end of file diff --git a/packages/article/README.md b/packages/article/README.md new file mode 100644 index 0000000..838df4c --- /dev/null +++ b/packages/article/README.md @@ -0,0 +1,3 @@ +# Dcat Admin Extension + + diff --git a/packages/article/composer.json b/packages/article/composer.json new file mode 100644 index 0000000..724cf28 --- /dev/null +++ b/packages/article/composer.json @@ -0,0 +1,31 @@ +{ + "name": "peidikeji/article", + "alias": "article", + "description": "Description...", + "type": "library", + "keywords": ["dcat-admin", "extension"], + "homepage": "https://github.com/peidikeji/article", + "license": "MIT", + "authors": [ + { + "name": "your name", + "email": "your email" + } + ], + "require": { + "php": ">=7.1.0" + }, + "autoload": { + "psr-4": { + "Peidikeji\\Article\\": "src/" + } + }, + "extra": { + "dcat-admin": "Peidikeji\\Article\\ArticleServiceProvider", + "laravel": { + "providers": [ + "Peidikeji\\Article\\ArticleServiceProvider" + ] + } + } +} diff --git a/packages/article/resources/lang/en/article-category.php b/packages/article/resources/lang/en/article-category.php new file mode 100644 index 0000000..0b67a5f --- /dev/null +++ b/packages/article/resources/lang/en/article-category.php @@ -0,0 +1,3 @@ + [ + 'ArticleCategory' => '文章分类', + 'article-categories'=> '文章分类', + ], + 'fields' => [ + 'parent_id' => '上级', + 'name' => '名称', + 'key' => 'KEY', + 'is_recommend' => '推荐状态', + 'sort' => '排序', + 'remarks' => '备注' + ], + 'options' => [ + ], +]; diff --git a/packages/article/resources/lang/zh_CN/article.php b/packages/article/resources/lang/zh_CN/article.php new file mode 100644 index 0000000..7d18348 --- /dev/null +++ b/packages/article/resources/lang/zh_CN/article.php @@ -0,0 +1,28 @@ + [ + 'Article' => '文章管理', + 'articles'=> '文章管理', + ], + 'fields' => [ + 'title' => '标题', + 'sub_title' => '副标题', + 'author' => '作者', + 'author_name' => '作者', + 'category_id' => '分类', + 'category'=>[ + 'name' => '分类', + ], + 'admin_user_id' => '创建人', + 'cover' => '封面', + 'is_recommend' => '推荐状态', + 'content' => '内容', + 'is_enable' => '可用状态', + 'sort' => '排序', + 'published_at' => '发布时间', + 'remarks' => '备注' + ], + 'options' => [ + ], +]; diff --git a/packages/article/src/ArticleServiceProvider.php b/packages/article/src/ArticleServiceProvider.php new file mode 100644 index 0000000..b5a11c7 --- /dev/null +++ b/packages/article/src/ArticleServiceProvider.php @@ -0,0 +1,27 @@ +disableRowSelector(); + + $grid->model()->sort(); + $grid->column('name')->tree(true, false); + $grid->column('key'); + $grid->column('sort')->editable(['mask' => '{alias:\'numeric\',min:0,max:999}']); + $grid->column('is_enable')->if(function(){ + return !config('admin.permission.enable') || Admin::user()->can('dcat.admin.article_categories.edit'); + })->then(function (GridColumn $column) { + $column->switch(); + })->else(function (GridColumn $column) { + $column->bool(); + }); + $grid->column('is_recommend') + ->if(function(){ + return !config('admin.permission.enable') || Admin::user()->can('dcat.admin.article_categories.edit'); + })->then(function (GridColumn $column) { + $column->switch(); + })->else(function (GridColumn $column) { + $column->bool(); + }); + + $grid->model()->orderBy('sort', 'desc'); + $grid->model()->orderBy('created_at', 'desc'); + + $grid->setDialogFormDimensions('50%', '70%'); + $grid->disableCreateButton(!(!config('admin.permission.enable') || Admin::user()->can('dcat.admin.article_categories.create'))); + $grid->enableDialogCreate(); + + $grid->actions(function (Grid\Displayers\Actions $actions) { + $actions->disableView(); + $actions->disableEdit(); + $actions->quickEdit(!config('admin.articles.enable') || Admin::user()->can('dcat.admin.article_categories.edit')); + }); + }); + } + + public function form() + { + return Form::make(new ArticleCategory(), function (Form $form) { + + $form->select('parent_id')->help('不选默认为顶级')->options(ArticleCategory::selectOptions())->default(0); + $form->text('name')->required(); + $unique = Rule::unique((new ArticleCategory())->getTable(), 'key'); + if ($form->isCreating()) { + $form->text('key')->help('不填则自动生成, 唯一')->rules([$unique]); + } else { + $form->text('key')->required()->help('唯一')->rules([$unique->ignore($form->getKey())]); + } + $form->number('sort') + ->min(0) + ->default(0) + ->help('数值越大, 越靠前'); + $form->switch('is_enable')->default(0); + $form->switch('is_recommend')->default(0); + $form->text('remarks'); + $form->hidden('level')->default(1); + + $controller = $this; + $form->saving(function (Form $form) use ($controller) { + if ($form->isCreating() && !$form->key) { + $form->key = $controller->generateKey(); + } + }); + + $form->saved(function (Form $form) { + if ($form->isEditing() && $form->input('is_enable') !== null && $form->input('is_enable') != $form->model()->is_enable) { + //处理分类子级状态同步(层级)父级 + ArticleCategory::where('path', 'like', "%-".$form->model()->id."-%")->update(['is_enable' => $form->is_enable]); + //处理父级状态 + if($form->is_enable){ + //如果状态是开启,判断(层级)父级是否开启 + $exPath = trim('-', $form->model()->path); + $parentIds = $exPath ? explode('-', $exPath) : []; + if($parentIds){ + ArticleCategory::whereIn('id', $parentIds)->update(['is_enable' => $form->is_enable]); + } + } + } + }); + + $form->deleting(function (Form $form) { + $hasArticles = Article::where('category_id', $form->getKey())->exists(); + if ($hasArticles) { + return $form->response()->error('需要先删除该分类下的文章'); + } + $hasChildren = ArticleCategory::where('parent_id', $form->getKey())->exists(); + if ($hasChildren) { + return $form->response()->error('需要先删除该分类下的子级'); + } + }); + + + }); + } + + protected function generateKey() + { + do { + $id = ArticleCategory::max('id') + 1; + $key = 'category-' . $id; + } while (ArticleCategory::where('key', $key)->exists()); + + return $key; + } +} diff --git a/packages/article/src/Http/Controllers/ArticleController.php b/packages/article/src/Http/Controllers/ArticleController.php new file mode 100644 index 0000000..e48fb8f --- /dev/null +++ b/packages/article/src/Http/Controllers/ArticleController.php @@ -0,0 +1,108 @@ +disableRowSelector(); + $grid->column('category.name')->label(); + $grid->column('title')->display(function ($v) { + if (mb_strlen($v) > 20) { + return mb_substr($v, 0, 17) . '...'; + } else { + return $v; + } + }); + $grid->column('cover')->image(100, 100); + $grid->column('author_name')->display(function (){ + return $this->author_name; + }); + $grid->column('sort')->editable(['mask' => '{alias:\'numeric\',min:0,max:999}']); + $grid->column('is_enable')->if(function(){ + return !config('admin.permission.enable') || Admin::user()->can('dcat.admin.articles.edit'); + })->then(function (GridColumn $column) { + $column->switch(); + })->else(function (GridColumn $column) { + $column->bool(); + }); + $grid->column('is_recommend')->if(function(){ + return !config('admin.permission.enable') || Admin::user()->can('dcat.admin.articles.edit'); + })->then(function (GridColumn $column) { + $column->switch(); + })->else(function (GridColumn $column) { + $column->bool(); + }); + + $grid->column('remarks'); + + $grid->model()->orderBy('sort', 'desc'); + $grid->model()->orderBy('created_at', 'desc'); + + + $grid->setDialogFormDimensions('50%', '70%'); + $grid->disableCreateButton(!(!config('admin.permission.enable') || Admin::user()->can('dcat.admin.articles.create'))); + $grid->enableDialogCreate(); + + $grid->actions(function (Grid\Displayers\Actions $actions) { + $actions->disableView(); + $actions->disableEdit(); + $actions->quickEdit(!config('admin.articles.enable') || Admin::user()->can('dcat.admin.articles.edit')); + }); + }); + } + + public function form() + { + return Form::make(new Article(), function($form){ + $form->select('category_id')->options(ArticleCategory::selectOptions(false)); + $form->text('title')->required(); + $form->text('sub_title'); + $form->image('cover') + ->uniqueName() + ->move('banner') + ->saveFullUrl() + ->autoSave(false) + ->autoUpload() + ->removable(false)//禁止用户从页面点击删除服务器上的文件,可以实现图片覆盖上传效果 + ->retainable(); + $form->text('author'); + $form->datetime('published_at'); + $form->switch('is_enable')->default(0); + $form->switch('is_recommend')->default(0); + $form->number('sort')->min(0)->default(0); + $form->text('remarks'); + + $form->editor('content')->options([ + 'plugins' => [ + 'image', + 'lists', + 'preview', + 'fullscreen', + 'table', + ], + 'toolbar' => [ + 'undo redo | preview fullscreen | styleselect | fontsizeselect bold italic underline strikethrough forecolor backcolor | image blockquote removeformat codesample', + 'alignleft aligncenter alignright alignjustify| indent outdent bullist numlist table subscript superscript | code', + ] + ])->height('800'); + Admin::style(<<names('article_categories'); +Route::resource('articles', Controllers\ArticleController::class); diff --git a/packages/article/src/Models/Article.php b/packages/article/src/Models/Article.php new file mode 100644 index 0000000..7084158 --- /dev/null +++ b/packages/article/src/Models/Article.php @@ -0,0 +1,70 @@ +admin_user_id = Admin::user()->id; + // 添加/修改分类时, 更新 category_path + if ($model->isDirty('category_id')) { + $model->category_path = ArticleCategory::where('id', $model->category_id)->value('path') . $model->category_id . '-'; + } + }); + } + + public function category() + { + return $this->belongsTo(ArticleCategory::class, 'category_id'); + } + + public function adminUser() + { + return $this->belongsTo(Administrator::class, 'admin_user_id'); + } + + public function getAuthorNameAttribute() + { + return $this->attributes['author'] ?? ($this->attributes['admin_user_id'] ? (Administrator::find($this->attributes['admin_user_id'])?->name ?? '未知') : '未知'); + } + + //public function image(): Attribute + //{ + // return Attribute::make( + // get: fn ($v) => $v ?: 'https://via.placeholder.com/640x480.png', + // set: fn ($v) => $v ?: 'https://via.placeholder.com/640x480.png', + // ); + //} + + public function scopeSort($q) + { + return $q->orderBy('sort', 'desc')->latest('published_at')->latest('id'); + } + + public function scopePublish($q) + { + return $q->where('published_at', '<=', now()); + } +} diff --git a/packages/article/src/Models/ArticleCategory.php b/packages/article/src/Models/ArticleCategory.php new file mode 100644 index 0000000..859d989 --- /dev/null +++ b/packages/article/src/Models/ArticleCategory.php @@ -0,0 +1,70 @@ +parent_id) { + // 将层级设为 1 + $category->level = 1; + // 将 path 设为 - + $category->path = '-'; + } else { + // 将层级设为父类目的层级 + 1 + $category->level = $category->parent->level + 1; + // 将 path 值设为父类目的 path 追加父类目 ID 以及最后跟上一个 - 分隔符 + $category->path = $category->parent->path . $category->parent_id . '-'; + } + }); + } + + public static function selectOptions($root = true) + { + $rootText = '顶级'; + + $options = (new static())->withQuery(function ($q) { + return $q->sort(); + })->buildSelectOptions(); + + return collect($options)->when($root, fn ($c) => $c->prepend($rootText, 0))->all(); + } + + public function articles() + { + return $this->hasMany(Article::class, 'category_id'); + } + + public function parent() + { + return $this->belongsTo(static::class, 'parent_id'); + } + + public function children() + { + return $this->hasMany(static::class, 'parent_id')->sort(); + } + + public function scopeSort($q) + { + return $q->orderBy('sort', 'desc')->orderBy('id'); + } +} diff --git a/packages/article/src/Setting.php b/packages/article/src/Setting.php new file mode 100644 index 0000000..1e96681 --- /dev/null +++ b/packages/article/src/Setting.php @@ -0,0 +1,14 @@ +text('key1')->required(); + $this->text('key2')->required(); + } +} diff --git a/packages/article/updates/ArticlePermissionSeeder.php b/packages/article/updates/ArticlePermissionSeeder.php new file mode 100644 index 0000000..533ff10 --- /dev/null +++ b/packages/article/updates/ArticlePermissionSeeder.php @@ -0,0 +1,70 @@ + ['name' => '文章分类管理', 'curd' => true], + 'articles' => ['name' => '文章管理', 'curd' => true], + ]; + $this->createPermissionData($permissions); + } + + /** + * 插入权限 + * + * @param array $permissions + * @param string $key + * @param integer $pId + */ + public function createPermissionData(array $permissions, string $key = '', int $pId = 0) + { + $curdArr = [ + 'index' => '列表', + 'create' => '新增', + 'edit' => '修改', + 'destroy' => '删除', + 'show' => '详情', + ]; + foreach ($permissions as $slug => $permission) { + //是否已存在该权限 + $slugKey = 'dcat.admin.' . ($key ? $key . '.' . $slug : $slug); + + + $pper = Permission::updateOrCreate(['slug' => $slugKey], ['name' => is_string($permission) ? $permission : $permission['name'], 'parent_id' => $pId]); + + if (!is_string($permission)) { + if (!isset($permission['children'])) { + $permission['children'] = []; + } + //判断是否默认插入curd权限 + if (isset($permission['curd']) && $permission['curd']) { + if (is_array($permission['curd'])) { + $permission['curd'] = array_reverse($permission['curd']); + foreach ($permission['curd'] as $value) { + $permission['children'] = array_merge([$value => $curdArr[$value]], $permission['children']); + } + } else { + $permission['children'] = array_merge($curdArr, $permission['children']); + } + } + + if (count($permission['children']) > 0) { + $_key = $permission['curd'] !== false ? ($key ? $key . '.' . $slug : $slug) : $key; + $this->createPermissionData($permission['children'], $_key ?? $slug, $pper->id); + } + } + } + } +} diff --git a/packages/article/updates/CreateArticleTable.php b/packages/article/updates/CreateArticleTable.php new file mode 100644 index 0000000..bafded3 --- /dev/null +++ b/packages/article/updates/CreateArticleTable.php @@ -0,0 +1,66 @@ +id(); + $table->string('name')->comment('名称'); + $table->string('key')->unique()->nullable()->comment('key'); + $table->unsignedTinyInteger('is_recommend')->default(0)->comment('推荐状态'); + $table->unsignedTinyInteger('is_enable')->default(1)->comment('可用状态'); + $table->unsignedBigInteger('parent_id')->default(0)->comment('上级ID'); + $table->unsignedInteger('level')->default(1)->comment('层级'); + $table->string('path')->default('-')->comment('所有的父级ID'); + $table->unsignedInteger('sort')->default(0)->comment('排序 desc'); + $table->string('remarks')->nullable()->comment('备注'); + + $table->timestamps(); + }); + } + + if (!Schema::hasTable('articles')) { + Schema::create('articles', function (Blueprint $table) { + $table->id(); + $table->unsignedBigInteger('category_id')->nullable()->comment('分类ID'); + $table->string('category_path')->default('-')->comment('分类 path'); + $table->unsignedBigInteger('admin_user_id')->comment('创建人'); + $table->string('title')->comment('文章标题'); + $table->string('sub_title')->nullable()->comment('副标题'); + $table->string('author')->nullable()->comment('作者'); + $table->string('cover')->nullable()->comment('封面'); + $table->longText('content')->nullable()->comment('文章内容'); + $table->timestamp('published_at')->nullable()->comment('发布时间'); + $table->unsignedInteger('sort')->default(1)->comment('排序 desc'); + $table->string('remarks')->nullable()->comment('备注'); + + $table->unsignedTinyInteger('is_recommend')->default(0)->comment('推荐状态'); + $table->unsignedTinyInteger('is_enable')->default(1)->comment('可用状态'); + + $table->timestamps(); + }); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('articles'); + Schema::dropIfExists('article_categories'); + } +}; diff --git a/packages/article/version.php b/packages/article/version.php new file mode 100644 index 0000000..422fc1c --- /dev/null +++ b/packages/article/version.php @@ -0,0 +1,9 @@ + [ + '初始化文章管理', + 'CreateArticleTable.php', + 'ArticlePermissionSeeder.php', + ], +];