Просмотр исходного кода

Merge pull request #6 from jqhph/master

拉取最新
yxx 5 лет назад
Родитель
Сommit
4996dce8cd
33 измененных файлов с 297 добавлено и 164 удалено
  1. 20 2
      resources/assets/dcat/extra/upload.js
  2. 1 0
      resources/assets/dcat/sass/components/_menu.scss
  3. 0 0
      resources/dist/dcat/css/dcat-app-blue-dark.css
  4. 0 0
      resources/dist/dcat/css/dcat-app-blue-light.css
  5. 0 0
      resources/dist/dcat/css/dcat-app-blue.css
  6. 0 0
      resources/dist/dcat/css/dcat-app-green.css
  7. 0 0
      resources/dist/dcat/css/dcat-app.css
  8. 0 0
      resources/dist/dcat/extra/upload.js
  9. 0 0
      resources/dist/dcat/extra/upload.js.map
  10. 6 6
      resources/lang/zh-TW/admin.php
  11. 1 1
      resources/lang/zh-TW/select2.php
  12. 5 3
      resources/views/form/container.blade.php
  13. 1 1
      resources/views/layouts/full-page.blade.php
  14. 1 1
      resources/views/tree/branch.blade.php
  15. 1 1
      src/Admin.php
  16. 10 6
      src/Console/MinifyCommand.php
  17. 36 32
      src/Controllers/MenuController.php
  18. 1 45
      src/Controllers/PermissionController.php
  19. 31 0
      src/Form.php
  20. 59 21
      src/Form/Concerns/HasFiles.php
  21. 0 8
      src/Form/Field/Currency.php
  22. 25 10
      src/Form/Field/File.php
  23. 11 11
      src/Form/Field/HasMany.php
  24. 10 4
      src/Form/Field/Select.php
  25. 8 2
      src/Form/NestedForm.php
  26. 54 0
      src/Form/Row.php
  27. 1 1
      src/Grid/Displayers/Button.php
  28. 1 3
      src/Grid/Displayers/QRCode.php
  29. 0 1
      src/Grid/Exporters/AbstractExporter.php
  30. 10 1
      src/Grid/Model.php
  31. 1 1
      src/Grid/Tools/ExportButton.php
  32. 2 2
      src/Models/Repositories/Menu.php
  33. 1 1
      src/Repositories/EloquentRepository.php

+ 20 - 2
resources/assets/dcat/extra/upload.js

@@ -3,6 +3,7 @@
         opts = $.extend({
             wrapper: '.web-uploader', // 图片显示容器选择器
             addFileButton: '.add-file-button', // 继续添加按钮选择器
+            inputSelector: '',
             isImage: false,
             preview: [], // 数据预览
             server: '',
@@ -66,6 +67,7 @@
 
         var $selector = $(opts.selector),
             updateColumn = opts.upload.formData.upload_column || ('webup' + Math.floor(Math.random()*10000)),
+            relation = opts.upload.formData._relation, // 一对多关联关系名称
             elementName = opts.elementName;
 
         if (typeof opts.upload.formData._id == "undefined" || !opts.upload.formData._id) {
@@ -100,7 +102,7 @@
             originalFilesNum = Dcat.helpers.len(opts.preview),
 
             // 上传表单
-            $input = $selector.find('input[name="' + elementName + '"]'),
+            $input = $selector.find(opts.inputSelector),
 
             // 获取文件视图选择器
             getFileViewSelector = function (fileId) {
@@ -357,6 +359,7 @@
                                 return uploader.removeFile(file);
                             }
                             post._column = updateColumn;
+                            post._relation = relation;
 
                             Dcat.loading();
                             $.post(opts.deleteUrl, post, function (result) {
@@ -484,7 +487,21 @@
                 return;
             }
 
-            form[updateColumn] = values.join(',');
+            if (relation) {
+                if (! relation[1]) {
+                    // 新增子表记录,则不调用update接口
+                    return;
+                }
+
+                form[relation[0]] = {};
+
+                form[relation[0]][relation[1]] = {};
+                form[relation[0]][relation[1]][updateColumn] = values.join(',');
+            } else {
+                form[updateColumn] = values.join(',');
+            }
+
+            delete form['_relation'];
             delete form['upload_column'];
 
             $.post(opts.updateServer, form);
@@ -867,6 +884,7 @@
                 Dcat.confirm(__('confirm_delete_file'), file.serverId, function () {
                     post.key = fileId;
                     post._column = updateColumn;
+                    post._relation = relation;
 
                     Dcat.loading();
                     $.post(opts.deleteUrl, post, function (result) {

+ 1 - 0
resources/assets/dcat/sass/components/_menu.scss

@@ -77,6 +77,7 @@ body.sidebar-collapse {
 .main-sidebar {
   .nav-sidebar .nav-item > .nav-link {
     font-size: 1.1rem;
+    border-radius: .1rem;
   }
 }
 

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
resources/dist/dcat/css/dcat-app-blue-dark.css


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
resources/dist/dcat/css/dcat-app-blue-light.css


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
resources/dist/dcat/css/dcat-app-blue.css


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
resources/dist/dcat/css/dcat-app-green.css


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
resources/dist/dcat/css/dcat-app.css


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
resources/dist/dcat/extra/upload.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
resources/dist/dcat/extra/upload.js.map


+ 6 - 6
resources/lang/zh-TW/admin.php

@@ -3,8 +3,8 @@
 return [
     'scaffold' => [
         'header'            => '代碼生成器',
-        'choose'            => '選擇有數據表',
-        'table'             => '表',
+        'choose'            => '選擇有數據表',
+        'table'             => '表',
         'model'             => '模型',
         'controller'        => '控制器',
         'add_field'         => '添加欄位',
@@ -96,8 +96,8 @@ return [
     'update_failed'         => '更新失敗 !',
     'save_succeeded'        => '儲存成功!',
     'save_failed'           => '儲存失敗 !',
-    'refresh_succeeded'     => '成功重新整理!',
-    'login_successful'      => '成功登入!',
+    'refresh_succeeded'     => '刷新成功!',
+    'login_successful'      => '登入成功!',
     'choose'                => '選擇',
     'choose_file'           => '選擇檔案',
     'choose_image'          => '選擇圖片',
@@ -110,7 +110,7 @@ return [
     'created_at'            => '建立時間',
     'updated_at'            => '更新時間',
     'alert'                 => '警告',
-    'parent_id'             => '父目錄',
+    'parent_id'             => '父',
     'icon'                  => '圖示',
     'uri'                   => '路徑',
     'operation_log'         => '操作記錄',
@@ -140,7 +140,7 @@ return [
         'method' => 'HTTP方法',
         'path'   => 'HTTP路徑',
     ],
-    'all_methods_if_empty'  => '為空默認為所有方法',
+    'all_methods_if_empty'  => '所有方法預設為空',
     'all'                   => '全部',
     'current_page'          => '現在頁面',
     'selected_rows'         => '選擇的行',

+ 1 - 1
resources/lang/zh-TW/select2.php

@@ -6,6 +6,6 @@ return [
     'input_too_short'  => '請輸入至少:num個字符',
     'loading_more'     => '載入更多結果...',
     'maximum_selected' => '最多只能選擇:num個項目',
-    'no_results'       => '為者到結果',
+    'no_results'       => '未找到結果',
     'searching'        => '搜尋中...',
 ];

+ 5 - 3
resources/views/form/container.blade.php

@@ -10,9 +10,11 @@
     @else
         <div class="fields-group">
             @if($form->hasRows())
-                @foreach($form->rows() as $row)
-                    {!! $row->render() !!}
-                @endforeach
+                <div class="ml-2 mb-2">
+                    @foreach($form->rows() as $row)
+                        {!! $row->render() !!}
+                    @endforeach
+                </div>
             @else
                 @foreach($form->fields() as $field)
                     {!! $field->render() !!}

+ 1 - 1
resources/views/layouts/full-page.blade.php

@@ -37,7 +37,7 @@
 {!! admin_section(\AdminSection::BODY_INNER_BEFORE) !!}
 
 <div class="app-content content">
-    <div class="wrapper" id="{{ $pjaxContainerId }}">
+    <div class="wrapper">
         @yield('app')
     </div>
 </div>

+ 1 - 1
resources/views/tree/branch.blade.php

@@ -11,7 +11,7 @@
             @endif
 
             @if($useDelete)
-            <a href="javascript:void(0);" data-url="{{ $path }}/{{ $branch[$keyName] }}" data-action="delete"><i class="feather icon-trash"></i></a>
+            <a href="javascript:void(0);" data-message="ID - {{ $branch[$keyName] }}" data-url="{{ $path }}/{{ $branch[$keyName] }}" data-action="delete"><i class="feather icon-trash"></i></a>
             @endif
         </span>
     </div>

+ 1 - 1
src/Admin.php

@@ -35,7 +35,7 @@ class Admin
      *
      * @var string
      */
-    const VERSION = '1.3.0';
+    const VERSION = '1.3.2';
 
     /**
      * @var array

+ 10 - 6
src/Console/MinifyCommand.php

@@ -151,16 +151,20 @@ class MinifyCommand extends Command
         $mixFile = $this->getMixFile();
         $mixBakFile = $this->getMixBakFile();
 
-        $this->files->delete($mixFile);
-        $this->files->copy($mixBakFile, $mixFile);
-        $this->files->delete($mixBakFile);
+        if (is_file($mixBakFile)) {
+            $this->files->delete($mixFile);
+            $this->files->copy($mixBakFile, $mixFile);
+            $this->files->delete($mixBakFile);
+        }
 
         $colorFile = $this->getColorFile();
         $colorBakFile = $this->getColorBakFile();
 
-        $this->files->delete($colorFile);
-        $this->files->copy($colorBakFile, $colorFile);
-        $this->files->delete($colorBakFile);
+        if (is_file($colorBakFile)) {
+            $this->files->delete($colorFile);
+            $this->files->copy($colorBakFile, $colorFile);
+            $this->files->delete($colorBakFile);
+        }
     }
 
     /**

+ 36 - 32
src/Controllers/MenuController.php

@@ -76,6 +76,7 @@ class MenuController extends AdminController
 
         $tree->disableCreateButton();
         $tree->disableQuickCreateButton();
+        $tree->disableEditButton();
 
         $tree->branch(function ($branch) {
             $payload = "<i class='fa {$branch['icon']}'></i>&nbsp;<strong>{$branch['title']}</strong>";
@@ -104,48 +105,51 @@ class MenuController extends AdminController
     public function form()
     {
         $menuModel = config('admin.database.menu_model');
-        $permissionModel = config('admin.database.permissions_model');
-        $roleModel = config('admin.database.roles_model');
 
-        $form = new Form(new Menu());
+        $relations = $menuModel::withPermission() ? ['permissions', 'roles'] : 'roles';
 
-        $form->tools(function (Form\Tools $tools) {
-            $tools->disableView();
-        });
+        return Form::make(new Menu($relations), function (Form $form) use ($menuModel) {
+            $permissionModel = config('admin.database.permissions_model');
+            $roleModel = config('admin.database.roles_model');
 
-        $form->display('id', 'ID');
+            $form->tools(function (Form\Tools $tools) {
+                $tools->disableView();
+            });
 
-        $form->select('parent_id', trans('admin.parent_id'))->options(function () use ($menuModel) {
-            return $menuModel::selectOptions();
-        });
-        $form->text('title', trans('admin.title'))->required();
-        $form->icon('icon', trans('admin.icon'))->help($this->iconHelp());
-        $form->text('uri', trans('admin.uri'));
-        $form->multipleSelect('roles', trans('admin.roles'))
-            ->options(function () use ($roleModel) {
-                return $roleModel::all()->pluck('name', 'id');
-            })
-            ->customFormat(function ($v) {
-                return array_column($v, 'id');
+            $form->display('id', 'ID');
+
+            $form->select('parent_id', trans('admin.parent_id'))->options(function () use ($menuModel) {
+                return $menuModel::selectOptions();
+            })->saving(function ($v) {
+                return (int) $v;
             });
-        if ($menuModel::withPermission()) {
-            $form->tree('permissions', trans('admin.permission'))
-                ->nodes(function () use ($permissionModel) {
-                    return (new $permissionModel())->allNodes();
+            $form->text('title', trans('admin.title'))->required();
+            $form->icon('icon', trans('admin.icon'))->help($this->iconHelp());
+            $form->text('uri', trans('admin.uri'));
+            $form->multipleSelect('roles', trans('admin.roles'))
+                ->options(function () use ($roleModel) {
+                    return $roleModel::all()->pluck('name', 'id');
                 })
                 ->customFormat(function ($v) {
-                    if (! $v) {
-                        return [];
-                    }
-
                     return array_column($v, 'id');
                 });
-        }
-
-        $form->display('created_at', trans('admin.created_at'));
-        $form->display('updated_at', trans('admin.updated_at'));
+            if ($menuModel::withPermission()) {
+                $form->tree('permissions', trans('admin.permission'))
+                    ->nodes(function () use ($permissionModel) {
+                        return (new $permissionModel())->allNodes();
+                    })
+                    ->customFormat(function ($v) {
+                        if (! $v) {
+                            return [];
+                        }
+
+                        return array_column($v, 'id');
+                    });
+            }
 
-        return $form;
+            $form->display('created_at', trans('admin.created_at'));
+            $form->display('updated_at', trans('admin.updated_at'));
+        });
     }
 
     /**

+ 1 - 45
src/Controllers/PermissionController.php

@@ -8,7 +8,6 @@ use Dcat\Admin\Grid;
 use Dcat\Admin\IFrameGrid;
 use Dcat\Admin\Layout\Content;
 use Dcat\Admin\Models\Repositories\Permission;
-use Dcat\Admin\Show;
 use Dcat\Admin\Tree;
 use Illuminate\Support\Str;
 
@@ -69,6 +68,7 @@ class PermissionController extends AdminController
         $tree = new Tree(new $model());
 
         $tree->disableCreateButton();
+        $tree->disableEditButton();
 
         $tree->branch(function ($branch) {
             $payload = "<div class='pull-left' style='min-width:310px'><b>{$branch['name']}</b>&nbsp;&nbsp;[<span class='text-primary'>{$branch['slug']}</span>]";
@@ -116,50 +116,6 @@ class PermissionController extends AdminController
         return $tree;
     }
 
-    /**
-     * Make a show builder.
-     *
-     * @param mixed $id
-     *
-     * @return Show
-     */
-    protected function detail($id)
-    {
-        $show = new Show($id, new Permission());
-
-        $show->id;
-        $show->slug;
-        $show->name;
-
-        $show->http_path->unescape()->as(function ($path) {
-            return collect($path)->filter()->map(function ($path) {
-                $method = $this->http_method ?: ['ANY'];
-
-                if (Str::contains($path, ':')) {
-                    [$method, $path] = explode(':', $path);
-                    $method = explode(',', $method);
-                }
-
-                $method = collect($method)->map(function ($name) {
-                    return strtoupper($name);
-                })->map(function ($name) {
-                    return "<span class='label bg-primary'>{$name}</span>";
-                })->implode('&nbsp;');
-
-                if (! empty(config('admin.route.prefix'))) {
-                    $path = '/'.trim(config('admin.route.prefix'), '/').$path;
-                }
-
-                return "<div style='margin-bottom: 5px;'>$method<code>$path</code></div>";
-            })->implode('');
-        });
-
-        $show->created_at;
-        $show->updated_at;
-
-        return $show;
-    }
-
     /**
      * Make a form builder.
      *

+ 31 - 0
src/Form.php

@@ -10,6 +10,7 @@ use Dcat\Admin\Form\Builder;
 use Dcat\Admin\Form\Concerns;
 use Dcat\Admin\Form\Condition;
 use Dcat\Admin\Form\Field;
+use Dcat\Admin\Form\NestedForm;
 use Dcat\Admin\Form\Row;
 use Dcat\Admin\Form\Tab;
 use Dcat\Admin\Traits\HasBuilderEvents;
@@ -802,6 +803,8 @@ class Form implements Renderable
 
         $this->inputs = $this->handleFileDelete($this->inputs);
 
+        $this->inputs = $this->handleHasManyValues($this->inputs);
+
         if ($response = $this->handleOrderable($this->inputs)) {
             return $response;
         }
@@ -818,6 +821,34 @@ class Form implements Renderable
         }
     }
 
+    /**
+     * @param array $inputs
+     *
+     * @return array
+     */
+    protected function handleHasManyValues(array $inputs)
+    {
+        foreach ($inputs as $column => &$input) {
+            $field = $this->builder()->field($column);
+
+            if (is_array($input) && $field instanceof Field\HasMany) {
+                $keyName = $field->getKeyName();
+
+                foreach ($input as $k => &$v) {
+                    if (empty($v[$keyName])) {
+                        $v[$keyName] = $k;
+                    }
+
+                    if (empty($v[NestedForm::REMOVE_FLAG_NAME])) {
+                        $v[NestedForm::REMOVE_FLAG_NAME] = null;
+                    }
+                }
+            }
+        }
+
+        return $inputs;
+    }
+
     /**
      * @param $key
      * @param $redirectTo

+ 59 - 21
src/Form/Concerns/HasFiles.php

@@ -5,6 +5,7 @@ namespace Dcat\Admin\Form\Concerns;
 use Dcat\Admin\Contracts\UploadField as UploadFieldInterface;
 use Dcat\Admin\Form\Builder;
 use Dcat\Admin\Form\Field;
+use Dcat\Admin\Form\NestedForm;
 use Dcat\Admin\Support\WebUploader;
 use Symfony\Component\HttpFoundation\File\UploadedFile;
 use Symfony\Component\HttpFoundation\Response;
@@ -15,6 +16,8 @@ use Symfony\Component\HttpFoundation\Response;
 trait HasFiles
 {
     /**
+     * 文件上传操作.
+     *
      * @param array $data
      *
      * @return Response|void
@@ -28,7 +31,16 @@ trait HasFiles
             return;
         }
 
-        $field = $this->findFieldByName($column);
+        $relation = $data['_relation'] ?? null;
+
+        if (empty($relation)) {
+            $field = $this->findFieldByName($column);
+        } else {
+            // hasMany表单文件上传
+            $relation = explode(',', $relation)[0];
+
+            $field = $this->getFieldByRelationName($relation, $column);
+        }
 
         if ($field && $field instanceof UploadFieldInterface) {
             if (($results = $this->callUploading($field, $file)) && $results instanceof Response) {
@@ -58,24 +70,12 @@ trait HasFiles
             return $field;
         }
 
-        if (mb_strpos($column, '.')) {
-            [$relation, $column] = explode('.', $column);
-
-            $relation = $this->findFieldByName($relation);
-
-            if ($relation instanceof Field\HasMany) {
-                return $relation->buildNestedForm()->fields()->first(function ($field) use ($column) {
-                    return $field->column() === $column;
-                });
-            }
-
-            return null;
-        }
-
         return $this->builder->field($column) ?: $this->builder->stepField($column);
     }
 
     /**
+     * 新增之前删除文件操作.
+     *
      * @param array $data
      *
      * @return \Illuminate\Http\JsonResponse|void
@@ -88,12 +88,17 @@ trait HasFiles
 
         $column = $data['_column'] ?? null;
         $file = $data['key'] ?? null;
+        $relation = $data['_relation'] ?? null;
 
         if (! $column && ! $file) {
             return;
         }
 
-        $field = $this->builder->field($column) ?: $this->builder->stepField($column);
+        if (empty($relation)) {
+            $field = $this->builder->field($column) ?: $this->builder->stepField($column);
+        } else {
+            $field = $this->getFieldByRelationName($relation[0], $column);
+        }
 
         if ($field && $field instanceof UploadFieldInterface) {
             $field->deleteFile($file);
@@ -102,6 +107,25 @@ trait HasFiles
         }
     }
 
+    /**
+     * 获取hasMany的子表单字段.
+     *
+     * @param string $relation
+     * @param string $column
+     *
+     * @return mixed
+     */
+    protected function getFieldByRelationName($relation, $column)
+    {
+        $relation = $this->findFieldByName($relation);
+
+        if ($relation && $relation instanceof Field\HasMany) {
+            return $relation->buildNestedForm()->fields()->first(function ($field) use ($column) {
+                return $field->column() === $column;
+            });
+        }
+    }
+
     /**
      * @param array $input
      *
@@ -122,7 +146,7 @@ trait HasFiles
     }
 
     /**
-     * Remove files in record.
+     * 根据传入数据删除文件.
      *
      * @param array $data
      * @param bool  $forceDelete
@@ -145,6 +169,8 @@ trait HasFiles
     }
 
     /**
+     * 编辑页面删除上传文件操作.
+     *
      * @param array $input
      *
      * @return array
@@ -156,14 +182,26 @@ trait HasFiles
         }
 
         $input[Field::FILE_DELETE_FLAG] = $input['key'];
-        unset($input['key']);
 
         if (! empty($input['_column'])) {
-            $input[$input['_column']] = '';
-
-            unset($input['_column']);
+            if (empty($input['_relation'])) {
+                $input[$input['_column']] = '';
+            } else {
+                [$relation, $relationKey] = $input['_relation'];
+                $keyName = $this->builder()->field($relation)->getKeyName();
+
+                $input[$relation] = [
+                    $relationKey => [
+                        $keyName                     => $relationKey,
+                        $input['_column']            => '',
+                        NestedForm::REMOVE_FLAG_NAME => null,
+                    ],
+                ];
+            }
         }
 
+        unset($input['key'], $input['_column'], $input['_relation']);
+
         $this->request->replace($input);
 
         return $input;

+ 0 - 8
src/Form/Field/Currency.php

@@ -50,14 +50,6 @@ class Currency extends Text
         return $this->options(compact('digits'));
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    protected function prepareInputValue($value)
-    {
-        return (float) $value;
-    }
-
     /**
      * {@inheritdoc}
      */

+ 25 - 10
src/Form/Field/File.php

@@ -4,6 +4,7 @@ namespace Dcat\Admin\Form\Field;
 
 use Dcat\Admin\Contracts\UploadField as UploadFieldInterface;
 use Dcat\Admin\Form\Field;
+use Dcat\Admin\Form\NestedForm;
 use Dcat\Admin\Support\Helper;
 use Dcat\Admin\Support\JavaScript;
 use Illuminate\Support\Arr;
@@ -112,11 +113,13 @@ class File extends Field implements UploadFieldInterface
      *
      * @return $this
      */
-    public function setRelation(?string $name)
+    public function setRelation(?string $name, $key)
     {
         $this->relationName = $name;
+        $this->options['formData']['_relation'] = [$name, $key];
 
-        $this->options['formData']['upload_column'] = $name.'.'.$this->column();
+        $this->containerId .= NestedForm::DEFAULT_KEY_NAME;
+        $this->id .= NestedForm::DEFAULT_KEY_NAME;
 
         return $this;
     }
@@ -182,7 +185,7 @@ class File extends Field implements UploadFieldInterface
 
         $this->forceOptions();
         $this->formatValue();
-        $this->setupScript();
+        $this->setUpScript();
 
         $this->addVariables([
             'fileType'    => $this->options['isImage'] ? '' : 'file',
@@ -192,31 +195,43 @@ class File extends Field implements UploadFieldInterface
         return parent::render();
     }
 
-    protected function setupScript()
+    protected function setUpScript()
     {
         $newButton = trans('admin.uploader.add_new_media');
         $options = JavaScript::format($this->options);
+        $hasManyKey = NestedForm::DEFAULT_KEY_NAME;
 
         $this->script = <<<JS
 (function () {
-    var uploader, newPage, options = {$options};
+    var uploader, 
+    newPage, 
+    cID = '#{$this->containerId}',
+    ID = '#{$this->id}',
+    hasManyKey = '{$hasManyKey}',
+    options = {$options};
+
+    if (typeof nestedIndex !== "undefined") {
+        cID = cID.replace(hasManyKey, nestedIndex);
+        ID = ID.replace(hasManyKey, nestedIndex);
+    }
 
     build();
 
     function build() {
         var opts = $.extend({
-            selector: '#{$this->containerId}',
-            addFileButton: '#{$this->containerId} .add-file-button',
+            selector: cID,
+            addFileButton: cID+' .add-file-button',
+            inputSelector: ID,
         }, options);
 
         opts.upload = $.extend({
             pick: {
-                id: '#{$this->containerId} .file-picker',
+                id: cID+' .file-picker',
                 name: '_file_',
                 label: '<i class="feather icon-folder"></i>&nbsp; {$newButton}'
             },
-            dnd: '#{$this->containerId} .dnd-area',
-            paste: '#{$this->containerId} .web-uploader'
+            dnd: cID+' .dnd-area',
+            paste: cID+' .web-uploader'
         }, opts);
 
         uploader = Dcat.Uploader(opts);

+ 11 - 11
src/Form/Field/HasMany.php

@@ -340,7 +340,7 @@ class HasMany extends Field
      *
      * @return string
      */
-    protected function getKeyName()
+    public function getKeyName()
     {
         if (is_null($this->form)) {
             return;
@@ -475,14 +475,14 @@ class HasMany extends Field
          */
         $script = <<<JS
 (function () {
-    var index = 0;
+    var nestedIndex = 0;
 $('#has-many-{$this->column}').on('click', '.add', function () {
 
     var tpl = $('template.{$this->column}-tpl');
 
-    index++;
+    nestedIndex++;
 
-    var template = tpl.html().replace(/{$defaultKey}/g, index);
+    var template = tpl.html().replace(/{$defaultKey}/g, nestedIndex);
     $('.has-many-{$this->column}-forms').append(template);
     {$templateScript}
 });
@@ -527,11 +527,11 @@ JS;
     }
 });
 
-var index = 0;
+var nestedIndex = 0;
 $('#has-many-{$this->column} > .header').off('click', '.add').on('click', '.add', function(){
-    index++;
-    var navTabHtml = $('#has-many-{$this->column} > template.nav-tab-tpl').html().replace(/{$defaultKey}/g, index);
-    var paneHtml = $('#has-many-{$this->column} > template.pane-tpl').html().replace(/{$defaultKey}/g, index);
+    nestedIndex++;
+    var navTabHtml = $('#has-many-{$this->column} > template.nav-tab-tpl').html().replace(/{$defaultKey}/g, nestedIndex);
+    var paneHtml = $('#has-many-{$this->column} > template.pane-tpl').html().replace(/{$defaultKey}/g, nestedIndex);
     $('#has-many-{$this->column} > .nav').append(navTabHtml);
     $('#has-many-{$this->column} > .tab-content').append(paneHtml);
     $('#has-many-{$this->column} > .nav > li:last-child a').click();
@@ -574,13 +574,13 @@ JS;
          */
         $script = <<<JS
 (function () {
-    var index = 0;
+    var nestedIndex = 0;
     $('#has-many-{$this->column}').on('click', '.add', function () {
         var tpl = $('template.{$this->column}-tpl');
     
-        index++;
+        nestedIndex++;
     
-        var template = tpl.html().replace(/{$defaultKey}/g, index);
+        var template = tpl.html().replace(/{$defaultKey}/g, nestedIndex);
         $('.has-many-{$this->column}-forms').append(template);
         {$templateScript}
     });

+ 10 - 4
src/Form/Field/Select.php

@@ -142,14 +142,20 @@ JS;
      */
     public function loads($fields = [], $sourceUrls = [], string $idField = 'id', string $textField = 'text')
     {
-        $fieldsStr = implode('.', (array) $fields);
+        $fieldsStr = implode('^', array_map(function ($field) {
+            if (Str::contains($field, '.')) {
+                return str_replace('.', '_', $field).'_';
+            }
+
+            return $field;
+        }, (array) $fields));
         $urlsStr = implode('^', array_map(function ($url) {
             return admin_url($url);
         }, (array) $sourceUrls));
 
         $script = <<<JS
 (function () {
-    var fields = '$fieldsStr'.split('.');
+    var fields = '$fieldsStr'.split('^');
     var urls = '$urlsStr'.split('^');
     
     var refreshOptions = function(url, target) {
@@ -161,7 +167,7 @@ JS;
                     d.text = d.$textField;
                     return d;
                 })
-            }).trigger('change');
+            }).val(target.data('value')).trigger('change');
         });
     };
     
@@ -172,7 +178,7 @@ JS;
 
         fields.forEach(function(field, index){
             var target = $(_this).closest('.fields-group').find('.' + fields[index]);
-            
+
             if (_this.value !== '0' && ! _this.value) {
                 return;
             }

+ 8 - 2
src/Form/NestedForm.php

@@ -256,7 +256,7 @@ class NestedForm
 
             $value = $this->fetchColumnValue($record, $columns);
 
-            if (is_null($value)) {
+            if ($value === false) {
                 continue;
             }
 
@@ -291,6 +291,10 @@ class NestedForm
     protected function fetchColumnValue($data, $columns)
     {
         if (is_string($columns)) {
+            if (! Arr::has($data, $columns)) {
+                return false;
+            }
+
             return Arr::get($data, $columns);
         }
 
@@ -305,6 +309,8 @@ class NestedForm
 
             return $value;
         }
+
+        return false;
     }
 
     /**
@@ -322,7 +328,7 @@ class NestedForm
         }
 
         if ($field instanceof UploadField) {
-            $field->setRelation($this->relationName);
+            $field->setRelation($this->relationName, $this->key);
         }
 
         $field::collectAssets();

+ 54 - 0
src/Form/Row.php

@@ -5,6 +5,60 @@ namespace Dcat\Admin\Form;
 use Dcat\Admin\Form;
 use Illuminate\Contracts\Support\Renderable;
 
+/**
+ * Class Row.
+ *
+ * @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()
+ * @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, $labelOrCallback, $callback = null)
+ * @method Field\Tree                   tree($column, $label = '')
+ * @method Field\Table                  table($column, $labelOrCallback, $callback = null)
+ * @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 = '')
+ */
 class Row implements Renderable
 {
     /**

+ 1 - 1
src/Grid/Displayers/Button.php

@@ -4,7 +4,7 @@ namespace Dcat\Admin\Grid\Displayers;
 
 class Button extends AbstractDisplayer
 {
-    public function display($style)
+    public function display($style = 'primary')
     {
         $style = collect((array) $style)->map(function ($style) {
             return 'btn-'.$style;

+ 1 - 3
src/Grid/Displayers/QRCode.php

@@ -27,9 +27,7 @@ JS;
         $content = $this->column->getOriginal();
 
         if ($formatter instanceof \Closure) {
-            $formatter->bindTo($this->row);
-
-            $content = call_user_func($formatter, $content);
+            $content = $formatter->call($this->row, $content);
         }
 
         $img = "<img src='https://api.qrserver.com/v1/create-qr-code/?size={$width}x{$height}&data={$content}' style='height: {$width}px;width: {$height}px;'/>";

+ 0 - 1
src/Grid/Exporters/AbstractExporter.php

@@ -189,7 +189,6 @@ abstract class AbstractExporter implements ExporterInterface
         $array = $this->grid->processFilter(true);
 
         $model->reset();
-        $model->rejectQuery('forPage');
 
         return $this->callBuilder($array);
     }

+ 10 - 1
src/Grid/Model.php

@@ -140,7 +140,15 @@ class Model
         }
 
         $this->request = $request;
-        $this->queries = collect();
+        $this->initQueries();
+    }
+
+    /**
+     * @return void
+     */
+    protected function initQueries()
+    {
+        $this->queries = new Collection();
     }
 
     /**
@@ -720,5 +728,6 @@ class Model
     {
         $this->data = null;
         $this->model = null;
+        $this->initQueries();
     }
 }

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

@@ -102,7 +102,7 @@ JS;
         $export = trans('admin.export');
 
         return <<<EOT
-<div class="btn-group dropdown shadow-none" style="margin-right:3px">
+<div class="btn-group dropdown" style="margin-right:3px">
     <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
         <i class="feather icon-download"></i>
         <span class="d-none d-sm-inline">&nbsp;{$export}&nbsp;</span>

+ 2 - 2
src/Models/Repositories/Menu.php

@@ -6,10 +6,10 @@ use Dcat\Admin\Repositories\EloquentRepository;
 
 class Menu extends EloquentRepository
 {
-    public function __construct()
+    public function __construct($modelOrRelations = [])
     {
         $this->eloquentClass = config('admin.database.menu_model');
 
-        parent::__construct();
+        parent::__construct($modelOrRelations);
     }
 }

+ 1 - 1
src/Repositories/EloquentRepository.php

@@ -720,7 +720,7 @@ class EloquentRepository extends Repository implements TreeRepository
                 || $relation instanceof Relations\MorphOne
                 || $relation instanceof Relations\BelongsTo;
 
-            $prepared = $form->prepareUpdate([$name => $values], $oneToOneRelation);
+            $prepared = $oneToOneRelation ? $form->prepareUpdate([$name => $values]) : [$name => $values];
 
             if (empty($prepared)) {
                 continue;

Некоторые файлы не были показаны из-за большого количества измененных файлов