diff --git a/.gitignore b/.gitignore index f0d10af..c6649d4 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ yarn-error.log /.fleet /.idea /.vscode +project.md \ No newline at end of file diff --git a/app/Admin/Controllers/AdminNoticeController.php b/app/Admin/Controllers/AdminNoticeController.php index 9ae9841..62336be 100644 --- a/app/Admin/Controllers/AdminNoticeController.php +++ b/app/Admin/Controllers/AdminNoticeController.php @@ -2,41 +2,37 @@ namespace App\Admin\Controllers; -use Slowlyo\OwlAdmin\Renderers\Page; -use Slowlyo\OwlAdmin\Renderers\Form; -use Slowlyo\OwlAdmin\Renderers\TableColumn; -use Slowlyo\OwlAdmin\Renderers\TextControl; +use Slowlyo\OwlAdmin\Renderers\{Form, Page}; +use Slowlyo\OwlAdmin\Renderers\{Component, Button, TableColumn, TextControl, SelectControl, DateTimeControl, SwitchControl, Tabs, Tab, Status, Html}; use Slowlyo\OwlAdmin\Controllers\AdminController; use App\Services\Admin\AdminNoticeService; use App\Admin\Components; -use Slowlyo\OwlAdmin\OwlAdmin; class AdminNoticeController extends AdminController { protected string $serviceName = AdminNoticeService::class; - protected string $pageTitle = '公告管理';//待完善-todo - public function list(): Page { - $options = []; - $user = OwlAdmin::user(); - // 添加权限 - if ($user->can('admin_notice.create')) { - array_push($options, $this->createButton(true, 'lg')); - } - $options = array_merge($options, $this->baseHeaderToolBar(),); $crud = $this->baseCRUD() ->filterTogglable(false) - ->headerToolbar($options) + ->headerToolbar([ + $this->createButton(true, 'lg'), + ...$this->baseHeaderToolBar(), + ]) + ->filter($this->baseFilter()->actions([])->body([ + TextControl::make()->name('title')->label(__('admin-notice.title'))->size('md'), + Button::make()->label(__('admin.reset'))->actionType('clear-and-submit'), + Component::make()->setType('submit')->label(__('admin.search'))->level('primary'), + ])) + ->quickSaveItemApi(admin_url('quick-edit/admin-notices/$id')) ->columns([ - TableColumn::make()->name('id')->label('ID')->sortable(true), - TableColumn::make()->name('title')->label('标题'), - TableColumn::make()->name('article_id')->label('关联文章')->className('text-primary'), - TableColumn::make()->name('is_enable')->type('switch')->label('显示'), - TableColumn::make()->name('published_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), + TableColumn::make()->name('id')->label(__('admin-notice.id'))->sortable(true), + TableColumn::make()->name('title')->label(__('admin-notice.title')), + TableColumn::make()->name('article.title')->label(__('admin-notice.article_id')), + TableColumn::make()->name('is_enable')->type('switch')->label(__('admin-notice.is_enable'))->quickEdit(SwitchControl::make()->saveImmediately(true)->mode('inline')), + TableColumn::make()->name('sort')->label(__('admin-notice.sort'))->align('center')->quickEdit(Components::make()->sortControl('sort', __('admin-notice.sort'))->saveImmediately(true)), + TableColumn::make()->name('published_at')->label(__('admin-notice.published_at')), $this->rowActions(true, 'lg'), ]); @@ -45,25 +41,30 @@ class AdminNoticeController extends AdminController public function form(): Form { - return $this->baseForm()->body([ - \amisMake()->TextControl()->name('title')->label('标题')->required(true), - Components::make()->fuEditorControl(), - \amisMake()->SelectControl()->name('article_id')->label('关联文章'), - Components::make()->sortControl(), - \amisMake()->DateTimeControl()->name('published_at')->label('发布时间')->description('*不填写则默认为创建时间'), - \amisMake()->SwitchControl()->name('is_enable')->label('显示'), + return $this->baseForm()->title('')->body([ + TextControl::make()->name('title')->label(__('admin-notice.title'))->required(true), + SelectControl::make()->name('article_id')->label(__('admin-notice.article_id'))->source(admin_url('api/articles/options')), + Components::make()->sortControl('sort', __('admin-notice.sort')), + DateTimeControl::make()->name('published_at')->label(__('admin-notice.published_at'))->value(now())->format('YYYY-MM-DD HH:mm:ss')->description('*不填写则默认为创建时间'), + SwitchControl::make()->name('is_enable')->label(__('admin-notice.is_enable'))->value(true), + Components::make()->fuEditorControl('content', __('admin-notice.content')) ]); } public function detail(): Form { - return $this->baseDetail()->body([ - TextControl::make()->static(true)->name('id')->label('ID'), - TextControl::make()->static(true)->name('title')->label('标题'), - //-todo - - TextControl::make()->static(true)->name('created_at')->label('创建时间'), - TextControl::make()->static(true)->name('updated_at')->label('更新时间') + return $this->baseDetail()->title('')->body([ + TextControl::make()->static(true)->name('id')->label(__('admin-notice.id')), + TextControl::make()->static(true)->name('title')->label(__('admin-notice.title')), + TextControl::make()->static(true)->name('article.title')->label(__('admin-notice.article_id')), + TextControl::make()->static(true)->name('sort')->label(__('admin-notice.sort')), + TextControl::make()->name('is_enable')->label(__('admin-notice.is_enable'))->static(true)->staticSchema(Status::make()->source([ + ['label' => '不显示', 'icon' => 'fa fa-close', 'color' => '#cc292e'], + ['label' => '显示', 'icon' => 'fa fa-check', 'color' => '#30bf13'], + ])), + TextControl::make()->static(true)->name('published_at')->label(__('admin-notice.published_at')), + TextControl::make()->static(true)->name('created_at')->label(__('admin-notice.created_at')), + TextControl::make()->name('content')->label(__('admin-notice.content'))->static(true)->staticSchema(Html::make()->name('content')), ]); } } diff --git a/app/Admin/Controllers/ArticleController.php b/app/Admin/Controllers/ArticleController.php index 3c98738..61632ac 100644 --- a/app/Admin/Controllers/ArticleController.php +++ b/app/Admin/Controllers/ArticleController.php @@ -16,6 +16,10 @@ class ArticleController extends AdminController { $crud = $this->baseCRUD() ->filterTogglable(false) + ->headerToolbar([ + $this->createButton(true, 'lg'), + ...$this->baseHeaderToolBar(), + ]) ->filter($this->baseFilter()->actions([])->body([ TextControl::make()->name('title')->label(__('article.title'))->size('md'), Components::make()->parentControl(admin_url('api/article-categories/tree-list'), 'category_path', __('article.category_id'))->size('lg'), @@ -29,9 +33,10 @@ class ArticleController extends AdminController TableColumn::make()->name('category.name')->label(__('article.category_id'))->className('text-primary'), Image::make()->name('cover')->label(__('article.cover'))->width(100), TableColumn::make()->name('sub_title')->label(__('article.sub_title')), + TableColumn::make()->name('sort')->label(__('article.sort'))->align('center')->quickEdit(Components::make()->sortControl('sort', __('article-category.sort'))->saveImmediately(true)), TableColumn::make()->name('is_enable')->type('switch')->label(__('article.is_enable'))->quickEdit(SwitchControl::make()->saveImmediately(true)->mode('inline')), TableColumn::make()->name('published_at')->label(__('article.published_at')), - $this->rowActions(), + $this->rowActions(true, 'lg'), ]); return $this->baseList($crud); @@ -39,41 +44,41 @@ class ArticleController extends AdminController public function form(): Form { - return $this->baseForm()->title('')->body( - Tabs::make()->tabs([ - Tab::make()->title('基本信息')->body([ - TextControl::make()->name('title')->label(__('article.title'))->required(true), - Components::make()->parentControl(admin_url('api/article-categories/tree-list'), 'category_id', __('article.category_id'))->required(true), - TextControl::make()->name('sub_title')->label(__('article.sub_title')), - ImageControl::make()->name('cover')->label(__('article.cover'))->autoUpload(true), - Components::make()->sortControl('sort', __('article.sort')), - DateTimeControl::make()->name('published_at')->label(__('article.published_at'))->value(now())->format('YYYY-MM-DD HH:mm:ss')->description('*不填写则默认为创建时间'), - SwitchControl::make()->name('is_enable')->label(__('article.is_enable'))->value(true), - ]), - Tab::make()->title('内容')->body(Components::make()->fuEditorControl('content', '')) - ])); + return $this->baseForm()->title('')->body([ + TextControl::make()->name('title')->label(__('article.title'))->required(true), + Components::make()->parentControl(admin_url('api/article-categories/tree-list'), 'category_id', __('article.category_id'))->required(true), + TextControl::make()->name('sub_title')->label(__('article.sub_title')), + ImageControl::make()->name('cover')->label(__('article.cover'))->autoUpload(true), + Components::make()->sortControl('sort', __('article.sort')), + DateTimeControl::make()->name('published_at')->label(__('article.published_at'))->value(now())->format('YYYY-MM-DD HH:mm:ss')->description('*不填写则默认为创建时间'), + SwitchControl::make()->name('is_enable')->label(__('article.is_enable'))->value(true), + Components::make()->fuEditorControl('content', __('article.content')), + ]); } public function detail(): Form { - return $this->baseDetail()->title('')->body( - Tabs::make()->tabs([ - Tab::make()->title('基本信息')->body([ - TextControl::make()->static(true)->name('id')->label(__('article.id')), - TextControl::make()->static(true)->name('title')->label(__('article.title')), - TextControl::make()->static(true)->name('category.name')->label(__('article.category_id')), - TextControl::make()->static(true)->name('sub_title')->label(__('article.sub_title')), - TextControl::make()->name('cover')->label(__('article.cover'))->static(true)->staticSchema(Image::make()), - TextControl::make()->static(true)->name('sort')->label(__('article.sort')), - TextControl::make()->name('is_enable')->label(__('article.is_enable'))->static(true)->staticSchema(Status::make()->source([ - ['label' => '不显示', 'icon' => 'fa fa-close', 'color' => '#cc292e'], - ['label' => '显示', 'icon' => 'fa fa-check', 'color' => '#30bf13'], - ])), - TextControl::make()->static(true)->name('published_at')->label(__('article.published_at')), - TextControl::make()->static(true)->name('created_at')->label(__('article.created_at')), - ]), - Tab::make()->title('内容')->body(Html::make()->name('content')) - ]) - ); + return $this->baseDetail()->title('')->body([ + TextControl::make()->static(true)->name('id')->label(__('article.id')), + TextControl::make()->static(true)->name('title')->label(__('article.title')), + TextControl::make()->static(true)->name('category.name')->label(__('article.category_id')), + TextControl::make()->static(true)->name('sub_title')->label(__('article.sub_title')), + TextControl::make()->name('cover')->label(__('article.cover'))->static(true)->staticSchema(Image::make()), + TextControl::make()->static(true)->name('sort')->label(__('article.sort')), + TextControl::make()->name('is_enable')->label(__('article.is_enable'))->static(true)->staticSchema(Status::make()->source([ + ['label' => '不显示', 'icon' => 'fa fa-close', 'color' => '#cc292e'], + ['label' => '显示', 'icon' => 'fa fa-check', 'color' => '#30bf13'], + ])), + TextControl::make()->static(true)->name('published_at')->label(__('article.published_at')), + TextControl::make()->static(true)->name('created_at')->label(__('article.created_at')), + TextControl::make()->name('content')->label(__('article.content'))->static(true)->staticSchema(Html::make()), + ]); + } + + public function options() + { + $list = $this->service->listQuery()->select(['id as value', 'title as label'])->without('category')->get(); + + return $this->response()->success($list); } } diff --git a/app/Admin/Controllers/BannerController.php b/app/Admin/Controllers/BannerController.php index ac5b863..df3dc2a 100644 --- a/app/Admin/Controllers/BannerController.php +++ b/app/Admin/Controllers/BannerController.php @@ -2,10 +2,8 @@ namespace App\Admin\Controllers; -use Slowlyo\OwlAdmin\Renderers\Page; -use Slowlyo\OwlAdmin\Renderers\Form; -use Slowlyo\OwlAdmin\Renderers\TableColumn; -use Slowlyo\OwlAdmin\Renderers\TextControl; +use Slowlyo\OwlAdmin\Renderers\{Page, Form}; +use Slowlyo\OwlAdmin\Renderers\{Component, TableColumn, TextControl, Image, Button, ImageControl, SelectControl, SwitchControl, DateTimeControl, InputKV, Status, Json}; use Slowlyo\OwlAdmin\Controllers\AdminController; use App\Services\Admin\BannerService; use App\Admin\Components; @@ -14,22 +12,28 @@ class BannerController extends AdminController { protected string $serviceName = BannerService::class; - protected string $pageTitle = '图片管理';//待完善-todo - public function list(): Page { $crud = $this->baseCRUD() ->filterTogglable(false) ->headerToolbar([ - $this->createButton(true), + $this->createButton(true, 'lg'), ...$this->baseHeaderToolBar(), ]) + ->quickSaveItemApi(admin_url('quick-edit/banners/$id')) + ->filter($this->baseFilter()->actions([])->body([ + SelectControl::make()->name('place_id')->label(__('banner.place_id'))->source(admin_url('api/banner-places/options'))->size('md'), + TextControl::make()->name('name')->label(__('banner.name'))->size('md'), + Button::make()->label(__('admin.reset'))->actionType('clear-and-submit'), + Component::make()->setType('submit')->label(__('admin.search'))->level('primary'), + ])) ->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), - $this->rowActions(true), + TableColumn::make()->name('place.name')->label(__('banner.place_id')), + TableColumn::make()->name('name')->label(__('banner.name')), + Image::make()->name('picture')->label(__('banner.picture'))->width(100), + TableColumn::make()->name('sort')->label(__('banner.sort'))->align('center')->quickEdit(Components::make()->sortControl('sort', __('article-category.sort'))->saveImmediately(true)), + TableColumn::make()->name('is_enable')->label(__('banner.is_enable'))->type('switch')->quickEdit(SwitchControl::make()->mode('inline')->saveImmediately(true)), + $this->rowActions(true, 'lg'), ]); return $this->baseList($crud); @@ -37,23 +41,31 @@ class BannerController extends AdminController public function form(): Form { - return $this->baseForm()->body([ - TextControl::make()->name('name')->label('名称')->required(true), - \amisMake()->ImageControl()->name('picture')->label('图片')->autoUpload(true), - Components::make()->parentControl('', 'place_id', '位置'), - Components::make()->sortControl(), - \amisMake()->DateTimeControl()->name('published_at')->label('发布时间')->description('*不填写则默认为创建时间'), - \amisMake()->SwitchControl()->name('is_enable')->label('显示'), + return $this->baseForm()->title('')->body([ + SelectControl::make()->name('place_id')->label(__('banner.place_id'))->source(admin_url('api/banner-places/options'))->required(true), + TextControl::make()->name('name')->label(__('banner.name'))->required(true), + ImageControl::make()->name('picture')->label(__('banner.picture'))->autoUpload(true)->required(true), + InputKV::make()->name('link_config')->label(__('banner.link_config'))->draggable(false), + Components::make()->sortControl('sort', __('banner.sort')), + DateTimeControl::make()->name('published_at')->label(__('banner.published_at'))->value(now())->format('YYYY-MM-DD HH:mm:ss')->description('*不填写则默认为创建时间'), + SwitchControl::make()->name('is_enable')->label(__('banner.is_enable'))->value(true), ]); } 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('更新时间') + return $this->baseDetail()->title('')->body([ + TextControl::make()->name('place.name')->label(__('banner.place_id'))->static(true), + TextControl::make()->name('name')->label(__('banner.name'))->static(true), + TextControl::make()->name('picture')->label(__('banner.picture'))->static(true)->staticSchema(Image::make()), + TextControl::make()->name('link_config')->label(__('banner.link_config'))->static(true)->staticSchema(Json::make()), + TextControl::make()->name('sort')->label(__('banner.sort'))->static(true), + TextControl::make()->name('published_at')->label(__('banner.published_at'))->static(true), + TextControl::make()->name('is_enable')->label(__('banner.is_enable'))->static(true)->staticSchema(Status::make()->source([ + ['label' => '不显示', 'icon' => 'fa fa-close', 'color' => '#cc292e'], + ['label' => '显示', 'icon' => 'fa fa-check', 'color' => '#30bf13'], + ])), + TextControl::make()->name('created_at')->label(__('banner.created_at'))->static(true), ]); } } diff --git a/app/Admin/Controllers/BannerPlaceController.php b/app/Admin/Controllers/BannerPlaceController.php index 35340e1..8d190d2 100644 --- a/app/Admin/Controllers/BannerPlaceController.php +++ b/app/Admin/Controllers/BannerPlaceController.php @@ -2,10 +2,8 @@ namespace App\Admin\Controllers; -use Slowlyo\OwlAdmin\Renderers\Page; -use Slowlyo\OwlAdmin\Renderers\Form; -use Slowlyo\OwlAdmin\Renderers\TableColumn; -use Slowlyo\OwlAdmin\Renderers\TextControl; +use Slowlyo\OwlAdmin\Renderers\{Form, Page}; +use Slowlyo\OwlAdmin\Renderers\{TableColumn, TextControl, SwitchControl, NumberControl, Status, Button, Component}; use Slowlyo\OwlAdmin\Controllers\AdminController; use App\Services\Admin\BannerPlaceService; @@ -13,7 +11,7 @@ class BannerPlaceController extends AdminController { protected string $serviceName = BannerPlaceService::class; - protected string $pageTitle = '图片位置';//待完善-todo + protected string $pageTitle = '图片位置'; public function list(): Page { @@ -23,11 +21,19 @@ class BannerPlaceController extends AdminController $this->createButton(true), ...$this->baseHeaderToolBar(), ]) + ->quickSaveItemApi(admin_url('quick-edit/banner-places/$id')) + ->filter($this->baseFilter()->actions([])->body([ + TextControl::make()->name('key')->label(__('banner-place.key'))->size('md'), + TextControl::make()->name('name')->label(__('banner-place.name'))->size('md'), + Button::make()->label(__('admin.reset'))->actionType('clear-and-submit'), + Component::make()->setType('submit')->label(__('admin.search'))->level('primary'), + ])) ->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), + TableColumn::make()->name('id')->label(__('banner-place.id')), + TableColumn::make()->name('key')->label(__('banner-place.key')), + TableColumn::make()->name('name')->label(__('banner-place.name')), + TableColumn::make()->name('is_enable')->type('switch')->label(__('banner-place.is_enable'))->quickEdit(SwitchControl::make()->saveImmediately(true)->mode('inline')), + TableColumn::make()->name('remark')->label(__('banner-place.remark'))->quickEdit(TextControl::make()->saveImmediately(true)), $this->rowActions(true), ]); @@ -36,10 +42,12 @@ class BannerPlaceController extends AdminController public function form(): Form { - return $this->baseForm()->body([ - TextControl::make()->name('name')->label('名称')->required(true), + return $this->baseForm()->title('')->body([ TextControl::make()->name('key')->label('KEY')->required(true), - \amisMake()->SwitchControl()->name('is_enable')->label('显示'), + TextControl::make()->name('name')->label('名称')->required(true), + NumberControl::make()->name('width')->label(__('banner-place.width'))->step(1)->min(0), + NumberControl::make()->name('height')->label(__('banner-place.height'))->step(1)->min(0), + SwitchControl::make()->name('is_enable')->label(__('banner-place.is_enable'))->value(true), TextControl::make()->name('remark')->label('备注'), ]); } @@ -47,10 +55,23 @@ class BannerPlaceController extends AdminController 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('更新时间') + TextControl::make()->static(true)->name('id')->label(__('banner-place.id')), + TextControl::make()->static(true)->name('name')->label(__('banner-place.name')), + TextControl::make()->static(true)->name('width')->label(__('banner-place.width')), + TextControl::make()->static(true)->name('height')->label(__('banner-place.height')), + TextControl::make()->name('is_enable')->label(__('banner-place.is_enable'))->static(true)->staticSchema(Status::make()->source([ + ['label' => '不显示', 'icon' => 'fa fa-close', 'color' => '#cc292e'], + ['label' => '显示', 'icon' => 'fa fa-check', 'color' => '#30bf13'], + ])), + TextControl::make()->static(true)->name('remark')->label(__('banner-place.remark')), + TextControl::make()->static(true)->name('created_at')->label(__('banner-place.created_at')), ]); } + + public function options() + { + $list = $this->service->listQuery()->select(['id as value', 'name as label'])->get(); + + return $this->response()->success($list); + } } diff --git a/app/Admin/routes.php b/app/Admin/routes.php index 1d8e6a8..f82afc8 100644 --- a/app/Admin/routes.php +++ b/app/Admin/routes.php @@ -14,6 +14,8 @@ Route::group([ ], 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('articles/options', [\App\Admin\Controllers\ArticleController::class, 'options'])->name('api.articles.options'); + $router->get('banner-places/options', [\App\Admin\Controllers\BannerPlaceController::class, 'options'])->name('api.banner-places.options'); $router->get('region-categories/tree-list', '\App\Admin\Controllers\RegionCategoryController@getTreeList')->name('api.region-categories.tree-list'); }); @@ -21,6 +23,7 @@ Route::group([ //公告管理 $router->resource('admin-notices', \App\Admin\Controllers\AdminNoticeController::class); + $router->post('quick-edit/admin-notices/{admin_notice}',[ \App\Admin\Controllers\AdminNoticeController::class, 'update']); // 文章分类 $router->resource('article-categories', \App\Admin\Controllers\ArticleCategoryController::class); $router->post('quick-edit/article-categories/{article_category}', [\App\Admin\Controllers\ArticleCategoryController::class, 'update']); @@ -28,10 +31,12 @@ Route::group([ // 文章管理 $router->resource('articles', \App\Admin\Controllers\ArticleController::class); $router->post('quick-edit/article/{article}', [\App\Admin\Controllers\ArticleController::class, 'update']); - //图片位置 + // 图片位置 $router->resource('banner-places', \App\Admin\Controllers\BannerPlaceController::class); - //图片管理 + $router->post('quick-edit/banner-places/{banner_place}',[\App\Admin\Controllers\BannerPlaceController::class, 'update']); + // 图片管理 $router->resource('banners', \App\Admin\Controllers\BannerController::class); + $router->post('quick-edit/banners/{banner}',[\App\Admin\Controllers\BannerController::class, 'update']); //友情链接 $router->resource('friend-links', \App\Admin\Controllers\FriendLinkController::class); //字典表 diff --git a/app/Filters/AdminNoticeFilter.php b/app/Filters/AdminNoticeFilter.php new file mode 100644 index 0000000..3a757d9 --- /dev/null +++ b/app/Filters/AdminNoticeFilter.php @@ -0,0 +1,21 @@ + [input_key1, input_key2]]. + * + * @var array + */ + public $relations = []; + + public function title($v) + { + $this->whereLike('title', $v); + } +} diff --git a/app/Filters/ArticleCategoryFilter.php b/app/Filters/ArticleCategoryFilter.php index 33c70bc..a5e95ce 100644 --- a/app/Filters/ArticleCategoryFilter.php +++ b/app/Filters/ArticleCategoryFilter.php @@ -6,6 +6,8 @@ use EloquentFilter\ModelFilter; class ArticleCategoryFilter extends ModelFilter { + protected $drop_id = false; + /** * Related Models that have ModelFilters as well as the method on the ModelFilter * As [relationMethod => [input_key1, input_key2]]. @@ -28,4 +30,9 @@ class ArticleCategoryFilter extends ModelFilter { $this->where('path', 'like', '%-'.$v.'-%'); } + + public function level($v) + { + $this->where('level', $v); + } } diff --git a/app/Filters/ArticleFilter.php b/app/Filters/ArticleFilter.php index fd57422..13df6f7 100644 --- a/app/Filters/ArticleFilter.php +++ b/app/Filters/ArticleFilter.php @@ -6,6 +6,8 @@ use EloquentFilter\ModelFilter; class ArticleFilter extends ModelFilter { + protected $drop_id = false; + /** * Related Models that have ModelFilters as well as the method on the ModelFilter * As [relationMethod => [input_key1, input_key2]]. diff --git a/app/Filters/BannerFilter.php b/app/Filters/BannerFilter.php new file mode 100644 index 0000000..feef705 --- /dev/null +++ b/app/Filters/BannerFilter.php @@ -0,0 +1,31 @@ + [input_key1, input_key2]]. + * + * @var array + */ + public $relations = []; + + public function place($id) + { + $this->where('place_id', $id); + } + + public function key($v) + { + $this->whereHas('place', fn ($q) => $q->enable()->whereIn('key', explode(',', $v))); + } + + public function name($v) + { + $this->whereLike('name', $v); + } +} diff --git a/app/Filters/BannerPlaceFilter.php b/app/Filters/BannerPlaceFilter.php new file mode 100644 index 0000000..f5df035 --- /dev/null +++ b/app/Filters/BannerPlaceFilter.php @@ -0,0 +1,25 @@ + [input_key1, input_key2]]. + * + * @var array + */ + public $relations = []; + + public function key($v) + { + $this->whereLike('key', $v); + } + public function name($v) + { + $this->whereLike('name', $v); + } +} diff --git a/app/Http/Controllers/Api/AdminNoticeController.php b/app/Http/Controllers/Api/AdminNoticeController.php new file mode 100644 index 0000000..691a668 --- /dev/null +++ b/app/Http/Controllers/Api/AdminNoticeController.php @@ -0,0 +1,31 @@ +all())->sort()->enable(); + + $list = $query->get(); + + return $this->json(AdminNoticeResource::collection($list)); + } + + public function show($id) + { + $info = AdminNotice::enable()->find($id); + + if (!$info) { + return $this->error('记录不存在'); + } + + return $this->json(AdminNoticeResource::make($info)); + } +} diff --git a/app/Http/Controllers/Api/ArticleController.php b/app/Http/Controllers/Api/ArticleController.php new file mode 100644 index 0000000..10049ed --- /dev/null +++ b/app/Http/Controllers/Api/ArticleController.php @@ -0,0 +1,48 @@ +all())->enable()->sort(); + + $list = $query->get(); + + return $this->json(ArticleCategoryResource::collection($list)); + } + + public function tree(Request $request) + { + $pid = $request->input('parent_id'); + $list = ArticleCategory::filter(['parent_path' => $pid])->enable()->sort()->select(['id', 'name', 'icon', 'parent_id'])->get()->toArray(); + + return $this->json(array2tree($list, $pid ?? 0)); + } + + public function index(Request $request) + { + $query = Article::filter($request->all())->enable()->sort(); + + $list = $query->paginate($request->input('per_page')); + + return $this->json(ArticleResource::collection($list)); + } + + public function show($id) + { + $info = Article::enable()->find($id); + + if (!$info) { + return $this->error('记录不存在'); + } + + return $this->json(ArticleResource::make($info)); + } +} diff --git a/app/Http/Controllers/Api/BannerController.php b/app/Http/Controllers/Api/BannerController.php new file mode 100644 index 0000000..85eb452 --- /dev/null +++ b/app/Http/Controllers/Api/BannerController.php @@ -0,0 +1,20 @@ +filter($request->all())->sort()->enable(); + + $list = $query->get(); + + return $this->json(BannerResource::collection($list)); + } +} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index a0a2a8a..359e510 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -6,8 +6,30 @@ use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Routing\Controller as BaseController; +use Illuminate\Pagination\LengthAwarePaginator; +use Illuminate\Http\Resources\Json\ResourceCollection ; +use Illuminate\Support\Arr; class Controller extends BaseController { use AuthorizesRequests, DispatchesJobs, ValidatesRequests; + + public function json($data, $code = 0, $message = '') + { + if ($data instanceof ResourceCollection ) { + $data = $data->resource; + } + $result = ['data' => $data ?: '', 'status' => $code, 'msg' => $message]; + return response()->json($result); + } + + public function success($message = '', $data = null) + { + return $this->json($data, 0, $message); + } + + public function error($message = '', $data = null) + { + return $this->json($data, 1, $message); + } } diff --git a/app/Http/Resources/AdminNoticeResource.php b/app/Http/Resources/AdminNoticeResource.php new file mode 100644 index 0000000..1f4e5f3 --- /dev/null +++ b/app/Http/Resources/AdminNoticeResource.php @@ -0,0 +1,27 @@ + $this->id, + 'title' => $this->title, + 'content' => $this->content, + 'article_id' => $this->article_id, + 'sort' => $this->sort, + 'published_at' => $this->published_at ? $this->published_at->timestamp : '', + 'created_at' => $this->created_at->timestamp + ]; + } +} diff --git a/app/Http/Resources/ArticleCategoryResource.php b/app/Http/Resources/ArticleCategoryResource.php new file mode 100644 index 0000000..177488d --- /dev/null +++ b/app/Http/Resources/ArticleCategoryResource.php @@ -0,0 +1,27 @@ + $this->id, + 'icon' => $this->icon, + 'name' => $this->name, + 'parent_id' => $this->parent_id, + 'level' => $this->level, + 'path' => $this->path, + 'sort' => $this->sort, + ]; + } +} diff --git a/app/Http/Resources/ArticleResource.php b/app/Http/Resources/ArticleResource.php new file mode 100644 index 0000000..8eda41a --- /dev/null +++ b/app/Http/Resources/ArticleResource.php @@ -0,0 +1,29 @@ + $this->id, + 'title' => $this->title, + 'sub_title' => $this->sub_title, + 'category_id' => $this->category_id, + 'category' => ArticleCategoryResource::make($this->whenLoaded('category')), + 'content' => $this->content, + 'cover' => $this->cover, + 'published_at' => $this->published_at ? $this->published_at->timestamp : '', + 'created_at' => $this->created_at->timestamp + ]; + } +} diff --git a/app/Http/Resources/BannerResource.php b/app/Http/Resources/BannerResource.php new file mode 100644 index 0000000..9e82dde --- /dev/null +++ b/app/Http/Resources/BannerResource.php @@ -0,0 +1,29 @@ + $this->id, + 'place_id' => $this->place_id, + 'key' => data_get($this->whenLoaded('place'), 'key'), + 'name' => $this->name, + 'picture' => $this->picture, + 'link_config' => $this->link_config, + 'sort' => $this->sort, + 'published_at' => $this->published_at ? $this->published_at->timestamp : '', + 'created_at' => $this->created_at->timestamp, + ]; + } +} diff --git a/app/Models/AdminNotice.php b/app/Models/AdminNotice.php index 496c4c1..42eaf32 100644 --- a/app/Models/AdminNotice.php +++ b/app/Models/AdminNotice.php @@ -2,18 +2,41 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use EloquentFilter\Filterable; +/** + * 公告 + */ class AdminNotice extends Model { - use HasFactory; use Filterable; protected $fillable = ['title', 'content', 'article_id', 'remark', 'is_enable', 'published_at' ,'sort']; + protected $casts = [ + 'published_at' => 'datetime:Y-m-d H:i:s', + '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'); + public function article() + { + return $this->belongsTo(Article::class, 'article_id'); + } + + public function scopeSort($q) + { + return $q->orderBy('sort', 'desc')->orderBy('published_at', 'desc'); + } + + public function scopeEnable($q) + { + return $q->where('is_enable', 1)->where('published_at', '<=', now()); + } + + public function modelFilter() + { + return \App\Filters\AdminNoticeFilter::class; } } diff --git a/app/Models/Article.php b/app/Models/Article.php index 9d37435..c45a8d1 100644 --- a/app/Models/Article.php +++ b/app/Models/Article.php @@ -42,8 +42,13 @@ class Article extends Model return $q->orderBy('sort', 'desc')->orderBy('published_at', 'desc'); } - public function scopeShow($q) + public function scopeEnable($q) { - return $q->where('is_enable', 1)->where(fn ($q1) => $q1->whereNull('published_at')->orWhere('published_at', '<=', now())); + return $q->where('is_enable', 1)->where('published_at', '<=', now()); + } + + public function modelFilter() + { + return \App\Filters\ArticleFilter::class; } } diff --git a/app/Models/ArticleCategory.php b/app/Models/ArticleCategory.php index 75703f5..42279c3 100644 --- a/app/Models/ArticleCategory.php +++ b/app/Models/ArticleCategory.php @@ -40,13 +40,23 @@ class ArticleCategory extends Model return $this->hasMany(static::class, 'parent_id'); } + public function articles() + { + return $this->hasMany(Article::class, 'category_id'); + } + public function scopeSort($q) { return $q->orderBy('sort', 'desc'); } - public function scopeShow($q) + public function scopeEnable($q) { return $q->where('is_enable', 1); } + + public function modelFilter() + { + return \App\Filters\ArticleCategoryFilter::class; + } } diff --git a/app/Models/Banner.php b/app/Models/Banner.php index f007d2b..1f96f01 100644 --- a/app/Models/Banner.php +++ b/app/Models/Banner.php @@ -2,10 +2,52 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Support\Str; +use App\Filters\BannerFilter; +use EloquentFilter\Filterable; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Storage; +use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Factories\HasFactory; class Banner extends Model { - use HasFactory; + use Filterable; + + protected $fillable = ['is_enable', 'link_config', 'name', 'picture', 'place_id', 'published_at', 'sort']; + + protected $casts = [ + 'created_at' => 'datetime:Y-m-d H:i:s', + 'updated_at' => 'datetime:Y-m-d H:i:s', + 'published_at' => 'datetime:Y-m-d H:i:s', + 'is_enable' => 'boolean', + 'link_config' => 'json', + ]; + + protected function picture(): Attribute + { + return Attribute::make( + get: fn($value) => $value ? (Str::startsWith($value, ['http://', 'https://']) ? $value : Storage::url($value)) : '', + ); + } + + public function place() + { + return $this->belongsTo(BannerPlace::class, 'place_id'); + } + + public function scopeSort($q) + { + return $q->orderBy('sort', 'desc'); + } + + public function scopeEnable($q) + { + return $q->where('is_enable', 1)->where('published_at', '<=', now()); + } + + public function modelFilter() + { + return BannerFilter::class; + } } diff --git a/app/Models/BannerPlace.php b/app/Models/BannerPlace.php index 069e84d..83a04f4 100644 --- a/app/Models/BannerPlace.php +++ b/app/Models/BannerPlace.php @@ -2,10 +2,33 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use EloquentFilter\Filterable; class BannerPlace extends Model { - use HasFactory; + use Filterable; + + protected $fillable = ['height', 'is_enable', 'key', 'name', 'remark', 'width']; + + protected $casts = [ + 'created_at' => 'datetime:Y-m-d H:i:s', + 'updated_at' => 'datetime:Y-m-d H:i:s', + 'is_enable' => 'boolean', + ]; + + public function banners() + { + return $this->hasMany(Banner::class, 'place_id'); + } + + public function scopeEnable($q) + { + return $q->where('is_enable', 1); + } + + public function scopeSort($q) + { + return $q->orderBy('id', 'desc'); + } } diff --git a/app/Services/Admin/AdminNoticeService.php b/app/Services/Admin/AdminNoticeService.php index 7081cf6..57a5ed3 100644 --- a/app/Services/Admin/AdminNoticeService.php +++ b/app/Services/Admin/AdminNoticeService.php @@ -3,13 +3,34 @@ namespace App\Services\Admin; use App\Models\AdminNotice; -use Slowlyo\OwlAdmin\Services\AdminService; +use App\Filters\AdminNoticeFilter; /** * @method AdminNotice getModel() * @method AdminNotice|\Illuminate\Database\Query\Builder query() */ -class AdminNoticeService extends AdminService +class AdminNoticeService extends BaseService { protected string $modelName = AdminNotice::class; + + protected string $modelFilterName = AdminNoticeFilter::class; + + protected array $withRelationships = ['article']; + + public function listQuery() + { + $model = $this->getModel(); + $filter = $this->getModelFilter(); + + $query = $this->query(); + if($this->withRelationships){ + $query->with($this->withRelationships); + } + + if ($filter) { + $query->filter(request()->input(), $filter); + } + + return $query->sort(); + } } diff --git a/app/Services/Admin/BannerPlaceService.php b/app/Services/Admin/BannerPlaceService.php index 8775178..2e6968f 100644 --- a/app/Services/Admin/BannerPlaceService.php +++ b/app/Services/Admin/BannerPlaceService.php @@ -2,14 +2,41 @@ namespace App\Services\Admin; -use App\Models\BannerPlace; -use Slowlyo\OwlAdmin\Services\AdminService; +use App\Models\{BannerPlace, Banner}; +use App\Filters\BannerPlaceFilter; /** * @method BannerPlace getModel() * @method BannerPlace|\Illuminate\Database\Query\Builder query() */ -class BannerPlaceService extends AdminService +class BannerPlaceService extends BaseService { protected string $modelName = BannerPlace::class; + + protected string $modelFilterName = BannerPlaceFilter::class; + + public function listQuery() + { + $model = $this->getModel(); + $filter = $this->getModelFilter(); + + $query = $this->query(); + if($this->withRelationships){ + $query->with($this->withRelationships); + } + + if ($filter) { + $query->filter(request()->input(), $filter); + } + + return $query->sort(); + } + + public function delete(string $ids): mixed + { + $id = explode(',', $ids); + // 删除广告 + Banner::whereIn('place_id', $id)->delete(); + return parent::delete($ids); + } } diff --git a/app/Services/Admin/BannerService.php b/app/Services/Admin/BannerService.php index c7b67f4..f2ee13f 100644 --- a/app/Services/Admin/BannerService.php +++ b/app/Services/Admin/BannerService.php @@ -3,13 +3,34 @@ namespace App\Services\Admin; use App\Models\Banner; -use Slowlyo\OwlAdmin\Services\AdminService; +use App\Filters\BannerFilter; /** * @method Banner getModel() * @method Banner|\Illuminate\Database\Query\Builder query() */ -class BannerService extends AdminService +class BannerService extends BaseService { protected string $modelName = Banner::class; + + protected array $withRelationships = ['place']; + + protected string $modelFilterName = BannerFilter::class; + + public function listQuery() + { + $model = $this->getModel(); + $filter = $this->getModelFilter(); + + $query = $this->query(); + if($this->withRelationships){ + $query->with($this->withRelationships); + } + + if ($filter) { + $query->filter(request()->input(), $filter); + } + + return $query->sort(); + } } diff --git a/database/factories/ArticleFactory.php b/database/factories/ArticleFactory.php new file mode 100644 index 0000000..5ba0c22 --- /dev/null +++ b/database/factories/ArticleFactory.php @@ -0,0 +1,40 @@ + + */ +class ArticleFactory extends Factory +{ + protected $model = Article::class; + /** + * Define the model's default state. + * + * @return array + */ + public function definition() + { + $imgs = [ + 'https://qiniu.abcdefg.fun/banner.png', + 'https://qiniu.abcdefg.fun/banner1.png', + 'https://qiniu.abcdefg.fun/banner2.png', + 'https://qiniu.abcdefg.fun/banner3.png', + ]; + $category = ArticleCategory::where('level', 3)->inRandomOrder()->first(); + return [ + 'author' => $this->faker->name(), + 'category_id' => $category->id, + 'category_path' => $category->path . $category->id . '-', + 'content' => $this->faker->text(), + 'cover' => $this->faker->randomElement($imgs), + 'published_at' => now(), + 'sort' => 0, + 'sub_title' => $this->faker->sentence, + 'title' => $this->faker->sentence, + ]; + } +} diff --git a/database/seeders/ArticleSeeder.php b/database/seeders/ArticleSeeder.php index f9e94fc..b6539c6 100644 --- a/database/seeders/ArticleSeeder.php +++ b/database/seeders/ArticleSeeder.php @@ -4,8 +4,9 @@ namespace Database\Seeders; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; -use App\Models\ArticleCategory; +use App\Models\{Article, ArticleCategory}; use Illuminate\Support\Arr; +use Database\Factories\ArticleFactory; class ArticleSeeder extends Seeder { @@ -71,6 +72,9 @@ class ArticleSeeder extends Seeder ]; ArticleCategory::truncate(); $this->createCategory($categoryList, 0); + + Article::truncate(); + (new ArticleFactory)->count(100)->create(); } protected function createCategory($list, $pid = 0) diff --git a/lang/zh_CN/admin-notice.php b/lang/zh_CN/admin-notice.php new file mode 100644 index 0000000..99f632c --- /dev/null +++ b/lang/zh_CN/admin-notice.php @@ -0,0 +1,13 @@ + 'ID', + 'title' => '标题', + 'content' => '内容', + 'article_id' => '关联文章', + 'remark' => '备注', + 'is_enable' => '显示', + 'published_at' => '发布时间', + 'sort' => '排序(倒序)', + 'created_at' => '创建时间', +]; diff --git a/lang/zh_CN/banner-place.php b/lang/zh_CN/banner-place.php new file mode 100644 index 0000000..2cb13e6 --- /dev/null +++ b/lang/zh_CN/banner-place.php @@ -0,0 +1,12 @@ + 'ID', + 'created_at' => '创建时间', + 'height' => '高度(px)', + 'is_enable' => '显示', + 'key' => 'KEY', + 'name' => '名称', + 'remark' => '备注', + 'width' => '宽度(px)', +]; diff --git a/lang/zh_CN/banner.php b/lang/zh_CN/banner.php new file mode 100644 index 0000000..46e1af9 --- /dev/null +++ b/lang/zh_CN/banner.php @@ -0,0 +1,13 @@ + 'ID', + 'created_at' => '创建时间', + 'is_enable' => '显示', + 'link_config' => '跳转', + 'name' => '名称', + 'picture' => '广告图', + 'place_id' => '位置', + 'published_at' => '发布时间', + 'sort' => '排序(倒序)', +]; diff --git a/routes/api.php b/routes/api.php index eb6fa48..09872b0 100644 --- a/routes/api.php +++ b/routes/api.php @@ -14,6 +14,12 @@ use Illuminate\Support\Facades\Route; | */ -Route::middleware('auth:sanctum')->get('/user', function (Request $request) { - return $request->user(); -}); +Route::get('banner', [\App\Http\Controllers\Api\BannerController::class, 'index']); + +Route::get('notice', [\App\Http\Controllers\Api\AdminNoticeController::class, 'index']); +Route::get('notice/{id}', [\App\Http\Controllers\Api\AdminNoticeController::class, 'show']); + +Route::get('article/category', [\App\Http\Controllers\Api\ArticleController::class, 'category']); +Route::get('article/tree', [\App\Http\Controllers\Api\ArticleController::class, 'tree']); +Route::get('article', [\App\Http\Controllers\Api\ArticleController::class, 'index']); +Route::get('article/{id}', [\App\Http\Controllers\Api\ArticleController::class, 'show']);