# 表结构
# 图片分类表
CREATE TABLE `ph_images_category` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`pid` int(11) DEFAULT '0' COMMENT '父级id,0为一级分类',
`name` varchar(20) DEFAULT NULL COMMENT '分类名称',
`cover_url` varchar(255) DEFAULT NULL COMMENT '分类封面图片',
`sort` smallint(5) DEFAULT '100' COMMENT '分类排序 越小越靠前',
`created_at` timestamp NULL DEFAULT NULL COMMENT '添加时间',
`updated_at` timestamp NULL DEFAULT NULL COMMENT '修改时间',
`deleted_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='图片分类表';
用到框架的 mode-tree 树形组件:https://laravel-admin.org/docs/zh/1.x/model-tree
1、创建模型、控制器、菜单
参照 2.5 节中的步骤,这里跳过
2、修改模型
组件的3个默认字段是:
parent_id - integer
order - integer
title - string
如果和默认字段一样,可以不重新指定字段
# 修改如下
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use DateTimeInterface;
use Encore\Admin\Traits\ModelTree;
class ImagesCategory extends Model
{
use HasFactory;
use SoftDeletes;
use ModelTree; // 使用数据模型树
// 数据库 mysql_api 中的表
protected $connection = 'mysql_api';
protected $table = "images_category";
/**
* 为数组 / JSON 序列化准备日期。
*
* @param \DateTimeInterface $date
* @return string
*/
protected function serializeDate(DateTimeInterface $date)
{
return $date->format('Y-m-d H:i:s');
}
/**
* 指定数据模型数的关联字段
*/
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
$this->setParentColumn('pid');
$this->setOrderColumn('sort');
$this->setTitleColumn('name');
}
}
3、修改控制器
<?php
namespace App\Admin\Controllers;
use App\Models\ImagesCategory;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Encore\Admin\Layout\Content;
use Encore\Admin\Show;
use Encore\Admin\Tree;
use Encore\Admin\Layout\Column;
use Encore\Admin\Layout\Row;
class ImagesCategoryController extends AdminController
{
/**
* Title for current resource.
*
* @var string
*/
protected $title = '图片分类';
/**
* Make a grid builder.
* 这个没用了
* @return Grid
*/
protected function grid()
{
$grid = new Grid(new ImagesCategory());
$grid->column('id', __('Id'));
$grid->column('pid', __('ImagesCategoryPid'));
$grid->column('name', __('ImagesCategoryName'));
$grid->column('cover_url', __('Cover url'))->image('',150,150);
$grid->column('sort', __('Sort'));
$grid->column('created_at', __('Created at'));
$grid->column('updated_at', __('Updated at'));
$grid->column('deleted_at', __('Deleted at'));
$grid->filter(function ($filter) {
// 范围过滤器,调用模型的`onlyTrashed`方法,查询出被软删除的数据。
$filter->scope('trashed', '回收站')->onlyTrashed();
});
return $grid;
}
/**
* Index interface.
* 这里重写首页实现树状结构
*
* @return Content
*/
public function index(Content $content)
{
return $content
->header($this->title)
->description('列表')->row(function (Row $row) {
// 左侧显示树形分类
$tree = new Tree(new ImagesCategory());
$tree->disableCreate();// 禁用新增按钮
// 修改返回结构
$tree->branch(function ($branch) {
if ($branch['cover_url']){
$src = config('filesystems.disks.admin.url') . '/' . $branch['cover_url'] ;
$logo = "<img src='$src' style='max-width:30px;max-height:30px;border-radius: 100%;' class='img'/>";
}else{
$logo = '';
}
return "{$branch['id']} - {$branch['name']} $logo";
});
$row->column(4, $tree);
// 右侧显示新增框
$row->column(8, function (Column $column) {
$column->append( $this->form() );
});
});
}
/**
* Make a show builder.
*
* @param mixed $id
* @return Show
*/
protected function detail($id)
{
$show = new Show(ImagesCategory::findOrFail($id));
$show->field('id', __('Id'));
$show->field('pid', __('ImagesCategoryPid'));
$show->field('name', __('ImagesCategoryName'));
$show->field('cover_url', __('Cover url'))->image('',200,200);
$show->field('sort', __('Sort'));
$show->field('created_at', __('Created at'));
$show->field('updated_at', __('Updated at'));
$show->field('deleted_at', __('Deleted at'));
return $show;
}
/**
* Make a form builder.
*
* @return Form
*/
protected function form()
{
$form = new Form(new ImagesCategory());
$form->select('pid', __('ImagesCategoryPid'))->options(ImagesCategory::selectOptions());// 使用数据模型树,用于选择父级
$form->text('name', __('ImagesCategoryName'))->required();
// 以合适比例裁剪成300x300,更多使用方法 http://image.intervention.io/api/fit
$form->image('cover_url', __('Cover url'))->removable()->fit(300);
$form->number('sort', __('Sort'))->default(100);
$form->setAction(admin_base_path('images-categories'));// 设置post数据到当前页,页面接收到会执行插入操作
$form->header()->disableList();// 移除跳转列表按钮
return $form;
}
}
现在大致功能已经完成,可实现分类拖拽然后保存等操作。
但是现在的表是用到软删除的,发现树形列表里并没有回收站这个功能,下面自己拓展一下。
4、自定义工具
先定义工具类app/Admin/Extensions/Tools/CategoryRecycle.php:
<?php
namespace App\Admin\Extensions\Tools;
use Encore\Admin\Admin;
use Encore\Admin\Grid\Tools\AbstractTool;
use Illuminate\Support\Facades\Request;
/*
* 分类回收站按钮工具类
* */
class CategoryRecycle extends AbstractTool
{
protected function script()
{
$url = Request::fullUrlWithQuery(['category_recycle' => '_category_recycle_']);
return <<<EOT
$('input:radio.category_recycle').change(function () {
var url = "$url".replace('_category_recycle_', $(this).val());
$.pjax({container:'#pjax-container', url: url });
});
EOT;
}
public function render()
{
Admin::script($this->script());
$options = [
'default' => '默认',
'd' => '回收站',
];
return view('admin.tools.category_recycle', compact('options'));
}
}
视图 admin.tools.category_recycle 文件为resources/views/admin/tools/category_recycle.blade.php:
<div class="btn-group" data-toggle="buttons">
@foreach($options as $option => $label)
<label class="btn btn-default btn-sm {{ \Request::get('category_recycle', 'default') == $option ? 'active' : '' }}">
<input type="radio" class="category_recycle" value="{{ $option }}">{{$label}}
</label>
@endforeach
</div>
控制器修改如下:
主要修改了tree获取数据的查询条件
<?php
namespace App\Admin\Controllers;
use App\Admin\Extensions\Tools\CategoryRecycle;
use App\Models\ImagesCategory;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Encore\Admin\Layout\Content;
use Encore\Admin\Show;
use Encore\Admin\Tree;
use Encore\Admin\Layout\Column;
use Encore\Admin\Layout\Row;
use Illuminate\Support\Facades\Request;
class ImagesCategoryController extends AdminController
{
/**
* Title for current resource.
*
* @var string
*/
protected $title = '图片分类';
/**
* Make a grid builder.
* 这个没用了
* @return Grid
*/
// protected function grid()
// {
// $grid = new Grid(new ImagesCategory());
//
// $grid->column('id', __('Id'));
// $grid->column('pid', __('ImagesCategoryPid'));
// $grid->column('name', __('ImagesCategoryName'));
// $grid->column('cover_url', __('Cover url'))->image('',150,150);
// $grid->column('sort', __('Sort'));
// $grid->column('created_at', __('Created at'));
// $grid->column('updated_at', __('Updated at'));
// $grid->column('deleted_at', __('Deleted at'));
// $grid->filter(function ($filter) {
// // 范围过滤器,调用模型的`onlyTrashed`方法,查询出被软删除的数据。
// $filter->scope('trashed', '回收站')->onlyTrashed();
// });
// return $grid;
// }
/**
* Index interface.
* 这里重写首页实现树状结构
*
* @return Content
*/
public function index(Content $content)
{
return $content
->header($this->title)
->description('列表')->row(function (Row $row) {
// 左侧显示树形分类
$tree = new Tree(new ImagesCategory());
// 添加查询条件
$tree->query(function ($model){
if (Request::get('category_recycle')=='d'){
$model = $model->onlyTrashed();
}
return $model;
});
$tree->disableCreate();// 禁用新增按钮
// 添加回收站按钮组
$tree->tools(function ($tools){
$tools->add((new CategoryRecycle())->render());
});
// 修改返回结构
$tree->branch(function ($branch) {
if ($branch['cover_url']){
$src = config('filesystems.disks.admin.url') . '/' . $branch['cover_url'] ;
$logo = "<img src='$src' style='max-width:30px;max-height:30px;border-radius: 100%;' class='img'/>";
}else{
$logo = '';
}
return "{$branch['id']} - {$branch['name']} $logo";
});
$row->column(4, $tree);
// 右侧显示新增框
$row->column(8, function (Column $column) {
$column->append( $this->form() );
});
});
}
/**
* Make a show builder.
*
* @param mixed $id
* @return Show
*/
protected function detail($id)
{
$show = new Show(ImagesCategory::findOrFail($id));
$show->field('id', __('Id'));
$show->field('pid', __('ImagesCategoryPid'));
$show->field('name', __('ImagesCategoryName'));
$show->field('cover_url', __('Cover url'))->image('',200,200);
$show->field('sort', __('Sort'));
$show->field('created_at', __('Created at'));
$show->field('updated_at', __('Updated at'));
$show->field('deleted_at', __('Deleted at'));
return $show;
}
/**
* Make a form builder.
*
* @return Form
*/
protected function form()
{
$form = new Form(new ImagesCategory());
$form->select('pid', __('ImagesCategoryPid'))->options(ImagesCategory::selectOptions());// 使用数据模型树,用于选择父级
$form->text('name', __('ImagesCategoryName'))->required();
// 以合适比例裁剪成300x300,更多使用方法 http://image.intervention.io/api/fit
$form->image('cover_url', __('Cover url'))->removable()->fit(300);
$form->number('sort', __('Sort'))->default(100);
// 只有index页面要使用这个,不然编辑页面不会在action的url里传编辑的id
if(!preg_match('/edit/',Request::path())){
$form->setAction(admin_base_path('images-categories'));// 设置post数据到当前页,页面接收到会执行插入操作
}
$form->header()->disableList();// 移除跳转列表按钮
return $form;
}
}
以下省略。。。