背景
随着 dcat-admin 越来越多的人使用,相信有许多跟我一样热爱这个项目的的人最后也会参与到这个项目中来,从使用者到项目的维护者,可以为项目贡献一份自己的力量。我以后也会将维护这个项目的一些心得,底层代码的实现都以博文的形式分享给大家。
需求
有个同学提了一个这样的需求 ; 需要在表单 / 详情支持多栏布局,而这个需求刚好我自己用 dcat-admin 做项目时候也遇到过。尤其是 form 表单字段比较多的时候,我开始的解决方案是通过 form 的 tab 来减少表单一页的字段数量。
表单的多栏目布局
思路:我想的是 form 表单的字段外面包一次 row ,然后控制 row 里面每个字段的长宽。当我看了 dcat-admin 的代码后,发现是已经实现好了的,所以不需要自己在开发,我这里主要讲讲表单的多栏目布局的用法和底层代码怎么实现的。
效果:
使用代码:
在控制器创建一个 from 方法
protected function form() { return Form::make(new WxyMaterialItem(), function (Form $form) { $form->row(function (Form\Row $row) { $row->width(4)->text('name')->required(); $row->width(4)->text('id'); $row->width(4)->text('simple_code'); }); $form->row(function (Form\Row $row) { $row->width(6)->text('integral_money'); $row->width(6)->text('stock_min'); }); $form->row(function (Form\Row $row) { $row->width(12)->text('attribute'); }); $form->row(function (Form\Row $row) { $row->width(3)->text('price1'); $row->width(3)->text('price2'); $row->width(3)->text('price3'); $row->width(3)->text('status'); }); }); }
代码分析:
整个 form 表单渲染出来的流程如下
Dcat\Admin\Form 对象 -> 方法 rows 实例化一个 Dcat\Admin\Form\Row 对象并保存对象属性 -> 最后通过 render 方法渲染界面
这里面核心作用文件是 Dcat\Admin\Form\Row,我们可以看看里面的几个方法
width 方法
public function width($width = 12) { $this->defaultFieldWidth = $width; return $this; }
这个方法主要设置当前行的每一个显示字段的宽度,比如你一行显示三个字段
建议每个字段的宽度设置为 3,例如 $row->width (3)->text (‘name’);
__call 方法
public function __call($method, $arguments) { $field = $this->form->__call($method, $arguments); $field->disableHorizontal(); $this->fields[] = [ 'width' => $this->defaultFieldWidth, 'element' => $field, ]; return $field; }
这个方法主要是保存当前行要显示的字段的信息,通过 __call 方法去调用 Dcat\Admin\Form 的字段方法获取字段信息,如使用代码的 $row->width (4)->text (‘name’),会保存一个宽度 col-md-4 的 Dcat\Admin\Form\Field\Text 字段。
render 方法
public function render() { return view('admin::form.row', ['fields' => $this->fields]); }
admin::form.row 视图
<div class="row"> @foreach($fields as $field) <div class="col-md-{{ $field['width'] }}"> {!! $field['element']->render() !!} </div> @endforeach </div>
$field [‘element’]->render () 就是将字段渲染成 html
我们可以 dd 下 $this->fields , 看看其数据结构
详情的多栏目布局
思路:详情的多栏目布局是需要重新开发的,思路逻辑是和表单的多栏目布局类似的
主要是创建一个 Dcat\Admin\Show\Row 文件,里面的代码如下
<?php namespace Dcat\Admin\Show; use Dcat\Admin\Show; use Illuminate\Contracts\Support\Renderable; use Illuminate\Support\Collection; class Row implements Renderable { /** * Callback for add field to current row.s. * * @var \Closure */ protected $callback; /** * Parent show. * * @var Show */ protected $show; /** * @var Collection */ protected $fields; /** * Default field width for appended field. * * @var int */ protected $defaultFieldWidth = 12; /** * Row constructor. * * @param \Closure $callback * @param Show $show */ public function __construct(\Closure $callback, Show $show) { $this->callback = $callback; $this->show = $show; $this->fields = new Collection(); call_user_func($this->callback, $this); } /** * Render the row. * * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function render() { return view('admin::show.row', ['fields' => $this->fields]); } /** * @return Collection|\Dcat\Admin\Show\Field[] */ public function fields() { return $this->fields; } /** * Set width for a incomming field. * * @param int $width * * @return $this */ public function width($width = 12) { $this->defaultFieldWidth = $width; return $this; } /** * Add field. * * @param string $name * @param string $label * * @return \Dcat\Admin\Show\Field */ public function field($name, $label = '') { $field = $this->show->field($name, $label); $this->pushField($field); return $field; } /** * Add field. * * @param $name * * @return \Dcat\Admin\Show\Field|Collection */ public function __get($name) { $field = $this->show->field($name); $this->pushField($field); return $field; } /** * @param $method * @param $arguments * * @return \Dcat\Admin\Show\Field */ public function __call($method, $arguments) { $field = $this->show->__call($method, $arguments); $this->pushField($field); return $field; } /** * @param \Dcat\Admin\Show\Field $field * * @return void */ protected function pushField($field) { $this->fields->push([ 'width' => $this->defaultFieldWidth, 'element' => $field, ]); } }
里面 __ cal l, __get , field 三个方法都是获取当前行的字段信息,并保存到行的属性,在最后渲染详情的时候先循环 rows(这一步在 Dcat\Admin\Show\Panel 的 render 方法), 在通过上面代码中的 render 方法渲染 rows 的每个字段;如下:
html 如下
<div class="box-body"> <div class="form-horizontal mt-1"> @if($rows->isEmpty()) @foreach($fields as $field) {!! $field->render() !!} @endforeach @else <div> @foreach($rows as $row) {!! $row->render() !!} @endforeach </div> @endif <div class="clearfix"></div> </div> </div>
使用代码:
控制器创建一个 detail 方法
protected function detail($id) { return Show::make($id, new WxyMaterialItem("brands"), function (Show $show) { $show->row(function (Show\Row $row) { $row->width(6)->name; $row->width(6)->simple_code; }); $show->row(function (Show\Row $row) { $row->width(4)->specs; $row->width(4)->integral_money; $row->width(4)->field("aa", "你好"); }); $show->row(function (Show\Row $row) { $row->width(4)->mdept_id("部门"); $row->width(4)->status; $row->width(4)->field("brands.name", "品牌"); }); }); }