dcat-admin 詳情頁多欄佈局開發心得

喝卵形發表於2020-05-12

背景

隨著 dcat-admin 越來越多的人使用,相信有許多跟我一樣熱愛這個專案的的人最後也會參與到這個專案中來,從使用者到專案的維護者,可以為專案貢獻一份自己的力量。我以後也會將維護這個專案的一些心得,底層程式碼的實現都以博文的形式分享給大家。

需求

有個同學提了一個這樣的需求;需要在表單/詳情支援多欄佈局,而這個需求剛好我自己用 dcat-admin 做專案時候也遇到過。尤其是form表單欄位比較多的時候,我開始的解決方案是透過form的tab來減少表單一頁的欄位數量。

表單的多欄目佈局

思路:我想的是 form 表單的欄位外面包一次 row ,然後控制 row 裡面每個欄位的長寬。當我看了 dcat-admin 的程式碼後,發現是已經實現好了的,所以不需要自己在開發,我這裡主要講講表單的多欄目佈局的用法和底層程式碼怎麼實現的。
效果
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詳情頁多欄佈局開發心得

詳情的多欄目佈局

思路:詳情的多欄目佈局是需要重新開發的,思路邏輯是和表單的多欄目佈局類似的
主要是建立一個 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", "品牌");
              });
          });
      }
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章