generated from panliang/owl-admin-starter
article
parent
fa8b07eb58
commit
5202921132
|
|
@ -77,14 +77,6 @@ class Components extends BaseRenderer
|
|||
->excludeKeys(['group-video']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典表下拉框
|
||||
*/
|
||||
public function keywordTypeControl($type)
|
||||
{
|
||||
return amisMake()->SelectControl()->source(admin_url('api/keywords/list').'?type_key='.$type);
|
||||
}
|
||||
|
||||
/**
|
||||
* switch
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use App\Admin\Components;
|
||||
use App\Admin\Services\ArticleService;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use Slowlyo\OwlAdmin\Renderers\Form;
|
||||
use Slowlyo\OwlAdmin\Renderers\Page;
|
||||
|
||||
class ArticleController extends AdminController
|
||||
{
|
||||
protected string $serviceName = ArticleService::class;
|
||||
|
||||
public function list(): Page
|
||||
{
|
||||
$crud = $this->baseCRUD()
|
||||
->filterTogglable(false)
|
||||
->columnsTogglable(false)
|
||||
->headerToolbar([
|
||||
$this->createButton(true, 'lg'),
|
||||
])
|
||||
->filter($this->baseFilter()->actions([])->body([
|
||||
amisMake()->TextControl()->name('title')->label(__('article.title'))->size('md')->clearable(),
|
||||
// amisMake()->Button()->label(__('admin.reset'))->actionType('clear-and-submit'),
|
||||
amisMake()->Component()->setType('submit')->label(__('admin.search'))->level('primary'),
|
||||
]))
|
||||
->quickSaveItemApi(admin_url('quick-edit/article/$id'))
|
||||
->columns([
|
||||
amisMake()->TableColumn()->name('id')->label(__('article.id'))->sortable(true),
|
||||
amisMake()->TableColumn()->name('title')->label(__('article.title')),
|
||||
amisMake()->TableColumn()->name('category.name')->label(__('article.category_id'))->className('text-primary'),
|
||||
amisMake()->Image()->name('cover')->label(__('article.cover'))->width(100),
|
||||
amisMake()->TableColumn()->name('sort')->label(__('article.sort'))->align('center')->quickEdit(Components::make()->sortControl('sort', __('article.sort'))->saveImmediately(true)),
|
||||
Components::make()->switchControl('table')->name('is_enable')->label(__('article.is_enable')),
|
||||
amisMake()->TableColumn()->name('published_at')->label(__('article.published_at')),
|
||||
$this->rowActions(true, 'lg'),
|
||||
]);
|
||||
|
||||
return $this->baseList($crud);
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
return $this->baseForm()->title('')->body([
|
||||
amisMake()->TextControl()->name('title')->label(__('article.title'))->required(true),
|
||||
Components::make()->parentControl(admin_url('api/keywords/tree-list'), 'category_id', __('article.category_id'))->onlyLeaf(true)->required(),
|
||||
amisMake()->ImageControl()->name('cover')->label(__('article.cover'))->autoUpload(true),
|
||||
Components::make()->sortControl('sort', __('article.sort')),
|
||||
amisMake()->DateTimeControl()->name('published_at')->label(__('article.published_at'))->value(now())->format('YYYY-MM-DD HH:mm:ss')->description('*不填写则默认为创建时间'),
|
||||
Components::make()->switchControl('form')->name('is_enable')->label(__('article.is_enable'))->value(true),
|
||||
Components::make()->fuEditorControl('content', __('article.content')),
|
||||
]);
|
||||
}
|
||||
|
||||
public function detail(): Form
|
||||
{
|
||||
return $this->baseDetail()->title('')->body([
|
||||
amisMake()->TextControl()->static(true)->name('id')->label(__('article.id')),
|
||||
amisMake()->TextControl()->static(true)->name('title')->label(__('article.title')),
|
||||
amisMake()->TextControl()->static(true)->name('category.name')->label(__('article.category_id')),
|
||||
amisMake()->TextControl()->name('cover')->label(__('article.cover'))->static(true)->staticSchema(amisMake()->Image()),
|
||||
amisMake()->TextControl()->static(true)->name('sort')->label(__('article.sort')),
|
||||
Components::make()->switchControl('show')->name('is_enable')->label(__('article.is_enable')),
|
||||
amisMake()->TextControl()->static(true)->name('published_at')->label(__('article.published_at')),
|
||||
amisMake()->TextControl()->static(true)->name('created_at')->label(__('article.created_at')),
|
||||
Components::make()->fuEditorControl('content', __('article.content'))->static(true),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Services;
|
||||
|
||||
use App\ModelFilters\ArticleFilter;
|
||||
use App\Models\Article;
|
||||
use App\Models\Keyword;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class ArticleService extends BaseService
|
||||
{
|
||||
protected array $withRelationships = ['category'];
|
||||
|
||||
protected string $modelName = Article::class;
|
||||
|
||||
protected string $modelFilterName = ArticleFilter::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 resloveData($data)
|
||||
{
|
||||
$cid = data_get($data, 'category_id');
|
||||
if ($cid && $category = Keyword::find($cid)) {
|
||||
$data['category_path'] = $category->path.$category->id.'-';
|
||||
}
|
||||
if (! data_get($data, 'published_at')) {
|
||||
$data['published_at'] = now();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function validate($data, $model = null)
|
||||
{
|
||||
$createRules = [
|
||||
];
|
||||
$updateRules = [
|
||||
];
|
||||
$validator = Validator::make($data, $model ? $updateRules : $createRules, [
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
return $validator->errors()->first();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Admin\Services;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Slowlyo\OwlAdmin\Services\AdminService;
|
||||
|
||||
/**
|
||||
|
|
@ -80,7 +81,7 @@ class BaseService extends AdminService
|
|||
{
|
||||
$data = $this->resloveData($data);
|
||||
$model = $this->query()->whereKey($primaryKey)->firstOrFail();
|
||||
$validate = $this->validate($data, $model->id);
|
||||
$validate = $this->validate($data, $model);
|
||||
if ($validate !== true) {
|
||||
$this->setError($validate);
|
||||
return false;
|
||||
|
|
@ -102,7 +103,7 @@ class BaseService extends AdminService
|
|||
|
||||
/**
|
||||
* 处理表单数据
|
||||
*
|
||||
*
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
|
|
@ -113,19 +114,19 @@ class BaseService extends AdminService
|
|||
|
||||
/**
|
||||
* 表单验证
|
||||
*
|
||||
*
|
||||
* @param array $data
|
||||
* @param int $id 空: 添加, 非空: 修改
|
||||
* @param Model $model 空: 添加, 非空: 修改
|
||||
* @return mixed true: 验证通过, string: 错误提示
|
||||
*/
|
||||
public function validate($data, $id = null)
|
||||
public function validate($data, $model = null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除的前置方法
|
||||
*
|
||||
*
|
||||
* @param array $ids 主键id
|
||||
* @return mixed true: 继续后续操作, string: 中断操作, 返回错误提示
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class KeywordService extends BaseService
|
|||
|
||||
/**
|
||||
* 删除的前置方法
|
||||
*
|
||||
*
|
||||
* @param array $ids 主键id
|
||||
* @return mixed true: 继续后续操作, string: 中断操作, 返回错误提示
|
||||
*/
|
||||
|
|
@ -49,16 +49,16 @@ class KeywordService extends BaseService
|
|||
return true;
|
||||
}
|
||||
|
||||
public function validate($data, $id = null)
|
||||
public function validate($data, $model = null)
|
||||
{
|
||||
$createRules = [
|
||||
'key' => ['required', Rule::unique('keywords', 'key')],
|
||||
'name' => ['required'],
|
||||
];
|
||||
$updateRules = [
|
||||
'key' => [Rule::unique('keywords', 'key')->ignore($id)]
|
||||
'key' => [Rule::unique('keywords', 'key')->ignore($model->id)]
|
||||
];
|
||||
$validator = Validator::make($data, $id ? $updateRules : $createRules, [
|
||||
$validator = Validator::make($data, $model ? $updateRules : $createRules, [
|
||||
'key.unique' => ':input 已经存在'
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ Route::group([
|
|||
|
||||
$router->resource('dashboard', \App\Admin\Controllers\HomeController::class);
|
||||
$router->get('menus', [\App\Admin\Controllers\HomeController::class, 'menus']);
|
||||
|
||||
|
||||
$router->get('login', [App\Admin\Controllers\AuthController::class, 'loginPage']);
|
||||
|
||||
$router->resource('system/settings', \App\Admin\Controllers\SettingController::class);
|
||||
|
|
@ -28,4 +28,7 @@ Route::group([
|
|||
|
||||
// 字典表
|
||||
$router->resource('keywords', \App\Admin\Controllers\KeywordsController::class)->names('admin.keywords');
|
||||
|
||||
$router->resource('articles', \App\Admin\Controllers\ArticleController::class);
|
||||
$router->post('quick-edit/article/{article}', [\App\Admin\Controllers\ArticleController::class, 'update']);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\ModelFilters;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
|
||||
class ArticleFilter extends ModelFilter
|
||||
{
|
||||
/**
|
||||
* Related Models that have ModelFilters as well as the method on the ModelFilter
|
||||
* As [relationMethod => [input_key1, input_key2]].
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $relations = [];
|
||||
}
|
||||
|
|
@ -25,11 +25,6 @@ class KeywordFilter extends ModelFilter
|
|||
$this->whereLike('key', $key);
|
||||
}
|
||||
|
||||
public function typeKey($key)
|
||||
{
|
||||
$this->where('type_key', $key);
|
||||
}
|
||||
|
||||
public function name($key)
|
||||
{
|
||||
$this->whereLike('name', $key);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use EloquentFilter\Filterable;
|
||||
use App\Traits\HasDateTimeFormatter;
|
||||
use App\Casts\StorageFile;
|
||||
|
||||
/**
|
||||
* 文章
|
||||
*/
|
||||
class Article extends Model
|
||||
{
|
||||
use Filterable, HasDateTimeFormatter;
|
||||
|
||||
protected $fillable = [
|
||||
'category_id', 'category_path', 'key',
|
||||
'title', 'cover', 'description', 'author', 'content',
|
||||
'files', 'medias', 'extension',
|
||||
'is_enable', 'is_recommend',
|
||||
'published_at', 'remarks', 'sort',
|
||||
'party_cate_id',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'cover' => StorageFile::class,
|
||||
'files' => 'array',
|
||||
'medias' => 'array',
|
||||
'extension' => 'array',
|
||||
'published_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo(Keyword::class, 'category_id');
|
||||
}
|
||||
|
||||
public function scopeSort($q)
|
||||
{
|
||||
return $q->orderBy('sort', 'asc')->latest('published_at')->latest('id');
|
||||
}
|
||||
|
||||
public function scopePublish($q)
|
||||
{
|
||||
return $q->where('published_at', '<=', now())->where('is_enable', 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ return [
|
|||
// 是否开启验证码
|
||||
'login_captcha' => env('ADMIN_LOGIN_CAPTCHA', true),
|
||||
// 是否开启鉴权
|
||||
'enable' => true,
|
||||
'enable' => false,
|
||||
// 用户模型
|
||||
'model' => \Slowlyo\OwlAdmin\Models\AdminUser::class,
|
||||
'controller' => \Slowlyo\OwlAdmin\Controllers\AuthController::class,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('articles', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('category_id')->comment('分类ID');
|
||||
$table->string('category_path')->default('-')->comment('分类 path');
|
||||
$table->string('title')->comment('文章标题');
|
||||
$table->string('key')->nullable('用于指定查询某一篇文章');
|
||||
$table->string('description')->nullable()->comment('描述');
|
||||
$table->string('author')->nullable()->comment('作者');
|
||||
$table->string('cover')->nullable()->comment('封面');
|
||||
$table->longText('content')->nullable()->comment('文章内容');
|
||||
$table->json('medias')->nullable()->comment('媒体[{name,url,type}]');
|
||||
$table->json('files')->nullable()->comment('附件[{name,url,type}]');
|
||||
$table->timestamp('published_at')->nullable()->comment('发布时间');
|
||||
$table->unsignedInteger('sort')->default(1)->comment('排序(asc)');
|
||||
$table->string('remarks')->nullable()->comment('备注');
|
||||
|
||||
$table->unsignedTinyInteger('is_recommend')->default(0)->comment('推荐状态');
|
||||
$table->unsignedTinyInteger('is_enable')->default(1)->comment('可用状态');
|
||||
|
||||
$table->json('extension')->nullable()->comment('扩展字段');
|
||||
$table->unsignedBigInteger('party_cate_id')->nullable()->comment('党支部ID');
|
||||
|
||||
$table->timestamps();
|
||||
|
||||
$table->comment('文章');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('articles');
|
||||
}
|
||||
};
|
||||
|
|
@ -19,6 +19,7 @@ class AdminMenuSeeder extends Seeder
|
|||
// 图标: https://iconpark.oceanengine.com/official
|
||||
$menus = [
|
||||
['title' => '主页', 'icon' => 'icon-park:home-two', 'url' => '/dashboard', 'is_home' => 1],
|
||||
['title' => '文章管理', 'icon' => 'icon-park:web-page', 'url' => '/articles'],
|
||||
['title' => '系统管理', 'icon' => 'icon-park:setting', 'url' => '/system', 'children' => [
|
||||
['title' => '用户管理', 'icon' => 'icon-park:people-plus', 'url' => '/system/admin_users'],
|
||||
['title' => '角色管理', 'icon' => 'icon-park:people-plus-one', 'url' => '/system/admin_roles'],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'id' => 'ID',
|
||||
'created_at' => '创建时间',
|
||||
'description' => '描述',
|
||||
'author' => '作者',
|
||||
'category_id' => '分类',
|
||||
'category_path' => '分类',
|
||||
'content' => '内容',
|
||||
'cover' => '封面图',
|
||||
'is_enable' => '显示',
|
||||
'is_recommend' => '推荐',
|
||||
'published_at' => '发布时间',
|
||||
'sort' => '排序(正序)',
|
||||
'title' => '标题',
|
||||
];
|
||||
Loading…
Reference in New Issue