generated from liutk/owl-admin-base
完善文章分类
parent
57568e93ca
commit
3e354ad31f
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use Slowlyo\OwlAdmin\Renderers\Page;
|
||||
use Slowlyo\OwlAdmin\Renderers\Form;
|
||||
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;
|
||||
|
||||
public function list(): Page
|
||||
{
|
||||
$crud = $this->baseCRUD()
|
||||
//去掉分页-start
|
||||
->loadDataOnce(true)
|
||||
->footerToolbar([])
|
||||
//去掉分页-end
|
||||
->headerToolbar([
|
||||
$this->createButton(true, 'md'),
|
||||
amis('reload')->align('right'),
|
||||
amis('filter-toggler')->align('right'),
|
||||
])
|
||||
->filter($this->baseFilter()->body([
|
||||
|
||||
]
|
||||
))
|
||||
->columns([
|
||||
amis()->TableColumn()->make()->name('id')->label('ID')->sortable(true),
|
||||
amis()->TableColumn('name', __('admin.article_categories.name')),
|
||||
amis()->TableColumn('key', __('admin.article_categories.key'))->copyable(true),
|
||||
// amis()->TableColumn('cover', __('admin.article_categories.cover'))->type('image')->height('50px')->width('150px')->enlargeAble(true),
|
||||
amis()->TableColumn('is_enable', __('admin.article_categories.is_enable'))->type('switch'),
|
||||
amis()->TableColumn('is_show', __('admin.article_categories.is_show'))->type('switch'),
|
||||
amis()->TableColumn('is_recommend', __('admin.article_categories.is_recommend'))->type('switch'),
|
||||
amis()->TableColumn('sort', __('admin.article_categories.sort')),
|
||||
amis()->TableColumn('created_at', __('admin.created_at'))->type('datetime')->sortable(true),
|
||||
amisMake()->Operation()->label(__('admin.actions'))->buttons([
|
||||
$this->rowEditButton(true, 'md'),
|
||||
$this->rowDeleteButton(),
|
||||
]),
|
||||
]);
|
||||
|
||||
return $this->baseList($crud);
|
||||
}
|
||||
|
||||
public function form(): Form
|
||||
{
|
||||
return $this->baseForm()->body([
|
||||
Components::make()->parentControl(admin_url('api/article_categories/tree-list')),
|
||||
amis()->TextControl('name', __('admin.article_categories.name'))->required(true),
|
||||
// amis()->TextControl('key', __('admin.article_categories.key'))->required(true),
|
||||
Components::make()->cropImageControl('cover', __('admin.article_categories.cover')),
|
||||
Components::make()->sortControl('sort', __('admin.article_categories.sort')),
|
||||
amis()->SwitchControl('is_enable', __('admin.article_categories.is_enable'))->value(false),
|
||||
amis()->SwitchControl('is_show', __('admin.article_categories.is_show'))->value(false),
|
||||
amis()->SwitchControl('is_recommend', __('admin.article_categories.is_recommend'))->value(false),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getTreeList(Request $request){
|
||||
return $this->service->getTree();
|
||||
}
|
||||
}
|
||||
|
|
@ -59,9 +59,7 @@ class ArticleController extends AdminController
|
|||
->columns([
|
||||
amis()->TableColumn('id', __('admin.id'))->sortable(true),
|
||||
amis()->TableColumn('title', __('admin.articles.title'))->width('300px'),
|
||||
amis()->TableColumn('category', __('admin.articles.category'))->type('mapping')
|
||||
->map(Keyword::allChildrenOfKey('article_category')->pluck('name', 'key')->toArray())
|
||||
->itemSchema(amis()->Tag()->label('${item}')->color(Admin::setting()->get('system_theme_setting')['theme_color'] ?? '#1677ff')),
|
||||
amis()->TableColumn('cate.name', __('admin.articles.category')),
|
||||
amis()->TableColumn('tags', __('admin.articles.tags'))->type('mapping')->map(Keyword::tagsMap('article_tag')),
|
||||
amis()->TableColumn('cover', __('admin.articles.cover'))->type('image')->height('50px')->width('50px')->enlargeAble(true),
|
||||
amis()->TableColumn('published_at', __('admin.articles.published_at'))->remark(__('admin.articles.published_at_remark')),
|
||||
|
|
@ -83,14 +81,14 @@ class ArticleController extends AdminController
|
|||
amis()->Grid()->columns([
|
||||
amis()->Wrapper()->body([
|
||||
amis()->TextControl('title', __('admin.articles.title'))->required(true),
|
||||
Components::make()->parentControl(admin_url('api/keywords/tree-list?parent_name=article_category&has_owner=0'), 'category', __('admin.articles.category'), 'name', 'key'),
|
||||
Components::make()->parentControl(admin_url('api/article_categories/tree-list'), 'category', __('admin.articles.category')),
|
||||
Components::make()->keywordsTagControl('t_ids', __('admin.articles.tags'), 'article_tag'),
|
||||
Components::make()->cropImageControl('cover', __('admin.articles.cover')),
|
||||
Components::make()->sortControl('sort', __('admin.articles.sort')),
|
||||
amis()->DateTimeControl('published_at', __('admin.articles.published_at'))->format('YYYY-MM-DD HH:mm:ss')->description(__('admin.articles.published_at_remark')),
|
||||
amis()->SwitchControl('is_enable', __('admin.articles.is_enable'))->value(false),
|
||||
amis()->SwitchControl('is_recommend', __('admin.articles.is_recommend'))->value(false),
|
||||
Components::make()->fileControl('appendixes', __('admin.articles.appendixes'), '.xsl,.xlsx,.txt,.doc,.docx,.pdf,.pptx'),
|
||||
Components::make()->fileControl('appendixes', __('admin.articles.appendixes'), '.xsl,.xlsx,.txt,.doc,.docx,.pdf,.pptx', true),
|
||||
])->md(4),
|
||||
amis()->Wrapper()->body([
|
||||
Components::make()->fuEditorControl('content', __('admin.articles.content')),
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ Route::group([
|
|||
], function (Router $router) {
|
||||
$router->get('keywords/tree-list', '\App\Admin\Controllers\KeywordController@getTreeList')->name('api.keywords.tree-list');
|
||||
$router->get('institutions/tree-list', '\App\Admin\Controllers\InstitutionController@getTreeList')->name('api.institution.tree-list');
|
||||
$router->get('article_categories/tree-list', '\App\Admin\Controllers\ArticleCategoryController@getTreeList')->name('api.article_categories.tree-list');
|
||||
});
|
||||
|
||||
$router->resource('index', \App\Admin\Controllers\HomeController::class);
|
||||
|
|
@ -35,6 +36,8 @@ Route::group([
|
|||
|
||||
$router->resource('friend_links', \App\Admin\Controllers\FriendLinkController::class);
|
||||
|
||||
$router->resource('article_categories', \App\Admin\Controllers\ArticleCategoryController::class);
|
||||
|
||||
|
||||
//数据管理
|
||||
$router->resource('financial_cate', \App\Admin\Controllers\KeywordController::class);
|
||||
|
|
|
|||
|
|
@ -60,4 +60,9 @@ class Article extends Model
|
|||
get: fn($value) => $this->t_ids ? explode(',', $this->t_ids) : [],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function cate(){
|
||||
return $this->belongsTo(ArticleCategory::class, 'category');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use EloquentFilter\Filterable;
|
||||
|
||||
class ArticleCategory extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use Filterable;
|
||||
protected function serializeDate(\DateTimeInterface $date)
|
||||
{
|
||||
return $date->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
protected $fillable = [
|
||||
'name', 'key', 'cover',
|
||||
'parent_id', 'parent_key', 'lv', 'path',
|
||||
'is_enable', 'is_show', 'is_recommend',
|
||||
'sort',
|
||||
];
|
||||
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
// 监听 Keyword 的创建事件,用于初始化 path 和 lv 字段值
|
||||
static::saving(function ($keyword) {
|
||||
// 如果创建的是一个根类目
|
||||
if (! $keyword->parent_id) {
|
||||
// 将层级设为 1
|
||||
$keyword->lv = 1;
|
||||
// 将 path 设为 -
|
||||
$keyword->path = '-';
|
||||
} else {
|
||||
// 将层级设为父类目的层级 + 1
|
||||
$keyword->lv = $keyword->parent->lv +1;
|
||||
$keyword->parent_key = $keyword->parent->key;
|
||||
// 将 path 值设为父类目的 path 追加父类目 ID 以及最后跟上一个 - 分隔符
|
||||
$keyword->path = $keyword->parent->path.$keyword->parent_id.'-';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function parent()
|
||||
{
|
||||
return $this->belongsTo(static::class, 'parent_id');
|
||||
}
|
||||
|
||||
public function children()
|
||||
{
|
||||
return $this->hasMany(static::class, 'parent_id');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\Filters;
|
||||
|
||||
use EloquentFilter\ModelFilter;
|
||||
use App\Models\ArticleCategory;
|
||||
|
||||
class ArticleCategoryFilter extends ModelFilter
|
||||
{
|
||||
/**
|
||||
* 关键字
|
||||
*/
|
||||
public function name($name)
|
||||
{
|
||||
return $this->where('name','like', '%'.$name.'%')
|
||||
->orWhere('key','like', '%'.$name.'%');
|
||||
}
|
||||
|
||||
public function parentName($parent_name)
|
||||
{
|
||||
if(request('has_owner', 1)){
|
||||
$this->where(function($q) use ($parent_name){
|
||||
$q->where('name','like', '%'.$parent_name.'%')
|
||||
->orWhere('key','like', '%'.$parent_name.'%');
|
||||
});
|
||||
}
|
||||
return $this->orWhere('path','like', '%-'.
|
||||
ArticleCategory::where('name','like', '%'.$parent_name.'%')->orWhere('key','like', '%'.$parent_name.'%')->value('id')
|
||||
. '-%' ?? '');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\Admin;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use App\Models\ArticleCategory;
|
||||
use App\Models\Filters\ArticleCategoryFilter;
|
||||
use App\Traits\UploadTrait;
|
||||
|
||||
/**
|
||||
* @method ArticleCategory getModel()
|
||||
* @method ArticleCategory|\Illuminate\Database\Query\Builder query()
|
||||
*/
|
||||
class ArticleCategoryService extends BaseService
|
||||
{
|
||||
protected string $modelName = ArticleCategory::class;
|
||||
protected string $modelFilterName = ArticleCategoryFilter::class;
|
||||
|
||||
use UploadTrait;
|
||||
|
||||
public function getTree()
|
||||
{
|
||||
$list = $this->query()->filter(request()->all(), $this->modelFilterName)->get();
|
||||
$minNum = $list->min('parent_id');
|
||||
return !$list->isEmpty() ? array2tree($list->toArray(), $minNum) :[];
|
||||
}
|
||||
|
||||
public function parentIsChild($id, $pid): bool
|
||||
{
|
||||
$parent = $this->query()->find($pid);
|
||||
|
||||
do {
|
||||
if ($parent->parent_id == $id) {
|
||||
return true;
|
||||
}
|
||||
// 如果没有parent 则为顶级 退出循环
|
||||
$parent = $parent->parent;
|
||||
} while ($parent);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function list()
|
||||
{
|
||||
return ['items' => $this->getTree()];
|
||||
}
|
||||
|
||||
public function store($data): bool
|
||||
{
|
||||
if ($this->hasRepeated($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$columns = $this->getTableColumns();
|
||||
|
||||
$model = $this->getModel();
|
||||
|
||||
$data['cover'] = $this->saveImage('cover', 'article_category/cover')[0] ?? '';
|
||||
foreach ($data as $k => $v) {
|
||||
if (!in_array($k, $columns)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$model->setAttribute($k, $v);
|
||||
}
|
||||
|
||||
return $model->save();
|
||||
}
|
||||
|
||||
public function update($primaryKey, $data): bool
|
||||
{
|
||||
if ($this->hasRepeated($data, $primaryKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$columns = $this->getTableColumns();
|
||||
|
||||
$pid = Arr::get($data, 'parent_id');
|
||||
if ($pid != 0) {
|
||||
if ($this->parentIsChild($primaryKey, $pid)) {
|
||||
$this->setError('父级不允许设置为当前子级');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$model = $this->query()->whereKey($primaryKey)->first();
|
||||
|
||||
if(isset($data['cover'])){
|
||||
$data['cover'] = $this->saveImage('cover', 'article_category/cover')[0] ?? '';
|
||||
}
|
||||
foreach ($data as $k => $v) {
|
||||
if (!in_array($k, $columns)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$model->setAttribute($k, $v);
|
||||
}
|
||||
|
||||
return $model->save();
|
||||
}
|
||||
|
||||
|
||||
public function hasRepeated($data, $id = 0): bool
|
||||
{
|
||||
$query = $this->query()->when($id, fn($query) => $query->where('id', '<>', $id));
|
||||
|
||||
if (isset($data['key']) && (clone $query)->where('key', $data['key'])->exists()) {
|
||||
$this->setError('KEY重复');
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function delete(string $ids): mixed
|
||||
{
|
||||
$ids = explode(',', $ids);
|
||||
if(count($ids) == 1){
|
||||
$this->query()->where('path', 'like', '%-'.$ids[0].'-%')->delete();
|
||||
}
|
||||
|
||||
return $this->query()->whereIn('id', $ids)->delete();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<?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('article_categories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name')->comment('名称');
|
||||
$table->string('key')->nullable()->unique();
|
||||
$table->string('cover')->nullable()->comment('封面');
|
||||
|
||||
$table->unsignedBigInteger('parent_id')->default(0)->comment('上级ID');
|
||||
$table->string('parent_key')->nullable('上级key');
|
||||
$table->unsignedInteger('lv')->default(1)->comment('层级');
|
||||
$table->string('path')->default('-')->comment('所有的父级ID');
|
||||
|
||||
$table->unsignedTinyInteger('is_enable')->default(1)->comment('启用开关');
|
||||
$table->unsignedTinyInteger('is_show')->default(0)->comment('显示开关');
|
||||
$table->unsignedTinyInteger('is_recommend')->default(0)->comment('推荐开关');
|
||||
|
||||
$table->unsignedInteger('sort')->default(0)->comment('排序');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('article_categories');
|
||||
}
|
||||
};
|
||||
|
|
@ -35,6 +35,8 @@ class AdminMenuSeeder extends Seeder
|
|||
'children' => [
|
||||
['title'=>'ads', 'icon'=>'lets-icons:img-box','url'=>'/ads', 'order'=>0],
|
||||
['title'=>'friend_links', 'icon'=>'mdi:link-variant','url'=>'/friend_links', 'order'=>1],
|
||||
['title'=>'article_categories', 'icon'=>'tabler:category-2', 'url'=>'/article_categories', 'order'=>2],
|
||||
['title'=>'articles', 'icon'=>'ic:outline-article','url'=>'/articles', 'order'=>3],
|
||||
]
|
||||
],
|
||||
['title' => 'data_content', 'icon' => 'ph:codesandbox-logo-light', 'url' => '/data_content', 'order'=>3, //数据管理
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Slowlyo\OwlAdmin\Models\AdminMenu;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Throwable;
|
||||
|
||||
class ArticleCategorySeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$oldCates = DB::table('t_article_type')->get()->sortBy('pid');
|
||||
|
||||
$newCategories = [];
|
||||
foreach($oldCates as $cate){
|
||||
if(empty($cate->name)){
|
||||
continue;
|
||||
}
|
||||
$_category = [
|
||||
'id' => $cate -> id,
|
||||
'parent_id' => $cate -> pid ?? 0,
|
||||
'name' => $cate->name,
|
||||
'cover' => $cate->icon ?? null,
|
||||
'sort' => $cate -> sorted,
|
||||
'is_recommend' => $cate->recommend,
|
||||
'is_show' => $cate->showInOut,
|
||||
'lv' => !empty($cate->pid) ? $newCategories[$cate->pid]['lv'] + 1:1,
|
||||
'path' => !empty($cate->pid) ? $newCategories[$cate->pid]['path'] . $cate->pid .'-':'-',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
|
||||
$newCategories[$cate->id] = $_category;
|
||||
}
|
||||
|
||||
if(count($newCategories) > 0){
|
||||
DB::table('article_categories')->truncate();
|
||||
try {
|
||||
DB::begintransaction();
|
||||
DB::table('article_categories')->insert($newCategories);
|
||||
DB::commit();
|
||||
} catch (Throwable $th) {
|
||||
DB::rollBack();
|
||||
report($th);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -326,5 +326,16 @@ return [
|
|||
'cover'=> '图片',
|
||||
'sort' => '排序',
|
||||
'is_enable'=>'显示',
|
||||
]
|
||||
],
|
||||
'article_categories' => [
|
||||
'name' => '名称',
|
||||
'parent' => '上级分类',
|
||||
'parent_id' => '上级分类',
|
||||
'key' => 'KEY',
|
||||
'cover'=>'图标',
|
||||
'sort'=>'排序',
|
||||
'is_enable' => '启用',
|
||||
'is_show' => '展示',
|
||||
'is_recommend' => '推荐'
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -34,4 +34,5 @@ return [
|
|||
'welfare_cate' => '福利类型',
|
||||
'job_cate' => '工种管理',
|
||||
'friend_links' => '友情链接',
|
||||
'article_categories'=> '文章分类',
|
||||
];
|
||||
|
|
|
|||
Loading…
Reference in New Issue