Parcourir la source

Merge branch '2.0' into gitee2

jqh il y a 4 ans
Parent
commit
8bbe7e5d64
36 fichiers modifiés avec 695 ajouts et 158 suppressions
  1. 0 1
      composer.json
  2. 167 25
      resources/assets/dcat/js/extensions/Grid.js
  3. 0 0
      resources/dist/adminlte/adminlte.css
  4. 0 0
      resources/dist/adminlte/adminlte.js
  5. 0 0
      resources/dist/adminlte/adminlte.js.map
  6. 0 0
      resources/dist/dcat/extra/action.js.map
  7. 0 0
      resources/dist/dcat/extra/grid-extend.js
  8. 0 0
      resources/dist/dcat/extra/grid-extend.js.map
  9. 0 0
      resources/dist/dcat/extra/select-table.js.map
  10. 0 0
      resources/dist/dcat/extra/upload.js
  11. 0 0
      resources/dist/dcat/extra/upload.js.map
  12. 0 0
      resources/dist/dcat/js/dcat-app.js
  13. 0 0
      resources/dist/dcat/js/dcat-app.js.map
  14. 1 1
      resources/views/filter/button.blade.php
  15. 189 0
      resources/views/grid/async-fixed-table.blade.php
  16. 87 0
      resources/views/grid/async-table.blade.php
  17. 3 3
      resources/views/grid/fixed-table.blade.php
  18. 1 5
      resources/views/grid/table.blade.php
  19. 56 1
      src/Admin.php
  20. 103 16
      src/Grid.php
  21. 1 1
      src/Grid/Actions/Show.php
  22. 5 0
      src/Grid/Concerns/CanFixColumns.php
  23. 18 0
      src/Grid/Concerns/HasFilter.php
  24. 11 0
      src/Grid/Filter.php
  25. 13 13
      src/Grid/FixColumns.php
  26. 1 1
      src/Grid/LazyRenderable.php
  27. 10 11
      src/Grid/Tools/FilterButton.php
  28. 1 1
      src/Grid/Tools/PerPageSelector.php
  29. 4 2
      src/Http/Controllers/PermissionController.php
  30. 4 2
      src/Http/Controllers/RoleController.php
  31. 0 68
      src/Http/Middleware/Pjax.php
  32. 5 5
      src/Layout/Asset.php
  33. 7 0
      src/Layout/Column.php
  34. 4 0
      src/Layout/Content.php
  35. 1 0
      src/Support/Context.php
  36. 3 2
      src/Traits/HasAssets.php

+ 0 - 1
composer.json

@@ -13,7 +13,6 @@
     ],
     "require": {
         "php": ">=7.1.0",
-        "symfony/dom-crawler": "~3.1|~4.0|~5.0",
         "laravel/framework": "~5.5|~6.0|~7.0|~8.0",
         "spatie/eloquent-sortable": "3.*|4.*"
     },

+ 167 - 25
resources/assets/dcat/js/extensions/Grid.js

@@ -1,25 +1,167 @@
-
-let defaultName = '_def_';
-
-export default class Grid {
-    constructor(Dcat) {
-        Dcat.grid = this;
-
-        this.selectors = {};
-    }
-
-    // 添加行选择器对象
-    addSelector(selector, name) {
-        this.selectors[name || defaultName] = selector
-    }
-
-    // 获取行选择器选中的ID字符串
-    selected(name) {
-        return this.selectors[name || defaultName].getSelectedKeys()
-    }
-
-    // 获取行选择器选中的行
-    selectedRows(name) {
-        return this.selectors[name || defaultName].getSelectedRows()
-    }
-}
+
+let defaultName = '_def_';
+
+export default class Grid {
+    constructor(Dcat) {
+        Dcat.grid = this;
+
+        this.selectors = {};
+    }
+
+    // 添加行选择器对象
+    addSelector(selector, name) {
+        this.selectors[name || defaultName] = selector
+    }
+
+    // 获取行选择器选中的ID字符串
+    selected(name) {
+        return this.selectors[name || defaultName].getSelectedKeys()
+    }
+
+    // 获取行选择器选中的行
+    selectedRows(name) {
+        return this.selectors[name || defaultName].getSelectedRows()
+    }
+
+    async(options) {
+        return new AsyncGrid(options);
+    }
+}
+
+class AsyncGrid {
+    constructor(options) {
+        let nullFun = function () {};
+
+        options = $.extend({
+            selector: null,
+            bodySelector: '.async-body',
+            tableSelector: '.async-table',
+            queryName: null,
+            url: null,
+            loadingStyle: 'height:240px;',
+            before: nullFun,
+            after: nullFun,
+        }, options);
+
+        var self = this,
+            $box = $(options.selector),
+            $body = $box.find(options.bodySelector);
+
+        self.options = options;
+        self.$box = $box;
+        self.$body = $body;
+        self.loading = false;
+    }
+
+    render(url, callback) {
+        let self = this, options = self.options;
+
+        url = url || options.url;
+
+        if (self.loading || url.indexOf('javascript:') !== -1) {
+            return;
+        }
+        self.loading = true;
+
+        let $box = self.$box,
+            $body = self.$body,
+            reqName = options.queryName,
+            tableSelector = options.tableSelector,
+            $table = $body.find(tableSelector),
+            events = {0: 'grid:rendering', 1: 'grid:render', 2: 'grid:rendered'},
+            before = options.before,
+            after = options.after;
+
+        // 开始渲染前事件
+        before($box, url);
+        $box.trigger(events[0], [url]);
+        $body.trigger(events[0], [url]);
+
+        // loading效果
+        let loadingOptions = {background: 'transparent'}
+        if ($body.find(`${tableSelector} tbody tr`).length <= 2) {
+            loadingOptions['style'] = options.loadingStyle;
+        }
+        $table.loading(loadingOptions);
+        Dcat.NP.start();
+
+        if (url.indexOf('?') === -1) {
+            url += '?';
+        }
+
+        if (url.indexOf(reqName) === -1) {
+            url += '&'+reqName+'=1';
+        }
+
+        history.pushState({}, '', url.replace(reqName+'=1', ''));
+
+        $box.data('current', url);
+
+        Dcat.helpers.asyncRender(url, function (html) {
+            self.loading = false;
+            Dcat.NP.done();
+
+            $body.html(html);
+
+            let refresh = function () {
+                self.render($box.data('current'));
+            };
+
+            // 表格渲染事件
+            $box.off(events[1]).on(events[1], refresh);
+            $body.off(events[1]).on(events[1], refresh);
+            $table.on(events[1], refresh);
+
+            // 刷新按钮
+            $box.find('.grid-refresh').off('click').on('click', function () {
+                refresh();
+
+                return false;
+            });
+
+            // 分页
+            $box.find('.pagination .page-link').on('click', loadLink);
+            // 页选择器
+            $box.find('.per-pages-selector .dropdown-item a').on('click', loadLink);
+            // 表头url
+            $box.find('.grid-column-header a').on('click', loadLink);
+
+            // 快捷搜索、表头搜索以及过滤器筛选
+            $box.find('form').off('submit').on('submit', function () {
+                var action = $(this).attr('action');
+
+                if ($(this).attr('method') === 'post') {
+                    return;
+                }
+
+                if (action.indexOf('?') === -1) {
+                    action += '?';
+                }
+
+                self.render(action+'&'+$(this).serialize());
+
+                return false;
+            });
+
+            $box.find('.filter-box .reset').on('click', loadLink);
+
+            // 规格选择器
+            $box.find('.grid-selector a').on('click', loadLink);
+
+            // 渲染完成后事件
+            $box.trigger(events[2], [url, html]);
+            $body.trigger(events[2], [url, html]);
+            $table.trigger(events[2], [url, html]);
+
+            after($box, url, html);
+
+            callback && callback($box, url, html);
+        });
+
+        function loadLink() {
+            self.render($(this).attr('href'));
+
+            return false;
+        }
+    }
+}

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
resources/dist/adminlte/adminlte.css


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
resources/dist/adminlte/adminlte.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
resources/dist/adminlte/adminlte.js.map


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
resources/dist/dcat/extra/action.js.map


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
resources/dist/dcat/extra/grid-extend.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
resources/dist/dcat/extra/grid-extend.js.map


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
resources/dist/dcat/extra/select-table.js.map


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
resources/dist/dcat/extra/upload.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
resources/dist/dcat/extra/upload.js.map


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
resources/dist/dcat/js/dcat-app.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
resources/dist/dcat/js/dcat-app.js.map


+ 1 - 1
resources/views/filter/button.blade.php

@@ -6,7 +6,7 @@
     >
         <i class="feather icon-filter"></i>@if($filter_text)<span class="d-none d-sm-inline">&nbsp;&nbsp;{{ trans('admin.filter') }}</span>@endif
 
-        @if($valueCount) &nbsp;({!! $valueCount !!}) @endif
+        <span class="filter-count">@if($valueCount) &nbsp;({!! $valueCount !!}) @endif</span>
     </button>
     @if($scopes->isNotEmpty())
         <ul class="dropdown-menu" role="menu">

+ 189 - 0
resources/views/grid/async-fixed-table.blade.php

@@ -0,0 +1,189 @@
+@if($grid->isAsyncRequest())
+    {!! $grid->renderHeader() !!}
+
+    <div class="table-responsive table-wrapper {{ $grid->option('table_collapse') ? 'table-collapse' : '' }}">
+        <div class="tables-container">
+            <div class="table-wrap table-main" data-height="{{ $tableHeight }}">
+                <table class="custom-data-table async-table {{ $grid->formatTableClass() }}" id="{{ $tableId }}">
+                    <thead>
+                    @if ($headers = $grid->getVisibleComplexHeaders())
+                        <tr>
+                            @foreach($headers as $header)
+                                {!! $header->render() !!}
+                            @endforeach
+                        </tr>
+                    @endif
+                    <tr>
+                        @foreach($grid->getVisibleColumns() as $column)
+                            <th {!! $column->formatTitleAttributes() !!}>{!! $column->getLabel() !!}{!! $column->renderHeader() !!}</th>
+                        @endforeach
+                    </tr>
+                    </thead>
+
+                    @if ($grid->hasQuickCreate())
+                        {!! $grid->renderQuickCreate() !!}
+                    @endif
+
+                    <tbody>
+                    @foreach($grid->rows() as $row)
+                        <tr {!! $row->rowAttributes() !!}>
+                            @foreach($grid->getVisibleColumnNames() as $name)
+                                <td {!! $row->columnAttributes($name) !!}>
+                                    {!! $row->column($name) !!}
+                                </td>
+                            @endforeach
+                        </tr>
+                    @endforeach
+                    @if ($grid->rows()->isEmpty())
+                        <tr>
+                            <td colspan="{!! count($grid->getVisibleColumnNames()) !!}">
+                                <div style="margin:5px 0 0 10px;"><span class="help-block" style="margin-bottom:0"><i class="feather icon-alert-circle"></i>&nbsp;{{ trans('admin.no_data') }}</span></div>
+                            </td>
+                        </tr>
+                    @endif
+                    </tbody>
+                </table>
+            </div>
+
+            @if ($grid->leftVisibleColumns()->isNotEmpty() || $grid->leftVisibleComplexColumns()->isNotEmpty())
+                <div class="table-wrap table-fixed table-fixed-left" data-height="{{ $tableHeight }}">
+                    <table class="custom-data-table  {{ $grid->formatTableClass() }} ">
+                        <thead>
+
+                        @if ($grid->getVisibleComplexHeaders())
+                            <tr>
+                                @foreach($grid->leftVisibleComplexColumns() as $header)
+                                    {!! $header->render() !!}
+                                @endforeach
+                            </tr>
+                            <tr>
+                                @foreach($grid->leftVisibleComplexColumns() as $header)
+                                    @if ($header->getColumnNames()->count() > 1)
+                                        @foreach($header->columns() as $column)
+                                            <th {!! $column->formatTitleAttributes() !!}>{!! $column->getLabel() !!}{!! $column->renderHeader() !!}</th>
+                                        @endforeach
+                                    @endif
+                                @endforeach
+                            </tr>
+                        @else
+                            <tr>
+                                @foreach($grid->leftVisibleColumns() as $column)
+                                    <th {!! $column->formatTitleAttributes() !!}>{!! $column->getLabel() !!}{!! $column->renderHeader() !!}</th>
+                                @endforeach
+                            </tr>
+                        @endif
+                        </thead>
+                        <tbody>
+
+                        @foreach($grid->rows() as $row)
+                            <tr {!! $row->rowAttributes() !!}>
+                                @foreach($grid->leftVisibleColumns() as $column)
+                                    <td {!! $row->columnAttributes($column->getName()) !!}>
+                                        {!! $row->column($column->getName()) !!}
+                                    </td>
+                                @endforeach
+                            </tr>
+                        @endforeach
+                        </tbody>
+                    </table>
+                </div>
+            @endif
+
+            @if ($grid->rightVisibleColumns()->isNotEmpty() || $grid->rightVisibleComplexColumns()->isNotEmpty())
+                <div class="table-wrap table-fixed table-fixed-right" data-height="{{ $tableHeight }}">
+                    <table class="custom-data-table  {{ $grid->formatTableClass() }} ">
+                        <thead>
+                        @if ($grid->getVisibleComplexHeaders())
+                            <tr>
+                                @foreach($grid->rightVisibleComplexColumns() as $header)
+                                    {!! $header->render() !!}
+                                @endforeach
+                            </tr>
+                            <tr>
+                                @foreach($grid->rightVisibleComplexColumns() as $header)
+                                    @if ($header->getColumnNames()->count() > 1)
+                                        @foreach($header->columns() as $column)
+                                            <th {!! $column->formatTitleAttributes() !!}>{!! $column->getLabel() !!}{!! $column->renderHeader() !!}</th>
+                                        @endforeach
+                                    @endif
+                                @endforeach
+                            </tr>
+                        @else
+                            <tr>
+                                @foreach($grid->rightVisibleColumns() as $column)
+                                    <th {!! $column->formatTitleAttributes() !!}>{!! $column->getLabel() !!}{!! $column->renderHeader() !!}</th>
+                                @endforeach
+                            </tr>
+                        @endif
+
+                        </thead>
+
+                        <tbody>
+
+                        @foreach($grid->rows() as $row)
+                            <tr {!! $row->rowAttributes() !!}>
+                                @foreach($grid->rightVisibleColumns() as $column)
+                                    <td {!! $row->columnAttributes($column->getName()) !!}>
+                                        {!! $row->column($column->getName()) !!}
+                                    </td>
+                                @endforeach
+                            </tr>
+                        @endforeach
+                        </tbody>
+                    </table>
+                </div>
+            @endif
+        </div>
+    </div>
+
+    {!! $grid->renderFooter() !!}
+
+    {!! $grid->renderPagination() !!}
+@else
+<div class="dcat-box custom-data-table async-{{ $tableId }}">
+    @include('admin::grid.table-toolbar')
+
+    {!! $grid->renderFilter() !!}
+
+    <div class="async-body">
+        {!! $grid->renderHeader() !!}
+
+        <div class="table-responsive table-wrapper {{ $grid->option('table_collapse') ? 'table-collapse' : '' }}">
+            <div class="tables-container">
+                <div class="table-wrap table-main" data-height="{{ $tableHeight }}">
+                    <table class="custom-data-table async-table {{ $grid->formatTableClass() }}" id="{{ $tableId }}">
+                        <thead>
+                        @if ($headers = $grid->getVisibleComplexHeaders())
+                            <tr>
+                                @foreach($headers as $header)
+                                    {!! $header->render() !!}
+                                @endforeach
+                            </tr>
+                        @endif
+                        <tr>
+                            @foreach($grid->getVisibleColumns() as $column)
+                                <th {!! $column->formatTitleAttributes() !!}>{!! $column->getLabel() !!}{!! $column->renderHeader() !!}</th>
+                            @endforeach
+                        </tr>
+                        </thead>
+
+                        @if ($grid->hasQuickCreate())
+                            {!! $grid->renderQuickCreate() !!}
+                        @endif
+
+                        <tbody>
+                            <tr>
+                                <td colspan="{!! count($grid->getVisibleColumnNames()) !!}">
+                                    <div style="margin:5px 0 0 10px;"><span class="help-block" style="margin-bottom:0"><i class="feather icon-alert-circle"></i>&nbsp;{{ trans('admin.no_data') }}</span></div>
+                                </td>
+                            </tr>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        </div>
+
+        {!! $grid->renderFooter() !!}
+    </div>
+</div>
+@endif

+ 87 - 0
resources/views/grid/async-table.blade.php

@@ -0,0 +1,87 @@
+@if($grid->isAsyncRequest())
+    {!! $grid->renderHeader() !!}
+
+    <div class="{!! $grid->formatTableParentClass() !!}">
+        <table class="async-table {{ $grid->formatTableClass() }}" id="{{ $tableId }}" >
+            <thead>
+            @if ($headers = $grid->getVisibleComplexHeaders())
+                <tr>
+                    @foreach($headers as $header)
+                        {!! $header->render() !!}
+                    @endforeach
+                </tr>
+            @endif
+            <tr>
+                @foreach($grid->getVisibleColumns() as $column)
+                    <th {!! $column->formatTitleAttributes() !!}>{!! $column->getLabel() !!}{!! $column->renderHeader() !!}</th>
+                @endforeach
+            </tr>
+            </thead>
+
+            @if ($grid->hasQuickCreate())
+                {!! $grid->renderQuickCreate() !!}
+            @endif
+
+            <tbody>
+            @foreach($grid->rows() as $row)
+                <tr {!! $row->rowAttributes() !!}>
+                    @foreach($grid->getVisibleColumnNames() as $name)
+                        <td {!! $row->columnAttributes($name) !!}>{!! $row->column($name) !!}</td>
+                    @endforeach
+                </tr>
+            @endforeach
+            @if ($grid->rows()->isEmpty())
+                <tr>
+                    <td colspan="{!! count($grid->getVisibleColumnNames()) !!}">
+                        <div style="margin:5px 0 0 10px;"><span class="help-block" style="margin-bottom:0"><i class="feather icon-alert-circle"></i>&nbsp;{{ trans('admin.no_data') }}</span></div>
+                    </td>
+                </tr>
+            @endif
+            </tbody>
+        </table>
+    </div>
+
+    {!! $grid->renderFooter() !!}
+
+    {!! $grid->renderPagination() !!}
+@else
+    <div class="dcat-box async-{{ $tableId }}">
+
+        <div class="d-block pb-0">
+            @include('admin::grid.table-toolbar')
+        </div>
+
+        {!! $grid->renderFilter() !!}
+
+        <div class="async-body">
+            {!! $grid->renderHeader() !!}
+
+            <div class="{!! $grid->formatTableParentClass() !!}">
+                <table class="async-table {{ $grid->formatTableClass() }}" id="{{ $tableId }}" >
+                    <thead>
+                    @if ($headers = $grid->getVisibleComplexHeaders())
+                        <tr>
+                            @foreach($headers as $header)
+                                {!! $header->render() !!}
+                            @endforeach
+                        </tr>
+                    @endif
+                    <tr>
+                        @foreach($grid->getVisibleColumns() as $column)
+                            <th {!! $column->formatTitleAttributes() !!}>{!! $column->getLabel() !!}{!! $column->renderHeader() !!}</th>
+                        @endforeach
+                    </tr>
+                    </thead>
+
+                    <tbody>
+                    <tr>
+                        <td colspan="{!! count($grid->getVisibleColumnNames()) !!}">&nbsp;</td>
+                    </tr>
+                    </tbody>
+                </table>
+            </div>
+
+            {!! $grid->renderFooter() !!}
+        </div>
+    </div>
+@endif

+ 3 - 3
resources/views/grid/fixed-table.blade.php

@@ -8,7 +8,7 @@
     <div class="table-responsive table-wrapper {{ $grid->option('table_collapse') ? 'table-collapse' : '' }}">
         <div class="tables-container">
             <div class="table-wrap table-main" data-height="{{ $tableHeight }}">
-                <table class="custom-data-table dataTable {{ $grid->formatTableClass() }}" id="{{ $tableId }}">
+                <table class="custom-data-table {{ $grid->formatTableClass() }}" id="{{ $tableId }}">
                     <thead>
                     @if ($headers = $grid->getVisibleComplexHeaders())
                         <tr>
@@ -51,7 +51,7 @@
 
             @if ($grid->leftVisibleColumns()->isNotEmpty() || $grid->leftVisibleComplexColumns()->isNotEmpty())
                 <div class="table-wrap table-fixed table-fixed-left" data-height="{{ $tableHeight }}">
-                    <table class="custom-data-table dataTable {{ $grid->formatTableClass() }} ">
+                    <table class="custom-data-table {{ $grid->formatTableClass() }} ">
                         <thead>
 
                         @if ($grid->getVisibleComplexHeaders())
@@ -95,7 +95,7 @@
 
             @if ($grid->rightVisibleColumns()->isNotEmpty() || $grid->rightVisibleComplexColumns()->isNotEmpty())
                 <div class="table-wrap table-fixed table-fixed-right" data-height="{{ $tableHeight }}">
-                    <table class="custom-data-table dataTable {{ $grid->formatTableClass() }} ">
+                    <table class="custom-data-table {{ $grid->formatTableClass() }} ">
                         <thead>
                         @if ($grid->getVisibleComplexHeaders())
                             <tr>

+ 1 - 5
resources/views/grid/table.blade.php

@@ -34,9 +34,7 @@
             @foreach($grid->rows() as $row)
                 <tr {!! $row->rowAttributes() !!}>
                     @foreach($grid->getVisibleColumnNames() as $name)
-                        <td {!! $row->columnAttributes($name) !!}>
-                            {!! $row->column($name) !!}
-                        </td>
+                        <td {!! $row->columnAttributes($name) !!}>{!! $row->column($name) !!}</td>
                     @endforeach
                 </tr>
             @endforeach
@@ -56,5 +54,3 @@
     {!! $grid->renderPagination() !!}
 
 </div>
-
-

+ 56 - 1
src/Admin.php

@@ -13,6 +13,7 @@ use Dcat\Admin\Layout\Navbar;
 use Dcat\Admin\Layout\SectionManager;
 use Dcat\Admin\Repositories\EloquentRepository;
 use Dcat\Admin\Support\Composer;
+use Dcat\Admin\Support\Helper;
 use Dcat\Admin\Traits\HasAssets;
 use Dcat\Admin\Traits\HasHtml;
 use Dcat\Admin\Traits\HasPermissions;
@@ -30,7 +31,7 @@ class Admin
     use HasAssets;
     use HasHtml;
 
-    const VERSION = '2.0.24-beta';
+    const VERSION = '2.1.0-beta';
 
     const SECTION = [
         // 往 <head> 标签内输入内容
@@ -379,6 +380,60 @@ class Admin
         return static::context()->ignoreQueries ?? [];
     }
 
+    /**
+     * 输入直接渲染的内容.
+     *
+     * @param mixed $value
+     */
+    public static function content($value)
+    {
+        if ($value !== null) {
+            static::context()->add('contents', $value);
+        }
+    }
+
+    /**
+     * @return bool
+     */
+    public static function hasContents()
+    {
+        return count(static::context()->getArray('contents')) > 0;
+    }
+
+    /**
+     * 渲染内容.
+     *
+     * @return string|void
+     */
+    public static function renderContents()
+    {
+        if (! static::hasContents()) {
+            return;
+        }
+
+        $contents = static::context()->getArray('contents');
+
+        $results = '';
+
+        foreach ($contents as $content) {
+            $results .= Helper::render($content);
+        }
+
+        $asset = static::asset();
+
+        Admin::baseCss([], false);
+        Admin::baseJs([], false);
+        Admin::headerJs([], false);
+
+        $results .= Admin::html()
+            .$asset->jsToHtml()
+            .$asset->cssToHtml()
+            .$asset->scriptToHtml()
+            .$asset->styleToHtml();
+
+        return $results;
+    }
+
     /**
      * 响应json数据.
      *

+ 103 - 16
src/Grid.php

@@ -40,6 +40,7 @@ class Grid
 
     const CREATE_MODE_DEFAULT = 'default';
     const CREATE_MODE_DIALOG = 'dialog';
+    const ASYNC_NAME = '_async_';
 
     /**
      * The grid data model instance.
@@ -183,6 +184,11 @@ class Grid
      */
     protected $show = true;
 
+    /**
+     * @var bool
+     */
+    protected $async = false;
+
     /**
      * Create a new grid instance.
      *
@@ -272,6 +278,47 @@ class Grid
         return $this->addColumn('#', $label ?: '#');
     }
 
+    /**
+     * 启用异步渲染功能.
+     *
+     * @param bool $async
+     *
+     * @return $this
+     */
+    public function async(bool $async = true)
+    {
+        $this->async = $async;
+
+        if ($async) {
+            $this->view('admin::grid.async-table');
+        }
+
+        return $this;
+    }
+
+    public function getAsync()
+    {
+        return $this->async;
+    }
+
+    /**
+     * 判断是否允许查询数据.
+     *
+     * @return bool
+     */
+    public function buildable()
+    {
+        return ! $this->async || $this->isAsyncRequest();
+    }
+
+    /**
+     * @return bool
+     */
+    public function isAsyncRequest()
+    {
+        return $this->request->get(static::ASYNC_NAME);
+    }
+
     /**
      * Batch add column to grid.
      *
@@ -431,6 +478,18 @@ class Grid
      */
     public function build()
     {
+        if (! $this->buildable()) {
+            $this->callBuilder();
+            $this->handleExportRequest();
+
+            $this->prependRowSelectorColumn();
+            $this->appendActionsColumn();
+
+            $this->sortHeaders();
+
+            return;
+        }
+
         if ($this->built) {
             return;
         }
@@ -506,17 +565,7 @@ class Grid
      */
     public function getCreateUrl()
     {
-        $queryString = '';
-
-        if ($constraints = $this->model()->getConstraints()) {
-            $queryString = http_build_query($constraints);
-        }
-
-        return sprintf(
-            '%s/create%s',
-            $this->resource(),
-            $queryString ? ('?'.$queryString) : ''
-        );
+        return $this->urlWithConstraints($this->resource().'/create');
     }
 
     /**
@@ -526,8 +575,16 @@ class Grid
      */
     public function getEditUrl($key)
     {
-        $url = "{$this->resource()}/{$key}/edit";
+        return $this->urlWithConstraints("{$this->resource()}/{$key}/edit");
+    }
 
+    /**
+     * @param string $url
+     *
+     * @return string
+     */
+    public function urlWithConstraints(?string $url)
+    {
         $queryString = '';
 
         if ($constraints = $this->model()->getConstraints()) {
@@ -860,9 +917,7 @@ HTML;
      */
     public function with(array $variables)
     {
-        $this->variables = $variables;
-
-        return $this;
+        return $this->addVariables($variables);
     }
 
     /**
@@ -988,9 +1043,41 @@ HTML;
 
         $this->setUpOptions();
 
+        $this->addFilterScript();
+
+        $this->addScript();
+
         return $this->doWrap();
     }
 
+    public function getView()
+    {
+        if ($this->async && $this->hasFixColumns()) {
+            return 'admin::grid.async-fixed-table';
+        }
+
+        return $this->view;
+    }
+
+    protected function addScript()
+    {
+        if ($this->async && ! $this->isAsyncRequest()) {
+            $query = static::ASYNC_NAME;
+            $url = Helper::fullUrlWithoutQuery(['_pjax']);
+            $url = Helper::urlWithQuery($url, [static::ASYNC_NAME => 1]);
+
+            Admin::script(
+                <<<JS
+Dcat.grid.async({
+    selector: '.async-{$this->getTableId()}',
+    queryName: '{$query}',
+    url: '{$url}',
+}).render()
+JS
+            );
+        }
+    }
+
     /**
      * @return string
      */
@@ -1000,7 +1087,7 @@ HTML;
             return;
         }
 
-        $view = view($this->view, $this->variables());
+        $view = view($this->getView(), $this->variables());
 
         if (! $wrapper = $this->wrapper) {
             return $view->render();

+ 1 - 1
src/Grid/Actions/Show.php

@@ -23,6 +23,6 @@ class Show extends RowAction
      */
     public function href()
     {
-        return "{$this->resource()}/{$this->getKey()}";
+        return $this->parent->urlWithConstraints("{$this->resource()}/{$this->getKey()}");
     }
 }

+ 5 - 0
src/Grid/Concerns/CanFixColumns.php

@@ -29,6 +29,11 @@ trait CanFixColumns
         return $this->fixColumns;
     }
 
+    public function hasFixColumns()
+    {
+        return $this->fixColumns;
+    }
+
     protected function resetActions()
     {
         $actions = $this->getActionClass();

+ 18 - 0
src/Grid/Concerns/HasFilter.php

@@ -3,6 +3,7 @@
 namespace Dcat\Admin\Grid\Concerns;
 
 use Closure;
+use Dcat\Admin\Admin;
 use Dcat\Admin\Grid;
 use Illuminate\Support\Collection;
 
@@ -137,4 +138,21 @@ trait HasFilter
     {
         return $this->disableFilterButton(! $val);
     }
+
+    protected function addFilterScript()
+    {
+        if (! $this->isAsyncRequest()) {
+            return;
+        }
+
+        Admin::script(
+            <<<JS
+var count = {$this->filter()->countConditions()};
+
+if (count > 0) {
+    $('.async-{$this->getTableId()}').find('.filter-count').text('('+count+')');
+}
+JS
+        );
+    }
 }

+ 11 - 0
src/Grid/Filter.php

@@ -533,6 +533,17 @@ class Filter implements Renderable
         return $this->filters;
     }
 
+    /**
+     * 统计查询条件的数量.
+     *
+     * @return int
+     */
+    public function countConditions()
+    {
+        return $this->mode() === Filter::MODE_RIGHT_SIDE
+            ? count($this->getConditions()) : 0;
+    }
+
     /**
      * @param string $key
      * @param string $label

+ 13 - 13
src/Grid/FixColumns.php

@@ -170,32 +170,32 @@ class FixColumns
 
 (function () {
     var $tableMain = $('.table-main'), minHeight = 600;
-    
+
     var theadHeight = $('.table-main thead tr').outerHeight();
     $('.table-fixed thead tr').outerHeight(theadHeight);
-    
+
     var tfootHeight = $('.table-main tfoot tr').outerHeight();
     $('.table-fixed tfoot tr').outerHeight(tfootHeight);
-    
+
     $('.table-main tbody tr').each(function(i, obj) {
         var height = $(obj).outerHeight();
 
         $('.table-fixed-left tbody tr').eq(i).outerHeight(height);
         $('.table-fixed-right tbody tr').eq(i).outerHeight(height);
     });
-    
+
     if ($tableMain.width() >= $tableMain.prop('scrollWidth') || $(window).width() < 600) {
         $('.table-fixed').hide();
     } else {
         var height = ($(window).height() - 220);
         height = height < minHeight ? minHeight : height;
-        
+
         $tableMain.each(function (k, v) {
             var tableHight = $(v).find('.custom-data-table.table').eq(0).height();
             var maxHeight = $(v).data('height') || (height >= tableHight ? tableHight : height);
-            
+
             $(v).css({'max-height': maxHeight + 'px'});
-            
+
             if (maxHeight < tableHight) {
                 $(v).parents('.tables-container').find('.table-fixed-right').css({right: '12px'})
             }
@@ -203,24 +203,24 @@ class FixColumns
         $('.table-fixed-right,.table-fixed-left').each(function (k, v) {
             $(v).css({'max-height': (($(v).data('height') || height) - 15) + 'px'});
         });
-        
+
         $tableMain.scroll(function () {
-            var self = $(this); 
-            
+            var self = $(this);
+
             self.parents('.tables-container').find('.table-fixed-right,.table-fixed-left').scrollTop(self.scrollTop());
         });
     }
-    
+
     $('.table-wrap tbody tr').on('mouseover', function () {
         var index = $(this).index();
         $('.table-main tbody tr').eq(index).addClass('active');
         $('.table-fixed-left tbody tr').eq(index).addClass('active');
         $('.table-fixed-right tbody tr').eq(index).addClass('active');
     });
-    
+
     $('.table-wrap tbody tr').on('mouseout', function () {
         var index = $(this).index();
-        
+
         $('.table-main tbody tr').eq(index).removeClass('active');
         $('.table-fixed-left tbody tr').eq(index).removeClass('active');
         $('.table-fixed-right tbody tr').eq(index).removeClass('active');

+ 1 - 1
src/Grid/LazyRenderable.php

@@ -89,7 +89,7 @@ HTML;
             $visibleColumn && $grid->rowSelector()->titleColumn($visibleColumn);
         }
 
-        return $grid;
+        return $grid->async(false);
     }
 
     /**

+ 10 - 11
src/Grid/Tools/FilterButton.php

@@ -57,33 +57,33 @@ class FilterButton extends AbstractTool
 
             $script = <<<JS
 (function () {
-    var slider, 
+    var slider,
         expand = {$expand};
-    
+
      function initSlider() {
         slider = new Dcat.Slider({
             target: '#{$id}',
         });
-        
+
         slider
             .\$container
             .find('.right-side-filter-container .header')
             .width(slider.\$container.width() - 20);
-        
+
         expand && setTimeout(slider.open.bind(slider), 10);
     }
-    
+
     expand && setTimeout(initSlider, 10);
-    
+
     $('.{$this->getElementClassName()}').on('click', function () {
         if (! slider) {
             initSlider()
         }
         slider.toggle();
-       
+
         return false
     });
-    
+
     $('.wrapper').on('click', '.modal', function (e) {
         if (typeof e.cancelBubble != "undefined") {
             e.cancelBubble = true;
@@ -103,7 +103,7 @@ JS;
             $script = <<<JS
 $('.{$this->getElementClassName()}').on('click', function(){
     $('#{$id}').parent().toggleClass('d-none');
-}); 
+});
 JS;
         }
 
@@ -141,8 +141,7 @@ JS;
 
         $scopres = $filter->scopes();
         $filters = $filter->filters();
-        $valueCount = $filter->mode() === Filter::MODE_RIGHT_SIDE
-            ? count($this->parent->filter()->getConditions()) : 0;
+        $valueCount = $filter->countConditions();
 
         if ($scopres->isEmpty() && ! $filters) {
             return;

+ 1 - 1
src/Grid/Tools/PerPageSelector.php

@@ -86,7 +86,7 @@ class PerPageSelector implements Renderable
             ->render();
 
         return <<<EOT
-<label class="pull-right d-none d-sm-inline" style="margin-right: 10px">
+<label class="pull-right d-none d-sm-inline per-pages-selector" style="margin-right: 10px">
     $dropdown
 </label>
 EOT;

+ 4 - 2
src/Http/Controllers/PermissionController.php

@@ -119,9 +119,8 @@ class PermissionController extends AdminController
                     ->setTitleColumn('title')
                     ->nodes(function () {
                         $model = config('admin.database.menu_model');
-                        $model = new $model();
 
-                        return $model->allNodes();
+                        return (new $model())->allNodes();
                     })
                     ->customFormat(function ($v) {
                         if (! $v) {
@@ -137,6 +136,9 @@ class PermissionController extends AdminController
 
             $form->disableViewButton();
             $form->disableViewCheck();
+        })->saved(function () {
+            $model = config('admin.database.menu_model');
+            (new $model())->flushCache();
         });
     }
 

+ 4 - 2
src/Http/Controllers/RoleController.php

@@ -117,9 +117,8 @@ class RoleController extends AdminController
                     ->setTitleColumn('title')
                     ->nodes(function () {
                         $model = config('admin.database.menu_model');
-                        $model = new $model();
 
-                        return $model->allNodes();
+                        return (new $model())->allNodes();
                     })
                     ->customFormat(function ($v) {
                         if (! $v) {
@@ -137,6 +136,9 @@ class RoleController extends AdminController
             if ($id == $roleModel::ADMINISTRATOR_ID) {
                 $form->disableDeleteButton();
             }
+        })->saved(function () {
+            $model = config('admin.database.menu_model');
+            (new $model())->flushCache();
         });
     }
 

+ 0 - 68
src/Http/Middleware/Pjax.php

@@ -6,7 +6,6 @@ use Closure;
 use Dcat\Admin\Admin;
 use Illuminate\Http\Request;
 use Illuminate\Support\MessageBag;
-use Symfony\Component\DomCrawler\Crawler;
 use Symfony\Component\HttpFoundation\Response;
 
 class Pjax
@@ -64,73 +63,6 @@ class Pjax
         return back()->withInput()->withErrors($error, 'exception');
     }
 
-    /**
-     * Prepare the PJAX-specific response content.
-     *
-     * @param Response $response
-     * @param string   $container
-     *
-     * @return $this
-     */
-    protected function filterResponse(Response $response, $container)
-    {
-        $crawler = new Crawler($response->getContent());
-
-        $response->setContent(
-            $this->makeTitle($crawler).
-            $this->fetchContents($crawler, $container)
-        );
-
-        return $this;
-    }
-
-    /**
-     * Prepare an HTML title tag.
-     *
-     * @param Crawler $crawler
-     *
-     * @return string
-     */
-    protected function makeTitle($crawler)
-    {
-        $pageTitle = $crawler->filter('head > title')->html();
-
-        return "<title>{$pageTitle}</title>";
-    }
-
-    /**
-     * Fetch the PJAX-specific HTML from the response.
-     *
-     * @param Crawler $crawler
-     * @param string  $container
-     *
-     * @return string
-     */
-    protected function fetchContents($crawler, $container)
-    {
-        $content = $crawler->filter($container);
-
-        if (! $content->count()) {
-            abort(422);
-        }
-
-        return $this->decodeUtf8HtmlEntities($content->html());
-    }
-
-    /**
-     * Decode utf-8 characters to html entities.
-     *
-     * @param string $html
-     *
-     * @return string
-     */
-    protected function decodeUtf8HtmlEntities($html)
-    {
-        return preg_replace_callback('/(&#[0-9]+;)/', function ($html) {
-            return mb_convert_encoding($html[1], 'UTF-8', 'HTML-ENTITIES');
-        }, $html);
-    }
-
     /**
      * Set the PJAX-URL header to the current uri.
      *

+ 5 - 5
src/Layout/Asset.php

@@ -587,13 +587,13 @@ class Asset
      *
      * @param string|array $js
      */
-    public function headerJs($js)
+    public function headerJs($js, bool $merge = true)
     {
-        if (! $js) {
-            return;
+        if ($merge) {
+            $this->headerJs = $js ? array_merge($this->headerJs, (array) $js) : $this->headerJs;
+        } else {
+            $this->headerJs = (array) $js;
         }
-
-        $this->headerJs = array_merge($this->headerJs, (array) $js);
     }
 
     /**

+ 7 - 0
src/Layout/Column.php

@@ -2,6 +2,8 @@
 
 namespace Dcat\Admin\Layout;
 
+use Dcat\Admin\Admin;
+use Dcat\Admin\Grid;
 use Dcat\Admin\Support\Helper;
 use Illuminate\Contracts\Support\Renderable;
 
@@ -97,6 +99,11 @@ class Column implements Renderable
         $html = $this->startColumn();
 
         foreach ($this->contents as $content) {
+            if ($content instanceof Grid && $content->isAsyncRequest()) {
+                Admin::content($content->render());
+
+                continue;
+            }
             $html .= Helper::render($content);
         }
 

+ 4 - 0
src/Layout/Content.php

@@ -535,6 +535,10 @@ class Content implements Renderable
 
         $this->callComposed();
 
+        if (Admin::hasContents()) {
+            return Admin::renderContents();
+        }
+
         return view($this->view, $this->variables())->render();
     }
 

+ 1 - 0
src/Support/Context.php

@@ -15,6 +15,7 @@ use Illuminate\Support\Fluent;
  * @property array|null $ignoreQueries
  * @property array|null $jsVariables
  * @property string $translation
+ * @property array $contents
  */
 class Context extends Fluent
 {

+ 3 - 2
src/Traits/HasAssets.php

@@ -62,12 +62,13 @@ trait HasAssets
      * Add js.
      *
      * @param string|array $js
+     * @param bool $merge
      *
      * @return void
      */
-    public static function headerJs($js)
+    public static function headerJs($js, bool $merge = true)
     {
-        static::asset()->headerJs($js);
+        static::asset()->headerJs($js, $merge);
     }
 
     /**

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff