Browse Source

增加 selectTable 功能 #434

jqh 4 years ago
parent
commit
1b1568cb98
34 changed files with 615 additions and 285 deletions
  1. 70 1
      resources/assets/dcat/extra/grid-extend.js
  2. 279 0
      resources/assets/dcat/extra/select-table.js
  3. 0 0
      resources/dist/adminlte/adminlte.js
  4. 0 0
      resources/dist/adminlte/adminlte.js.map
  5. 0 0
      resources/dist/dcat/css/dcat-app-blue-dark.css
  6. 0 0
      resources/dist/dcat/css/dcat-app-blue-light.css
  7. 0 0
      resources/dist/dcat/css/dcat-app-blue.css
  8. 0 0
      resources/dist/dcat/css/dcat-app-green.css
  9. 0 0
      resources/dist/dcat/css/dcat-app.css
  10. 0 0
      resources/dist/dcat/extra/action.js.map
  11. 0 0
      resources/dist/dcat/extra/grid-extend.js
  12. 0 0
      resources/dist/dcat/extra/grid-extend.js.map
  13. 0 0
      resources/dist/dcat/extra/resource-selector.js.map
  14. 0 0
      resources/dist/dcat/extra/select-table.js
  15. 0 0
      resources/dist/dcat/extra/select-table.js.map
  16. 0 0
      resources/dist/dcat/extra/upload.js
  17. 0 0
      resources/dist/dcat/extra/upload.js.map
  18. 0 0
      resources/dist/dcat/js/dcat-app.js
  19. 0 0
      resources/dist/dcat/js/dcat-app.js.map
  20. 0 4
      src/Controllers/AdminController.php
  21. 0 5
      src/Controllers/RoleController.php
  22. 0 14
      src/Controllers/UserController.php
  23. 57 55
      src/Form.php
  24. 2 0
      src/Form/EmbeddedForm.php
  25. 31 7
      src/Form/Field.php
  26. 51 0
      src/Form/Field/MultipleSelectTable.php
  27. 18 93
      src/Form/Field/SelectTable.php
  28. 3 5
      src/Form/Field/Text.php
  29. 4 0
      src/Form/NestedForm.php
  30. 3 0
      src/Layout/Asset.php
  31. 5 42
      src/Widgets/AsyncTable.php
  32. 55 53
      src/Widgets/Form.php
  33. 21 6
      src/Widgets/Modal.php
  34. 16 0
      src/Widgets/TableModal.php

+ 70 - 1
resources/assets/dcat/extra/grid-extend.js

@@ -1,6 +1,12 @@
 (function (w, $) {
     let Dcat = w.Dcat;
-    
+
+    /**
+     * 树状表格
+     *
+     * @param opts
+     * @constructor
+     */
     function Tree(opts) {
         this.options = $.extend({
             button: null,
@@ -168,6 +174,12 @@
         }
     };
 
+    /**
+     * 可排序功能
+     *
+     * @param opts
+     * @constructor
+     */
     function Orderable(opts) {
         this.options = $.extend({
             button: null,
@@ -274,6 +286,60 @@
         },
     };
 
+    /**
+     * 异步加载表格
+     *
+     * @param options
+     * @constructor
+     */
+    function AsyncTable(options) {
+        options = $.extend({
+            container: '.table-card',
+        }, options)
+
+        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);
+        }
+
+        $(options.container).on('table:load', load);
+    }
+
+
     function isTr(v) {
         return $(v).prop('tagName').toLocaleLowerCase() === 'tr'
     }
@@ -337,4 +403,7 @@
     Dcat.grid.Orderable = function (opts) {
         return new Orderable(opts);
     };
+    Dcat.grid.AsyncTable =function (opts) {
+        return new AsyncTable(opts)
+    }
 })(window, jQuery);

+ 279 - 0
resources/assets/dcat/extra/select-table.js

@@ -0,0 +1,279 @@
+(function (w) {
+    function SelectTable(options) {
+        options = $.extend({
+            modal: null,
+            container: null,
+            input: null,
+            button: null,
+            multiple: false,
+            max: 0,
+            values: [],
+            lang: {
+                exceed_max_item: Dcat.lang.exceed_max_item || '已超出最大可选择的数量',
+            },
+        }, options);
+
+        let self = this,
+            values = options.values;
+
+        self.options = options;
+        self.$modal = $(options.modal);
+        self.$input = $(options.input);
+        self.$button = $(options.button);
+
+        self.labels = {};
+
+        for (let i in values) {
+            self.labels[values[i]['id']] = values[i]['label']
+        }
+
+        // 保存临时选中的值
+        self.resetSelected();
+
+        // 提交按钮
+        self.$button.on('click', function () {
+            var selected = self.getSelectedRows();
+
+            self.setKeys(selected[1]);
+
+            self.render(selected[0]);
+
+            self.$modal.modal('toggle');
+        });
+
+        self._bind();
+
+        self.render(values);
+
+        return self;
+    }
+
+    SelectTable.prototype = {
+        _bind() {
+            let self = this, options = self.options;
+
+            // 表格加载完成事件
+            self.$modal.find('.table-card').on('table:loaded', function () {
+                let checkbox = self.getCheckbox();
+
+                if (! options.multiple) {
+                    // 移除全选按钮
+                    $(this).find('.checkbox-grid-header').remove();
+                }
+
+                // 重置已选中数据
+                self.resetSelected();
+
+                checkbox.on('change', function () {
+                    let id = $(this).data('id'),
+                        label = $(this).data('label');
+
+                    if (this.checked) {
+                        if (! options.multiple) {
+                            self.selected = {};
+                        }
+                        self.selected[id] = {id: id, label: label};
+
+                        // 多选
+                        if (options.max && (self.getSelectedRows()[0].length > options.max)) {
+                            $(this).prop('checked', false);
+                            delete self.selected[id];
+
+                            return Dcat.warning(self.options.lang.exceed_max_item);
+                        }
+                    } else {
+                        delete self.selected[id];
+                    }
+
+                    if (! options.multiple) {
+                        if (this.checked) {
+                            // 单选效果
+                            checkbox.each(function () {
+                                if ($(this).data('id') != id) {
+                                    $(this).prop('checked', false);
+                                    $(this).parents('tr').css('background-color', '');
+                                }
+                            });
+                        }
+                    }
+                });
+
+                // 选中默认选项
+                checkbox.each(function () {
+                    let $this = $(this),
+                        current = $this.data('id');
+
+                    // 保存label字段
+                    self.labels[current] = $this.data('label');
+
+                    for (let i in self.selected) {
+                        if (current == i) {
+                            $this.prop('checked', true).trigger('change');
+
+                            continue;
+                        }
+                    }
+
+                    $this.trigger('change');
+                });
+            })
+        },
+
+        // 重置已选中数据
+        resetSelected() {
+            let self = this,
+                keys = self.getKeys();
+
+            self.selected = [];
+
+            for (let i in keys) {
+                self.selected[keys[i]] = {id: keys[i], label: self.labels[keys[i]]};
+            }
+        },
+
+        getCheckbox() {
+            return this.$modal.find('.checkbox-grid-column input[type="checkbox"]');
+        },
+
+        getSelectedRows() {
+            let self = this,
+                selected = [],
+                ids = [];
+
+            for (let i in self.selected) {
+                if (! self.selected[i]) {
+                    continue;
+                }
+
+                ids.push(i);
+                selected.push(self.selected[i])
+            }
+
+            return [selected, ids];
+        },
+
+        render(selected) {
+            let self = this,
+                options = self.options,
+                box = $(options.container),
+                placeholder = box.find('.default-text'),
+                option = box.find('.option');
+
+            if (! selected || ! selected.length) {
+                placeholder.removeClass('d-none');
+                option.addClass('d-none');
+
+                return;
+            }
+
+            placeholder.addClass('d-none');
+            option.removeClass('d-none');
+
+            if (! options.multiple) {
+                return renderDefault(selected, self, options);
+            }
+
+            return renderMultiple(selected, self, options);
+        },
+
+        setKeys(keys) {
+            this.$input.val(keys.length ? keys.join(',') : '');
+        },
+
+        deleteKey(key) {
+            let val = this.getKeys(),
+                results = [];
+
+            for (let i in val) {
+                if (val[i] != key) {
+                    results.push(val[i])
+                }
+            }
+
+            this.setKeys(results)
+        },
+
+        getKeys() {
+            let val = this.$input.val();
+
+            if (! val) return [];
+
+            return String(val).split(',');
+        },
+    };
+
+    // 多选
+    function renderMultiple(selected, self, options) {
+        let html = [],
+            box = $(options.container),
+            placeholder = box.find('.default-text'),
+            option = box.find('.option');
+
+        if (! box.hasClass('select2')) {
+            box.addClass('select2 select2-container select2-container--default select2-container--below');
+        }
+        box.removeClass('form-control');
+
+        for (let i in selected) {
+            html.push(`<li class="select2-selection__choice" >
+    ${selected[i]['label']} <span data-id="${selected[i]['id']}" class="select2-selection__choice__remove remove " role="presentation"> ×</span>
+</li>`);
+        }
+
+        html.unshift('<span class="select2-selection__clear remove-all">×</span>');
+
+        html = `<span class="select2-selection select2-selection--multiple">
+ <ul class="select2-selection__rendered">${html.join('')}</ul>
+ </span>`;
+
+        var $tags = $(html);
+
+        option.html($tags);
+
+        $tags.find('.remove').on('click', function () {
+            var $this = $(this);
+
+            self.deleteKey($this.data('id'));
+
+            $this.parent().remove();
+
+            if (! self.getKeys().length) {
+                removeAll();
+            }
+        });
+
+        function removeAll() {
+            option.html('');
+            placeholder.removeClass('d-none');
+            option.addClass('d-none');
+
+            box.addClass('form-control');
+
+            self.setKeys([]);
+        }
+
+        $tags.find('.remove-all').on('click', removeAll);
+    }
+
+    // 单选
+    function renderDefault(selected, self, options) {
+        let box = $(options.container),
+            placeholder = box.find('.default-text'),
+            option = box.find('.option');
+
+        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 () {
+            self.setKeys([]);
+            placeholder.removeClass('d-none');
+            option.addClass('d-none');
+        });
+    }
+
+    Dcat.grid.SelectTable = function (opts) {
+        return new SelectTable(opts)
+    };
+})(window)

File diff suppressed because it is too large
+ 0 - 0
resources/dist/adminlte/adminlte.js


File diff suppressed because it is too large
+ 0 - 0
resources/dist/adminlte/adminlte.js.map


File diff suppressed because it is too large
+ 0 - 0
resources/dist/dcat/css/dcat-app-blue-dark.css


File diff suppressed because it is too large
+ 0 - 0
resources/dist/dcat/css/dcat-app-blue-light.css


File diff suppressed because it is too large
+ 0 - 0
resources/dist/dcat/css/dcat-app-blue.css


File diff suppressed because it is too large
+ 0 - 0
resources/dist/dcat/css/dcat-app-green.css


File diff suppressed because it is too large
+ 0 - 0
resources/dist/dcat/css/dcat-app.css


File diff suppressed because it is too large
+ 0 - 0
resources/dist/dcat/extra/action.js.map


File diff suppressed because it is too large
+ 0 - 0
resources/dist/dcat/extra/grid-extend.js


File diff suppressed because it is too large
+ 0 - 0
resources/dist/dcat/extra/grid-extend.js.map


File diff suppressed because it is too large
+ 0 - 0
resources/dist/dcat/extra/resource-selector.js.map


File diff suppressed because it is too large
+ 0 - 0
resources/dist/dcat/extra/select-table.js


File diff suppressed because it is too large
+ 0 - 0
resources/dist/dcat/extra/select-table.js.map


File diff suppressed because it is too large
+ 0 - 0
resources/dist/dcat/extra/upload.js


File diff suppressed because it is too large
+ 0 - 0
resources/dist/dcat/extra/upload.js.map


File diff suppressed because it is too large
+ 0 - 0
resources/dist/dcat/js/dcat-app.js


File diff suppressed because it is too large
+ 0 - 0
resources/dist/dcat/js/dcat-app.js.map


+ 0 - 4
src/Controllers/AdminController.php

@@ -57,10 +57,6 @@ class AdminController extends Controller
     public function index(Content $content)
     {
         if (request(Grid::IFRAME_QUERY_NAME)) {
-            if (method_exists($this, 'iframe')) {
-                return $content->full()->body($this->iframe());
-            }
-
             return $content->full()->body($this->iFrameGrid());
         }
 

+ 0 - 5
src/Controllers/RoleController.php

@@ -44,11 +44,6 @@ class RoleController extends AdminController
         });
     }
 
-    protected function iframe()
-    {
-        return $this->grid()->inIframe();
-    }
-
     protected function detail($id)
     {
         return Show::make($id, new Role('permissions'), function (Show $show) {

+ 0 - 14
src/Controllers/UserController.php

@@ -66,20 +66,6 @@ class UserController extends AdminController
         });
     }
 
-    protected function iframe()
-    {
-        return new Grid(new Administrator(), function (Grid $grid) {
-            $grid->inIframe();
-
-            $grid->quickSearch(['id', 'name', 'username']);
-
-            $grid->column('id')->sortable();
-            $grid->column('username');
-            $grid->column('name');
-            $grid->column('created_at');
-        });
-    }
-
     protected function detail($id)
     {
         return Show::make($id, new Administrator('roles'), function (Show $show) {

+ 57 - 55
src/Form.php

@@ -84,6 +84,7 @@ use Symfony\Component\HttpFoundation\Response;
  * @method Field\Color                  color($column, $label = '')
  * @method Field\ArrayField             array($column, $labelOrCallback, $callback = null)
  * @method Field\SelectTable            selectTable($column, $label = '')
+ * @method Field\MultipleSelectTable    multipleSelectTable($column, $label = '')
  */
 class Form implements Renderable
 {
@@ -112,61 +113,62 @@ class Form implements Renderable
      * @var array
      */
     protected static $availableFields = [
-        'button'         => Field\Button::class,
-        'checkbox'       => Field\Checkbox::class,
-        'currency'       => Field\Currency::class,
-        'date'           => Field\Date::class,
-        'dateRange'      => Field\DateRange::class,
-        'datetime'       => Field\Datetime::class,
-        'datetimeRange'  => Field\DatetimeRange::class,
-        'decimal'        => Field\Decimal::class,
-        'display'        => Field\Display::class,
-        'divider'        => Field\Divide::class,
-        'embeds'         => Field\Embeds::class,
-        'editor'         => Field\Editor::class,
-        'email'          => Field\Email::class,
-        'hidden'         => Field\Hidden::class,
-        'id'             => Field\Id::class,
-        'ip'             => Field\Ip::class,
-        'map'            => Field\Map::class,
-        'mobile'         => Field\Mobile::class,
-        'month'          => Field\Month::class,
-        'multipleSelect' => Field\MultipleSelect::class,
-        'number'         => Field\Number::class,
-        'password'       => Field\Password::class,
-        'radio'          => Field\Radio::class,
-        'rate'           => Field\Rate::class,
-        'select'         => Field\Select::class,
-        'slider'         => Field\Slider::class,
-        'switch'         => Field\SwitchField::class,
-        'text'           => Field\Text::class,
-        'textarea'       => Field\Textarea::class,
-        'time'           => Field\Time::class,
-        'timeRange'      => Field\TimeRange::class,
-        'url'            => Field\Url::class,
-        'year'           => Field\Year::class,
-        'html'           => Field\Html::class,
-        'tags'           => Field\Tags::class,
-        'icon'           => Field\Icon::class,
-        'captcha'        => Field\Captcha::class,
-        'listbox'        => Field\Listbox::class,
-        'selectResource' => Field\SelectResource::class,
-        'file'           => Field\File::class,
-        'image'          => Field\Image::class,
-        'multipleFile'   => Field\MultipleFile::class,
-        'multipleImage'  => Field\MultipleImage::class,
-        'hasMany'        => Field\HasMany::class,
-        'tree'           => Field\Tree::class,
-        'table'          => Field\Table::class,
-        'list'           => Field\ListField::class,
-        'timezone'       => Field\Timezone::class,
-        'keyValue'       => Field\KeyValue::class,
-        'tel'            => Field\Tel::class,
-        'markdown'       => Field\Markdown::class,
-        'range'          => Field\Range::class,
-        'color'          => Field\Color::class,
-        'array'          => Field\ArrayField::class,
-        'selectTable'    => Field\SelectTable::class,
+        'button'              => Field\Button::class,
+        'checkbox'            => Field\Checkbox::class,
+        'currency'            => Field\Currency::class,
+        'date'                => Field\Date::class,
+        'dateRange'           => Field\DateRange::class,
+        'datetime'            => Field\Datetime::class,
+        'datetimeRange'       => Field\DatetimeRange::class,
+        'decimal'             => Field\Decimal::class,
+        'display'             => Field\Display::class,
+        'divider'             => Field\Divide::class,
+        'embeds'              => Field\Embeds::class,
+        'editor'              => Field\Editor::class,
+        'email'               => Field\Email::class,
+        'hidden'              => Field\Hidden::class,
+        'id'                  => Field\Id::class,
+        'ip'                  => Field\Ip::class,
+        'map'                 => Field\Map::class,
+        'mobile'              => Field\Mobile::class,
+        'month'               => Field\Month::class,
+        'multipleSelect'      => Field\MultipleSelect::class,
+        'number'              => Field\Number::class,
+        'password'            => Field\Password::class,
+        'radio'               => Field\Radio::class,
+        'rate'                => Field\Rate::class,
+        'select'              => Field\Select::class,
+        'slider'              => Field\Slider::class,
+        'switch'              => Field\SwitchField::class,
+        'text'                => Field\Text::class,
+        'textarea'            => Field\Textarea::class,
+        'time'                => Field\Time::class,
+        'timeRange'           => Field\TimeRange::class,
+        'url'                 => Field\Url::class,
+        'year'                => Field\Year::class,
+        'html'                => Field\Html::class,
+        'tags'                => Field\Tags::class,
+        'icon'                => Field\Icon::class,
+        'captcha'             => Field\Captcha::class,
+        'listbox'             => Field\Listbox::class,
+        'selectResource'      => Field\SelectResource::class,
+        'file'                => Field\File::class,
+        'image'               => Field\Image::class,
+        'multipleFile'        => Field\MultipleFile::class,
+        'multipleImage'       => Field\MultipleImage::class,
+        'hasMany'             => Field\HasMany::class,
+        'tree'                => Field\Tree::class,
+        'table'               => Field\Table::class,
+        'list'                => Field\ListField::class,
+        'timezone'            => Field\Timezone::class,
+        'keyValue'            => Field\KeyValue::class,
+        'tel'                 => Field\Tel::class,
+        'markdown'            => Field\Markdown::class,
+        'range'               => Field\Range::class,
+        'color'               => Field\Color::class,
+        'array'               => Field\ArrayField::class,
+        'selectTable'         => Field\SelectTable::class,
+        'multipleSelectTable' => Field\MultipleSelectTable::class,
     ];
 
     /**

+ 2 - 0
src/Form/EmbeddedForm.php

@@ -61,6 +61,8 @@ use Illuminate\Support\Collection;
  * @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 = '')
+ * @method Field\MultipleSelectTable    multipleSelectTable($column, $label = '')
  */
 class EmbeddedForm
 {

+ 31 - 7
src/Form/Field.php

@@ -493,17 +493,41 @@ class Field implements Renderable
      */
     public function options($options = [])
     {
-        if ($options instanceof Arrayable) {
-            $options = $options->toArray();
+        $this->options = $this->prepareOptions($options);
+
+        return $this;
+    }
+
+    /**
+     * @param array|Arrayable $options
+     *
+     * @return $this
+     */
+    public function mergeOptions($options)
+    {
+        $this->options = array_merge($this->options, $this->prepareOptions($options));
+
+        return $this;
+    }
+
+    /**
+     * Prepare options.
+     *
+     * @param $options
+     *
+     * @return array|mixed
+     */
+    protected function prepareOptions($options)
+    {
+        if ($options instanceof \Closure) {
+            $options = $options->call($this->data(), $this->value());
         }
 
-        if (is_array($this->options)) {
-            $this->options = array_merge($this->options, $options);
-        } else {
-            $this->options = $options;
+        if ($options instanceof Arrayable) {
+            $options = $options->toArray();
         }
 
-        return $this;
+        return $options;
     }
 
     /**

+ 51 - 0
src/Form/Field/MultipleSelectTable.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace Dcat\Admin\Form\Field;
+
+use Dcat\Admin\Admin;
+
+class MultipleSelectTable extends SelectTable
+{
+    protected $view = 'admin::form.selecttable';
+
+    /**
+     * @var int
+     */
+    protected $max = 0;
+
+    /**
+     * 设置最大选择数量.
+     *
+     * @param int $max
+     *
+     * @return $this
+     */
+    public function max(int $max)
+    {
+        $this->max = $max;
+
+        return $this;
+    }
+
+    public function render()
+    {
+        Admin::css('@select2');
+
+        return parent::render();
+    }
+
+    protected function addScript()
+    {
+        $this->script .= <<<JS
+Dcat.grid.SelectTable({
+    modal: replaceNestedFormIndex('#{$this->modal->getId()}'),
+    container: '{$this->getElementClassSelector()}',
+    input: replaceNestedFormIndex('#hidden-{$this->id}'),
+    button: replaceNestedFormIndex('#{$this->getButtonId()}'),
+    multiple: true,
+    max: {$this->max},
+    values: {$this->options},
+})
+JS;
+    }
+}

+ 18 - 93
src/Form/Field/SelectTable.php

@@ -11,6 +11,10 @@ class SelectTable extends Field
 {
     use PlainInput;
 
+    protected static $js = [
+        '@select-table',
+    ];
+
     /**
      * @var TableModal
      */
@@ -101,107 +105,28 @@ class SelectTable extends Field
     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]);
-})();
+Dcat.grid.SelectTable({
+    modal: replaceNestedFormIndex('#{$this->modal->getId()}'),
+    container: '{$this->getElementClassSelector()}',
+    input: replaceNestedFormIndex('#hidden-{$this->id}'),
+    button: replaceNestedFormIndex('#{$this->getButtonId()}'),
+    values: {$this->options},
+});
 JS;
     }
 
     protected function setUpModal()
     {
+        $this->modal
+            ->getTable()
+            ->getRenderable()
+            ->with('key', $this->form->getKey());
+
         $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;
+            ->footer($this->renderFooter());
     }
 
     public function render()
@@ -227,7 +152,7 @@ JS;
         $this->script = $this->modal->getScript();
 
         $this->addScript();
-        //dd($this->script);
+
         return parent::render();
     }
 

+ 3 - 5
src/Form/Field/Text.php

@@ -162,7 +162,7 @@ JS
      */
     protected function jsonEncodeOptions($options)
     {
-        $data = $this->prepareOptions($options);
+        $data = $this->formatOptions($options);
 
         $json = json_encode($data['options']);
 
@@ -172,20 +172,18 @@ JS
     }
 
     /**
-     * Prepare options.
-     *
      * @param array $options
      *
      * @return array
      */
-    protected function prepareOptions($options)
+    protected function formatOptions($options)
     {
         $original = [];
         $toReplace = [];
 
         foreach ($options as $key => &$value) {
             if (is_array($value)) {
-                $subArray = $this->prepareOptions($value);
+                $subArray = $this->formatOptions($value);
                 $value = $subArray['options'];
                 $original = array_merge($original, $subArray['original']);
                 $toReplace = array_merge($toReplace, $subArray['toReplace']);

+ 4 - 0
src/Form/NestedForm.php

@@ -4,6 +4,8 @@ namespace Dcat\Admin\Form;
 
 use Dcat\Admin\Admin;
 use Dcat\Admin\Form;
+use Dcat\Admin\Form\Field\MultipleSelectTable;
+use Dcat\Admin\Form\Field\SelectTable;
 use Dcat\Admin\Widgets\Form as WidgetForm;
 use Illuminate\Support\Arr;
 use Illuminate\Support\Collection;
@@ -63,6 +65,8 @@ use Illuminate\Support\Collection;
  * @method Field\Markdown               markdown($column, $label = '')
  * @method Field\Range                  range($start, $end, $label = '')
  * @method Field\Color                  color($column, $label = '')
+ * @method Field\SelectTable            selectTable($column, $label = '')
+ * @method Field\MultipleSelectTable    multipleSelectTable($column, $label = '')
  */
 class NestedForm
 {

+ 3 - 0
src/Layout/Asset.php

@@ -58,6 +58,9 @@ class Asset
         '@resource-selector' => [
             'js' => '@admin/dcat/extra/resource-selector.js',
         ],
+        '@select-table' => [
+            'js' => '@admin/dcat/extra/select-table.js',
+        ],
         '@layer' => [
             'js' => '@admin/dcat/plugins/layer/layer.js',
         ],

+ 5 - 42
src/Widgets/AsyncTable.php

@@ -11,6 +11,10 @@ class AsyncTable extends Widget
 {
     use AsyncRenderable;
 
+    public static $js = [
+        '@grid-extension',
+    ];
+
     protected $load = true;
 
     public function __construct(LazyRenderable $renderable = null, bool $load = true)
@@ -53,48 +57,7 @@ class AsyncTable extends Widget
     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);
-})();
+Dcat.grid.AsyncTable('.table-card');
 JS
         );
 

+ 55 - 53
src/Widgets/Form.php

@@ -27,59 +27,61 @@ use Illuminate\Validation\Validator;
 /**
  * Class Form.
  *
- * @method Field\Text           text($column, $label = '')
- * @method Field\Checkbox       checkbox($column, $label = '')
- * @method Field\Radio          radio($column, $label = '')
- * @method Field\Select         select($column, $label = '')
- * @method Field\MultipleSelect multipleSelect($column, $label = '')
- * @method Field\Textarea       textarea($column, $label = '')
- * @method Field\Hidden         hidden($column, $label = '')
- * @method Field\Id             id($column, $label = '')
- * @method Field\Ip             ip($column, $label = '')
- * @method Field\Url            url($column, $label = '')
- * @method Field\Email          email($column, $label = '')
- * @method Field\Mobile         mobile($column, $label = '')
- * @method Field\Slider         slider($column, $label = '')
- * @method Field\Map            map($latitude, $longitude, $label = '')
- * @method Field\Editor         editor($column, $label = '')
- * @method Field\Date           date($column, $label = '')
- * @method Field\Datetime       datetime($column, $label = '')
- * @method Field\Time           time($column, $label = '')
- * @method Field\Year           year($column, $label = '')
- * @method Field\Month          month($column, $label = '')
- * @method Field\DateRange      dateRange($start, $end, $label = '')
- * @method Field\DateTimeRange  datetimeRange($start, $end, $label = '')
- * @method Field\TimeRange      timeRange($start, $end, $label = '')
- * @method Field\Number         number($column, $label = '')
- * @method Field\Currency       currency($column, $label = '')
- * @method Field\SwitchField    switch($column, $label = '')
- * @method Field\Display        display($column, $label = '')
- * @method Field\Rate           rate($column, $label = '')
- * @method Field\Divide         divider()
- * @method Field\Password       password($column, $label = '')
- * @method Field\Decimal        decimal($column, $label = '')
- * @method Field\Html           html($html, $label = '')
- * @method Field\Tags           tags($column, $label = '')
- * @method Field\Icon           icon($column, $label = '')
- * @method Field\Embeds         embeds($column, $label = '')
- * @method Field\Captcha        captcha($column, $label = '')
- * @method Field\Listbox        listbox($column, $label = '')
- * @method Field\SelectResource selectResource($column, $label = '')
- * @method Field\File           file($column, $label = '')
- * @method Field\Image          image($column, $label = '')
- * @method Field\MultipleFile   multipleFile($column, $label = '')
- * @method Field\MultipleImage  multipleImage($column, $label = '')
- * @method Field\HasMany        hasMany($column, \Closure $callback)
- * @method Field\Tree           tree($column, $label = '')
- * @method Field\Table          table($column, $callback)
- * @method Field\ListField      list($column, $label = '')
- * @method Field\Timezone       timezone($column, $label = '')
- * @method Field\KeyValue       keyValue($column, $label = '')
- * @method Field\Tel            tel($column, $label = '')
- * @method Field\Markdown       markdown($column, $label = '')
- * @method Field\Range          range($start, $end, $label = '')
- * @method Field\Color          color($column, $label = '')
- * @method Field\ArrayField     array($column, $labelOrCallback, $callback = null)
+ * @method Field\Text                text($column, $label = '')
+ * @method Field\Checkbox            checkbox($column, $label = '')
+ * @method Field\Radio               radio($column, $label = '')
+ * @method Field\Select              select($column, $label = '')
+ * @method Field\MultipleSelect      multipleSelect($column, $label = '')
+ * @method Field\Textarea            textarea($column, $label = '')
+ * @method Field\Hidden              hidden($column, $label = '')
+ * @method Field\Id                  id($column, $label = '')
+ * @method Field\Ip                  ip($column, $label = '')
+ * @method Field\Url                 url($column, $label = '')
+ * @method Field\Email               email($column, $label = '')
+ * @method Field\Mobile              mobile($column, $label = '')
+ * @method Field\Slider              slider($column, $label = '')
+ * @method Field\Map                 map($latitude, $longitude, $label = '')
+ * @method Field\Editor              editor($column, $label = '')
+ * @method Field\Date                date($column, $label = '')
+ * @method Field\Datetime            datetime($column, $label = '')
+ * @method Field\Time                time($column, $label = '')
+ * @method Field\Year                year($column, $label = '')
+ * @method Field\Month               month($column, $label = '')
+ * @method Field\DateRange           dateRange($start, $end, $label = '')
+ * @method Field\DateTimeRange       datetimeRange($start, $end, $label = '')
+ * @method Field\TimeRange           timeRange($start, $end, $label = '')
+ * @method Field\Number              number($column, $label = '')
+ * @method Field\Currency            currency($column, $label = '')
+ * @method Field\SwitchField         switch($column, $label = '')
+ * @method Field\Display             display($column, $label = '')
+ * @method Field\Rate                rate($column, $label = '')
+ * @method Field\Divide              divider()
+ * @method Field\Password            password($column, $label = '')
+ * @method Field\Decimal             decimal($column, $label = '')
+ * @method Field\Html                html($html, $label = '')
+ * @method Field\Tags                tags($column, $label = '')
+ * @method Field\Icon                icon($column, $label = '')
+ * @method Field\Embeds              embeds($column, $label = '')
+ * @method Field\Captcha             captcha($column, $label = '')
+ * @method Field\Listbox             listbox($column, $label = '')
+ * @method Field\SelectResource      selectResource($column, $label = '')
+ * @method Field\File                file($column, $label = '')
+ * @method Field\Image               image($column, $label = '')
+ * @method Field\MultipleFile        multipleFile($column, $label = '')
+ * @method Field\MultipleImage       multipleImage($column, $label = '')
+ * @method Field\HasMany             hasMany($column, \Closure $callback)
+ * @method Field\Tree                tree($column, $label = '')
+ * @method Field\Table               table($column, $callback)
+ * @method Field\ListField           list($column, $label = '')
+ * @method Field\Timezone            timezone($column, $label = '')
+ * @method Field\KeyValue            keyValue($column, $label = '')
+ * @method Field\Tel                 tel($column, $label = '')
+ * @method Field\Markdown            markdown($column, $label = '')
+ * @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 = '')
+ * @method Field\MultipleSelectTable multipleSelectTable($column, $label = '')
  */
 class Form implements Renderable
 {

+ 21 - 6
src/Widgets/Modal.php

@@ -54,6 +54,11 @@ class Modal extends Widget
      */
     protected $load = '';
 
+    /**
+     * @var string
+     */
+    protected $subScript;
+
     /**
      * @var bool
      */
@@ -326,6 +331,7 @@ class Modal extends Widget
         $this->script = <<<JS
 (function () {
     var modal = $(replaceNestedFormIndex('{$this->getElementSelector()}'));
+    {$this->subScript};
     {$script}
 })();
 JS;
@@ -337,17 +343,26 @@ JS;
             return;
         }
 
+        $this->subScript = <<<JS
+modal.on('modal:load', function () {
+    Dcat.helpers.asyncRender('{$url}', function (html) {
+        body.html(html);
+        
+        {$this->load}
+        
+        modal.trigger('modal:loaded');
+    });
+});
+JS;
+
+
         $this->on('show.bs.modal', <<<JS
-var modal = $(this), body = modal.find('.modal-body');
+body = modal.find('.modal-body');
 
 body.html('<div style="min-height:150px"></div>').loading();
         
 setTimeout(function () {
-    Dcat.helpers.asyncRender('{$url}', function (html) {
-        body.html(html);
-
-        {$this->load}
-    });
+    modal.trigger('modal:load')
 }, {$this->delay});
 JS
         );

+ 16 - 0
src/Widgets/TableModal.php

@@ -134,6 +134,22 @@ class TableModal extends Widget
             .$this->table->getScript();
     }
 
+    /**
+     * @return Modal
+     */
+    public function getModal()
+    {
+        return $this->modal;
+    }
+
+    /**
+     * @return AsyncTable
+     */
+    public function getTable()
+    {
+        return $this->table;
+    }
+
     public static function __callStatic($method, $arguments)
     {
         return static::make()->$method(...$arguments);

Some files were not shown because too many files changed in this diff