Dcat\Admin自定义form组件

  • 以实现对json字段的key进行select封装SelectKeyValue组件

{"age": "11", "name": "张三", "height": "175"}
  • 实现效果

2022-08-12-11-07-01.gif

  • 继承Dcat\Admin\Form\Field, 实现文件 app\ExtField\SelectKeyValue.php

<?php

namespace App\ExtField;

use Dcat\Admin\Admin;
use Dcat\Admin\Form\Field;
use Dcat\Admin\Support\Helper;
use Illuminate\Support\Arr;

class SelectKeyValue extends Field
{
    // 自定义视图文件 resources\views\selectkeyvalue.blade.php
    protected $view = 'selectkeyvalue';

    const DEFAULT_FLAG_NAME = '_def_';

    /**
     * Fill data to the field.
     *
     * @param array $data
     *
     * @return array
     */
    public function formatFieldData($data)
    {
        $this->data = $data;

        return Helper::array(Arr::get($data, $this->normalizeColumn(), $this->value));
    }
    public function options($options = [])
    {
        if ($options instanceof \Closure) {
            $this->options = $options;

            return $this;
        }

        // remote options
        if (is_string($options)) {
            // reload selected
            if (class_exists($options) && in_array(Model::class, class_parents($options))) {
                return $this->model(...func_get_args());
            }

            return $this->loadRemoteOptions(...func_get_args());
        }

        $this->options = Helper::array($options);
        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getValidator(array $input)
    {
        if ($this->validator) {
            return $this->validator->call($this, $input);
        }

        if (!is_string($this->column)) {
            return false;
        }

        $rules = $attributes = [];

        if (!$fieldRules = $this->getRules()) {
            return false;
        }

        if (!Arr::has($input, $this->column)) {
            return false;
        }

        $rules["{$this->column}.keys.*"] = 'distinct';
        $rules["{$this->column}.values.*"] = $fieldRules;
        $attributes["{$this->column}.keys.*"] = __('Key');
        $attributes["{$this->column}.values.*"] = __('Value');

        $input = $this->prepareValidatorInput($input);

        return validator($input, $rules, $this->getValidationMessages(), $attributes);
    }

    protected function prepareValidatorInput(array $input)
    {
        Arr::forget($input, $this->column . '.' . static::DEFAULT_FLAG_NAME);

        return $input;
    }

    protected function addScript()
    {
        $value = old($this->column, $this->value());

        $number = $value ? count($value) : 0;
        $class = $this->getElementClassString();

        $this->script = <<<JS
(function () {
    var index = {$number};
    $('.{$class}-add').on('click', function () {
        var tpl = $('template.{$class}-tpl').html().replace('{key}', index).replace('{key}', index);
        $('tbody.kv-{$class}-table').append(tpl);

        index++;
    });

    $('tbody').on('click', '.{$class}-remove', function () {
        $(this).closest('tr').remove();
    });
})();
JS;
    }
    protected function prepareInputValue($value)
    {
        unset($value[static::DEFAULT_FLAG_NAME]);

        if (empty($value)) {
            return '[]';
        }

        return json_encode(array_combine($value['keys'], $value['values']));
    }
    // 主要是实现render()方法
    public function render()
    {
        $this->addScript();
        $this->addVariables([
            'options' => $this->options,
        ]);
        Admin::style('td .form-group {margin-bottom: 0 !important;}');

        return parent::render();
    }
}
  • 视图文件 resources\views\selectkeyvalue.blade.php

<div class="{{ $viewClass['form-group'] }}">

    <label class="{{ $viewClass['label'] }} control-label">{{ $label }}</label>

    <div class="{{ $viewClass['field'] }}">
        <span name="{{ $name }}"></span>
        <input name="{{ $name }}[{{ \Dcat\Admin\Form\Field\KeyValue::DEFAULT_FLAG_NAME }}]" type="hidden" />

        <div class="help-block with-errors"></div>

        <table class="table table-hover">
            <thead>
                <tr>
                    <th>{{ __('Key') }}</th>
                    <th>{{ __('Value') }}</th>
                    <th style="width: 85px;"></th>
                </tr>
            </thead>
            <tbody class="kv-{{ $class }}-table">

                @foreach (old("{$column}.keys", $value ?: []) as $k => $v)
                    @php($keysErrorKey = "{$column}.keys.{$loop->index}")
                    @php($valsErrorKey = "{$column}.values.{$loop->index}")

                    <tr>
                        <td>
                            <div class="form-group {{ $errors->has($keysErrorKey) ? 'has-error' : '' }}">
                                <div class="col-sm-12">
                                    <div class="help-block with-errors"></div>
                                    @if ($errors->has($keysErrorKey))
                                        @foreach ($errors->get($keysErrorKey) as $message)
                                            <label class="control-label" for="inputError"><i
                                                    class="feather icon-x-circle"></i>
                                                {{ $message }}</label><br />
                                        @endforeach
                                    @endif

                                    {{-- 默认键展示 --}}
                                    @if ($options)
                                        <div class="{{ $viewClass['field'] }}">
                                            <select class="form-control {{ $class }}" style="width: 100%;"
                                                name="{{ $name }}[keys][{{ $loop->index }}]"
                                                {!! $attributes !!}>
                                                <option value=""></option>
                                                @foreach ($options as $select => $option)
                                                    <option value="{{ $select }}"
                                                        {{ Dcat\Admin\Support\Helper::equal($select, old("{$column}.keys.{$k}", $k)) ? 'selected' : '' }}>
                                                        {{ $option }}</option>
                                                @endforeach
                                            </select>
                                        </div>
                                    @else
                                        <input name="{{ $name }}[keys][{{ $loop->index }}]"
                                            value="{{ old("{$column}.keys.{$k}", $k) }}" class="form-control"
                                            required />
                                    @endif

                                </div>
                            </div>
                        </td>
                        <td>
                            <div class="form-group {{ $errors->has($valsErrorKey) ? 'has-error' : '' }}">
                                <div class="col-sm-12">
                                    <div class="help-block with-errors"></div>
                                    @if ($errors->has($valsErrorKey))
                                        @foreach ($errors->get($valsErrorKey) as $message)
                                            <label class="control-label" for="inputError"><i
                                                    class="feather icon-x-circle"></i>
                                                {{ $message }}</label><br />
                                        @endforeach
                                    @endif
                                    <input name="{{ $name }}[values][{{ $loop->index }}]"
                                        value="{{ old("{$column}.values.{$k}", $v) }}" class="form-control" />
                                </div>
                            </div>
                        </td>

                        <td class="form-group">
                            <div>
                                <div class="{{ $class }}-remove btn btn-white btn-sm pull-right">
                                    <i class="feather icon-trash">&nbsp;</i>{{ __('admin.remove') }}
                                </div>
                            </div>
                        </td>
                    </tr>
                @endforeach
            </tbody>
            <tfoot>
                <tr>
                    <td></td>
                    <td></td>
                    <td>
                        {{-- 新增按钮 --}}
                        <div class="{{ $class }}-add btn btn-primary btn-outline btn-sm pull-right">
                            <i class="feather icon-save"></i>&nbsp;{{ __('admin.new') }}
                        </div>
                    </td>
                </tr>
            </tfoot>
        </table>
    </div>
</div>

<template class="{{ $class }}-tpl">
    <tr>
        <td>
            {{-- 这里进行操作 --}}
            @if ($options)
                <div class="{{ $viewClass['field'] }}">
                    <select class="form-control {{ $class }}" style="width: 100%;"
                        name="{{ $name }}[keys][{key}]" {!! $attributes !!}>
                        <option value=""></option>
                        @foreach ($options as $select => $option)
                            <option value="{{ $select }}"
                                {{ Dcat\Admin\Support\Helper::equal($select, old($column, $value)) ? 'selected' : '' }}>
                                {{ $option }}</option>
                        @endforeach
                    </select>
                </div>
            @else
                <div class="form-group  ">
                    <div class="col-sm-12">
                        <div class="help-block with-errors"></div>
                        <input name="{{ $name }}[keys][{key}]" class="form-control" required />
                    </div>
                </div>
            @endif

        </td>
        <td>
            <div class="form-group  ">
                <div class="col-sm-12">
                    <div class="help-block with-errors"></div>
                    <input name="{{ $name }}[values][{key}]" class="form-control" />
                </div>
            </div>
        </td>

        <td class="form-group">
            <div>
                <div class="{{ $class }}-remove btn btn-white btn-sm pull-right">
                    <i class="feather icon-trash">&nbsp;</i>{{ __('admin.remove') }}
                </div>
            </div>
        </td>
    </tr>
</template>
  • 配置到启动文件 app\Admin\bootstrap.php

// 最后增加即可
Dcat\Admin\Form::extend('selectKeyValue', \App\ExtField\SelectKeyValue::class);
  • 使用

protected function form()
  {
      return Form::make(new User(), function (Form $form) {
          $form->selectKeyValue('config')
              ->options([
                  'name' => '姓名',
                  'age' => '年龄'
              ]);
      });
  }