jqh hace 5 años
padre
commit
39a5966530

+ 10 - 2
resources/assets/dcat/js/extensions/Helpers.js

@@ -246,15 +246,23 @@ export default class Helpers {
     }
 
     // 异步加载
-    asyncRender(url, callback) {
+    asyncRender(url, done, error) {
         let Dcat = this.dcat;
 
         $.ajax(url).then(function (data) {
-            callback(
+            done(
                 Dcat.assets.executeScripts(data, function () {
                     Dcat.triggerReady();
                 }).render()
             );
+        }, function (a, b, c) {
+            if (error) {
+                if (error(a, b, c) === false) {
+                    return false;
+                }
+            }
+
+            Dcat.handleAjaxError(a, b, c);
         })
     }
 }

+ 3 - 3
resources/assets/dcat/js/extensions/RowSelector.js

@@ -22,10 +22,10 @@ export default class RowSelector {
     _bind() {
         let options = this.options,
             checkboxSelector = options.checkboxSelector,
-            $selectAllSelector = $(options.selectAllSelector),
+            $selectAll = $(options.selectAllSelector),
             $checkbox = $(checkboxSelector);
 
-        $selectAllSelector.on('change', function() {
+        $selectAll.on('change', function() {
             $(this).parents(options.container).find(checkboxSelector).prop('checked', this.checked).trigger('change');
         });
         if (options.clickRow) {
@@ -47,7 +47,7 @@ export default class RowSelector {
                 tr.css('background-color', options.background);
 
                 if ($(checkboxSelector + ':checked').length === $checkbox.length) {
-                    $selectAllSelector.prop('checked', true)
+                    $selectAll.prop('checked', true)
                 }
             } else {
                 tr.css('background-color', '');

+ 2 - 0
resources/assets/dcat/sass/components/_grid.scss

@@ -58,7 +58,9 @@
     //background:$body-bg;
     padding:1.5rem
   }
+}
 
+.table-card {
   .filter-box {
     background: transparent;
     box-shadow: none!important;

+ 27 - 0
resources/views/form/selecttable.blade.php

@@ -0,0 +1,27 @@
+<div class="{{$viewClass['form-group']}} {!! !$errors->has($column) ?: 'has-error' !!}">
+    <label class="{{$viewClass['label']}} control-label">{!! $label !!}</label>
+    <div class="{{$viewClass['field']}} select-resource">
+        @include('admin::form.error')
+
+        <div class="input-group">
+            <div {!! $attributes !!}>
+                <span class="default-text" style="opacity:0.75">{{ $placeholder }}</span>
+                <span class="option d-none"></span>
+            </div>
+
+            @if(! $disabled)
+                <input name="{{ $name }}" type="hidden" id="hidden-{{ $id }}" value="{{ implode(',', \Dcat\Admin\Support\Helper::array($value)) }}" />
+            @endif
+            <div class="input-group-append">
+                <div class="btn btn-{{ $style }} " data-toggle="modal" data-target="#{{ $id }}">
+                    &nbsp;<i class="feather icon-arrow-up"></i>&nbsp;
+                </div>
+            </div>
+        </div>
+
+        {!! $modal !!}
+
+        @include('admin::form.help-block')
+
+    </div>
+</div>

+ 4 - 0
src/Color.php

@@ -55,6 +55,7 @@ use Illuminate\Support\Traits\Macroable;
  * @method string grayBg(int $amt = 0)
  * @method string border(int $amt = 0)
  * @method string inputBorder(int $amt = 0)
+ * @method string background(int $amt = 0)
  */
 class Color
 {
@@ -176,6 +177,9 @@ class Color
 
         // 表单边框
         'input-border' => '#d9d9d9',
+
+        // 背景色
+        'background' => '#eff3f8',
     ];
 
     /**

+ 2 - 0
src/Form.php

@@ -83,6 +83,7 @@ use Symfony\Component\HttpFoundation\Response;
  * @method Field\Range                  range($start, $end, $label = '')
  * @method Field\Color                  color($column, $label = '')
  * @method Field\ArrayField             array($column, $labelOrCallback, $callback = null)
+ * @method Field\SelectTable            selectTable($column, $label = '')
  */
 class Form implements Renderable
 {
@@ -165,6 +166,7 @@ class Form implements Renderable
         'range'          => Field\Range::class,
         'color'          => Field\Color::class,
         'array'          => Field\ArrayField::class,
+        'selectTable'    => Field\SelectTable::class,
     ];
 
     /**

+ 3 - 0
src/Form/Field/SelectResource.php

@@ -8,6 +8,9 @@ use Dcat\Admin\IFrameGrid;
 use Dcat\Admin\Support\Helper;
 use Illuminate\Contracts\Support\Arrayable;
 
+/**
+ * @deprecated 即将在2.0版本中废弃
+ */
 class SelectResource extends Field
 {
     use PlainInput;

+ 259 - 0
src/Form/Field/SelectTable.php

@@ -0,0 +1,259 @@
+<?php
+
+namespace Dcat\Admin\Form\Field;
+
+use Dcat\Admin\Form\Field;
+use Dcat\Admin\Support\Helper;
+use Dcat\Admin\Widgets\TableModal;
+use Dcat\Admin\Grid\LazyRenderable;
+
+class SelectTable extends Field
+{
+    use PlainInput;
+
+    /**
+     * @var TableModal
+     */
+    protected $modal;
+
+    protected $style = 'primary';
+
+    public function __construct($column, $arguments = [])
+    {
+        parent::__construct($column, $arguments);
+
+        $this->modal = TableModal::title($this->label);
+    }
+
+    /**
+     * 设置弹窗标题.
+     *
+     * @param string $title
+     *
+     * @return $this
+     */
+    public function title($title)
+    {
+        $this->modal->title($title);
+
+        return $this;
+    }
+
+    /**
+     * 设置尺寸为 xl.
+     *
+     * @return $this
+     */
+    public function xl()
+    {
+        $this->modal->xl();
+
+        return $this;
+    }
+
+    /**
+     * 设置表格异步渲染实例.
+     *
+     * @param LazyRenderable $renderable
+     *
+     * @return $this
+     */
+    public function from(LazyRenderable $renderable)
+    {
+        $this->modal->body($renderable);
+
+        return $this;
+    }
+
+    /**
+     * 转化为数组格式保存.
+     *
+     * @param mixed $value
+     *
+     * @return array|mixed
+     */
+    public function prepareInputValue($value)
+    {
+        return Helper::array($value, true);
+    }
+
+    protected function formatOptions()
+    {
+        $value = Helper::array(old($this->column, $this->value()));
+
+        if ($this->options instanceof \Closure) {
+            $this->options = $this->options->call($this->values(), $value, $this);
+        }
+
+        $values = [];
+
+        foreach (Helper::array($this->options) as $id => $label) {
+            foreach ($value as $v) {
+                if ($v == $id && $v !== null) {
+                    $values[] = ['id' => $v, 'label' => $label];
+                }
+            }
+        }
+
+        $this->options = json_encode($values);
+    }
+
+    protected function addScript()
+    {
+        $this->script .= <<<JS
+(function () {
+    var modal = $(replaceNestedFormIndex('#{$this->modal->getId()}'));
+    var input = $(replaceNestedFormIndex('#hidden-{$this->id}'));
+    var options = {$this->options};
+    
+    function getSelectedRows() {
+        var selected = [], ids = [];
+    
+        modal.find('.checkbox-grid-column input[type="checkbox"]:checked').each(function() {
+            var id = $(this).data('id'), i, exist;
+    
+            for (i in selected) {
+                if (selected[i].id === id) {
+                    exist = true
+                }
+            }
+    
+            if (! exist) {
+                selected.push({'id': id, 'label': $(this).data('label')});
+                ids.push(id)
+            }
+        });
+    
+        return [selected, ids];
+    }
+    
+    function setKeys(ids) {
+        input.val(ids.length ? ids.join(',') : '');
+    }
+            
+    $(replaceNestedFormIndex('#{$this->getButtonId()}')).on('click', function () {
+        var selected = getSelectedRows();
+        
+        setKeys(selected[1]);
+        
+        render(selected[0]);
+        
+        $(this).parents('.modal').modal('toggle');
+    });
+    
+    function render(selected) {
+        var box = $('{$this->getElementClassSelector()}'),
+            placeholder = box.find('.default-text'),
+            option = box.find('.option');
+        
+        if (! selected) {
+            placeholder.removeClass('d-none');
+            option.addClass('d-none');
+            
+            return;
+        }
+        
+        placeholder.addClass('d-none');
+        option.removeClass('d-none');
+        
+        var remove = $("<div class='pull-right ' style='font-weight:bold;cursor:pointer'>×</div>");
+
+        option.text(selected[0]['label']);
+        option.append(remove);
+        
+        remove.on('click', function () {
+            setKeys([]);
+            placeholder.removeClass('d-none');
+            option.addClass('d-none');
+        });
+    }
+    
+    render(options[0]);
+})();
+JS;
+    }
+
+    protected function setUpModal()
+    {
+        $this->modal
+            ->join()
+            ->id($this->getElementId())
+            ->runScript(false)
+            ->footer($this->renderFooter())
+            ->onLoad($this->getOnLoadScript());
+    }
+
+    protected function getOnLoadScript()
+    {
+        // 实现单选效果
+        return <<<JS
+$(this).find('.checkbox-grid-header').remove();
+
+var checkbox = $(this).find('.checkbox-grid-column input[type="checkbox"]');
+
+checkbox.on('change', function () {
+    var id = $(this).data('id');
+    
+    checkbox.each(function () {
+        if ($(this).data('id') != id) {
+            $(this).prop('checked', false);
+            $(this).parents('tr').css('background-color', '');
+        }
+    });
+});
+JS;
+    }
+
+    public function render()
+    {
+        $this->setUpModal();
+        $this->formatOptions();
+
+        $name = $this->getElementName();
+
+        $this->prepend('<i class="feather icon-arrow-up"></i>')
+            ->defaultAttribute('class', 'form-control '. $this->getElementClassString())
+            ->defaultAttribute('type', 'text')
+            ->defaultAttribute('name', $name);
+
+        $this->addVariables([
+            'prepend'     => $this->prepend,
+            'append'      => $this->append,
+            'style'       => $this->style,
+            'modal'       => $this->modal->render(),
+            'placeholder' => $this->placeholder(),
+        ]);
+
+        $this->script = $this->modal->getScript();
+
+        $this->addScript();
+        //dd($this->script);
+        return parent::render();
+    }
+
+    /**
+     * 弹窗底部内容构建.
+     *
+     * @return string
+     */
+    protected function renderFooter()
+    {
+        $submit = trans('admin.submit');
+        $cancel = trans('admin.cancel');
+
+        return <<<HTML
+<a id="{$this->getButtonId()}" class="btn btn-primary" style="color: #fff">&nbsp;{$submit}&nbsp;</a>&nbsp;
+<a onclick="$(this).parents('.modal').modal('toggle')" class="btn btn-white">&nbsp;{$cancel}&nbsp;</a>
+HTML;
+    }
+
+    /**
+     * 提交按钮ID
+     *
+     * @return string
+     */
+    protected function getButtonId()
+    {
+        return 'submit-'.$this->id;
+    }
+}

+ 1 - 5
src/Grid/Column/Filter/Input.php

@@ -59,11 +59,7 @@ JS;
         <li>
             <input placeholder="{$this->placeholder}" type="text" name="{$this->getQueryName()}" value="{$value}" class="form-control input-sm {$this->class}" autocomplete="off"/>
         </li>
-        <li class="dropdown-divider"></li>
-        <li>
-            <button class="btn btn-sm btn-primary column-filter-submit "><i class="feather icon-search"></i></button>
-            <span onclick="Dcat.reload('{$this->urlWithoutFilter()}')" class="btn btn-sm btn-default"><i class="feather icon-rotate-ccw"></i></span>
-        </li>
+        {$this->renderFormButtons()}
     </ul>
     </form>
 </span>

+ 42 - 0
src/Grid/LazyRenderable.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace Dcat\Admin\Grid;
+
+use Dcat\Admin\Grid;
+use Dcat\Admin\Support\LazyRenderable as Renderable;
+
+abstract class LazyRenderable extends Renderable
+{
+    abstract public function grid(): Grid;
+
+    public function render()
+    {
+        return $this->prepare($this->grid())->render();
+    }
+
+    protected function prepare(Grid $grid)
+    {
+        if (! $grid->getName()) {
+            $grid->setName($this->getDefaultName());
+        }
+
+        $grid->disableCreateButton();
+        $grid->disableActions();
+        $grid->disablePerPages();
+        $grid->disableBatchActions();
+        $grid->disableRefreshButton();
+
+        $grid->filter()
+            ->panel()
+            ->view('admin::filter.tile-container');
+
+        $grid->rowSelector()->click();
+
+        return $grid;
+    }
+
+    protected function getDefaultName()
+    {
+        return strtolower(str_replace('\\', '-', static::class));
+    }
+}

+ 2 - 2
src/Grid/Tools/RowSelector.php

@@ -54,7 +54,7 @@ class RowSelector
     public function renderHeader()
     {
         return <<<HTML
-<div class="vs-checkbox-con vs-checkbox-{$this->style} checkbox-grid">
+<div class="vs-checkbox-con vs-checkbox-{$this->style} checkbox-grid checkbox-grid-header">
     <input type="checkbox" class="select-all {$this->grid->getSelectAllName()}">
     <span class="vs-checkbox"><span class="vs-checkbox--check"><i class="vs-icon feather icon-check"></i></span></span>
 </div>
@@ -67,7 +67,7 @@ HTML;
         $title = e($this->getTitle($row, $id));
 
         return <<<EOT
-<div class="vs-checkbox-con vs-checkbox-{$this->style} checkbox-grid">
+<div class="vs-checkbox-con vs-checkbox-{$this->style} checkbox-grid checkbox-grid-column">
     <input type="checkbox" class="{$this->grid->getRowName()}-checkbox" data-id="{$id}" data-label="{$title}">
     <span class="vs-checkbox"><span class="vs-checkbox--check"><i class="vs-icon feather icon-check"></i></span></span>
 </div>        

+ 1 - 1
src/Traits/AsyncRenderable.php

@@ -26,7 +26,7 @@ trait AsyncRenderable
      *
      * @return $this
      */
-    public function setRenderable(LazyRenderable $renderable)
+    public function setRenderable(?LazyRenderable $renderable)
     {
         $this->renderable = $renderable;
 

+ 141 - 0
src/Widgets/AsyncTable.php

@@ -0,0 +1,141 @@
+<?php
+
+namespace Dcat\Admin\Widgets;
+
+use Dcat\Admin\Admin;
+use Dcat\Admin\Grid\LazyRenderable;
+use Dcat\Admin\Traits\AsyncRenderable;
+use Illuminate\Support\Str;
+
+class AsyncTable extends Widget
+{
+    use AsyncRenderable;
+
+    protected $load = true;
+
+    public function __construct(LazyRenderable $renderable = null, bool $load = true)
+    {
+        $this->setRenderable($renderable);
+        $this->load($load);
+
+        $this->id('table-card-'.Str::random(8));
+        $this->class('table-card');
+    }
+
+    /**
+     * 设置是否自动加载.
+     *
+     * @param bool $value
+     *
+     * @return $this
+     */
+    public function load(bool $value)
+    {
+        $this->load = $value;
+
+        return $this;
+    }
+
+    /**
+     * 监听异步渲染完成事件.
+     *
+     * @param string $script
+     *
+     * @return $this
+     */
+    public function onLoad(string $script)
+    {
+        $this->script .= "$(replaceNestedFormIndex('{$this->getElementSelector()}')).on('table:loaded', function (event) { {$script} });";
+
+        return $this;
+    }
+
+    protected function addScript()
+    {
+        Admin::script(<<<'JS'
+(function () {
+    function load(url, box) {
+        var $this = $(this);
+        
+        box = box || $this;
+        
+        url = $this.data('url') || url;
+        if (! url) {
+            return;
+        }
+        
+        box.loading({background: 'transparent!important'});
+        
+        Dcat.helpers.asyncRender(url, function (html) {
+            box.loading(false);
+            box.html(html);
+            bind(box);
+            box.trigger('table:loaded');
+        });
+    }            
+                
+    function bind(box) {
+        function loadLink() {
+            load($(this).attr('href'), box);
+            
+            return false;
+        }
+        
+        box.find('.pagination .page-link').on('click', loadLink);
+        box.find('.grid-column-header a').on('click', loadLink);
+  
+        box.find('form').on('submit', function () {
+            load($(this).attr('action')+'&'+$(this).serialize(), box);
+            
+            return false;
+        });
+         
+        box.find('.filter-box .reset').on('click', loadLink);
+    }
+    
+    $('.table-card').on('table:load', load);
+})();
+JS
+        );
+
+        if ($this->load) {
+            $this->script .= $this->getLoadScript();
+        }
+    }
+
+    /**
+     * @return string
+     */
+    public function getElementSelector()
+    {
+        return '#'.$this->getHtmlAttribute('id');
+    }
+
+    /**
+     * @return string
+     */
+    public function getLoadScript()
+    {
+        return <<<JS
+$(replaceNestedFormIndex('{$this->getElementSelector()}')).trigger('table:load');
+JS;
+    }
+
+    public function render()
+    {
+        $this->addScript();
+
+        return parent::render();
+    }
+
+    public function html()
+    {
+        $this->setHtmlAttribute([
+            'data-url' => $this->getRequestUrl(),
+        ]);
+
+        return <<<HTML
+<div {$this->formatHtmlAttributes()} style="min-height: 200px"></div>        
+HTML;
+    }
+}

+ 24 - 1
src/Widgets/Modal.php

@@ -54,6 +54,11 @@ class Modal extends Widget
      */
     protected $load = '';
 
+    /**
+     * @var bool
+     */
+    protected $join = false;
+
     /**
      * Modal constructor.
      *
@@ -183,6 +188,20 @@ class Modal extends Widget
         return $this->content($content);
     }
 
+    /**
+     * 设置是否返回弹窗HTML.
+     *
+     * @param bool $value
+     *
+     * @return $this
+     */
+    public function join(bool $value = true)
+    {
+        $this->join = $value;
+
+        return $this;
+    }
+
     /**
      * @param string|Closure|Renderable|LazyRenderable $footer
      *
@@ -306,7 +325,7 @@ class Modal extends Widget
 
         $this->script = <<<JS
 (function () {
-    var modal = $('{$this->getElementSelector()}');
+    var modal = $(replaceNestedFormIndex('{$this->getElementSelector()}'));
     {$script}
 })();
 JS;
@@ -339,6 +358,10 @@ JS
         $this->addRenderableScript();
         $this->addEventScript();
 
+        if ($this->join) {
+            return $this->renderButton().parent::render();
+        }
+
         Admin::html(parent::render());
 
         return $this->renderButton();

+ 8 - 5
src/Widgets/Sparkline/Sparkline.php

@@ -155,13 +155,13 @@ class Sparkline extends Widget
     /**
      * @return string
      */
-    protected function script()
+    protected function addScript()
     {
         $values = json_encode($this->values);
         $options = json_encode($this->options);
 
         if (! $this->allowBuildRequest()) {
-            return <<<JS
+            return $this->script = <<<JS
 $('#{$this->getId()}').sparkline($values, $options);
 {$this->buildCombinationScript()};
 JS;
@@ -178,7 +178,7 @@ $('#'+id).sparkline(response.values || $values, opt);
 JS
         );
 
-        return $this->buildRequestScript();
+        return $this->script = $this->buildRequestScript();
     }
 
     /**
@@ -205,14 +205,17 @@ JS;
      */
     public function render()
     {
-        Admin::script($this->script());
+        $this->addScript();
 
         $this->setHtmlAttribute([
             'id' => $this->getId(),
         ]);
 
-        $this->collectAssets();
+        return parent::render();
+    }
 
+    public function html()
+    {
         return <<<HTML
 <span {$this->formatHtmlAttributes()}></span>
 HTML;

+ 158 - 0
src/Widgets/TableModal.php

@@ -0,0 +1,158 @@
+<?php
+
+namespace Dcat\Admin\Widgets;
+
+use Dcat\Admin\Grid\LazyRenderable;
+use Illuminate\Contracts\Support\Renderable;
+
+/**
+ * Class TableModal
+ *
+ * @method $this title(string $title)
+ * @method $this button(string|\Closure $title)
+ * @method $this join(bool $value = true)
+ * @method $this xl()
+ * @method $this on(string $script)
+ * @method $this onShown(string $script)
+ * @method $this onShow(string $script)
+ * @method $this onHidden(string $script)
+ * @method $this onHide(string $script)
+ * @method $this footer(string|\Closure|Renderable $footer)
+ * @method $this getId()
+ * @method $this getElementSelector()
+ */
+class TableModal extends Widget
+{
+    /**
+     * @var Modal
+     */
+    protected $modal;
+
+    /**
+     * @var AsyncTable
+     */
+    protected $table;
+
+    /**
+     * @var array
+     */
+    protected $allowMethods = [
+        'id',
+        'title',
+        'button',
+        'join',
+        'xl',
+        'on',
+        'onShown',
+        'onShow',
+        'onHidden',
+        'onHide',
+        'getId',
+        'getElementSelector',
+        'footer',
+    ];
+
+    /**
+     * @var string
+     */
+    protected $loadScript;
+
+    /**
+     * TableModal constructor.
+     *
+     * @param null $title
+     * @param \Dcat\Admin\Grid\LazyRenderable|null $renderable
+     */
+    public function __construct($title = null, LazyRenderable $renderable = null)
+    {
+        $this->modal = Modal::make()
+            ->lg()
+            ->title($title)
+            ->class('grid-modal', true);
+
+        $this->body($renderable);
+    }
+
+    /**
+     * 设置异步表格实例.
+     *
+     * @param LazyRenderable|null $renderable
+     *
+     * @return $this
+     */
+    public function body(?LazyRenderable $renderable)
+    {
+        if (! $renderable) {
+            return $this;
+        }
+
+        $this->table = AsyncTable::make($renderable, false);
+
+        $this->modal
+            ->body($this->table)
+            ->onShow($this->table->getLoadScript());
+
+        return $this;
+    }
+
+    /**
+     * 监听弹窗异步渲染完成事件.
+     *
+     * @param string $script
+     *
+     * @return $this
+     */
+    public function onLoad(string $script)
+    {
+        $this->loadScript .= $script.';';
+
+        return $this;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function html()
+    {
+        if ($this->loadScript) {
+            $this->table->onLoad($this->loadScript);
+        }
+
+        $this->table->runScript($this->runScript);
+        $this->modal->runScript($this->runScript);
+
+        return $this->modal->render();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getScript()
+    {
+        return parent::getScript()
+            .$this->modal->getScript()
+            .$this->table->getScript();
+    }
+
+    public static function __callStatic($method, $arguments)
+    {
+        return static::make()->$method(...$arguments);
+    }
+
+    public function __call($method, $parameters)
+    {
+        if (in_array($method, $this->allowMethods, true)) {
+            $result = $this->modal->$method(...$parameters);
+
+            if (in_array($method, ['getElementSelector', 'getId'], true)) {
+                return $result;
+            }
+
+            return $this;
+        }
+
+        throw new \Exception(
+            sprintf('Call to undefined method "%s::%s"', static::class, $method)
+        );
+    }
+}