Kaynağa Gözat

:construction: 接口响应JSON数据方法调整

jqh 5 yıl önce
ebeveyn
işleme
aab94f385e

+ 1 - 38
resources/assets/dcat/extra/action.js

@@ -66,44 +66,7 @@
                     return;
                 }
 
-                if (typeof response !== 'object') {
-                    return Dcat.error({type: 'error', title: 'Oops!'});
-                }
-
-                var then = function (then) {
-                    switch (then.action) {
-                        case 'refresh':
-                            Dcat.reload();
-                            break;
-                        case 'download':
-                            window.open(then.value, '_blank');
-                            break;
-                        case 'redirect':
-                            Dcat.reload(then.value);
-                            break;
-                        case 'location':
-                            window.location = then.value;
-                            break;
-                        case 'script':
-                            (function () {
-                                eval(then.value);
-                            })();
-                            break;
-                    }
-                };
-
-                if (typeof response.html === 'string' && response.html) {
-                    // 处理api返回的HTML代码
-                    options.html(target, response.html, response);
-                }
-
-                if (typeof response.data.message === 'string' && response.data.type) {
-                    Dcat[response.data.type](response.data.message);
-                }
-
-                if (response.data.then) {
-                    then(response.data.then);
-                }
+                Dcat.handleJsonResponse(response, {html: options.html, target: target});
             };
         }
 

+ 10 - 14
resources/assets/dcat/js/bootstrappers/DataActions.js

@@ -22,14 +22,12 @@ let defaultActions = {
                 Dcat.NP.start();
                 $.delete({
                     url: url,
-                    success: function (data) {
+                    success: function (response) {
                         Dcat.NP.done();
-                        if (data.status) {
-                            Dcat.reload(redirect);
-                            Dcat.swal.success(data.message, msg);
-                        } else {
-                            Dcat.swal.error(data.message, msg);
-                        }
+
+                        response.data.detail = msg;
+
+                        Dcat.handleJsonResponse(response);
                     }
                 });
             });
@@ -46,18 +44,16 @@ let defaultActions = {
             if (! keys.length) {
                 return;
             }
-            Dcat.confirm(lang.delete_confirm, keys.join(', '), function () {
+            let msg = 'ID - ' + keys.join(', ');
+
+            Dcat.confirm(lang.delete_confirm, msg, function () {
                 Dcat.NP.start();
                 $.delete({
                     url: url + '/' + keys.join(','),
                     success: function (data) {
                         Dcat.NP.done();
-                        if (data.status) {
-                            Dcat.reload();
-                            Dcat.swal.success(data.message, keys.join(', '));
-                        } else {
-                            Dcat.swal.error(data.message, keys.join(', '));
-                        }
+
+                        Dcat.handleJsonResponse(data);
                     }
                 });
             });

+ 71 - 3
resources/assets/dcat/js/extensions/Ajax.js

@@ -3,7 +3,8 @@ export default class Ajax {
     constructor(Dcat) {
         this.dcat = Dcat;
 
-        Dcat.handleAjaxError = this.handleAjaxError.bind(this)
+        Dcat.handleAjaxError = this.handleAjaxError.bind(this);
+        Dcat.handleJsonResponse = this.handleJsonResponse.bind(this);
 
         this.init(Dcat)
     }
@@ -25,13 +26,14 @@ export default class Ajax {
     }
 
     handleAjaxError(xhr, text, msg) {
-        let Dcat = this.dcat;
+        let Dcat = this.dcat,
+            json = xhr.responseJSON || {},
+            _msg = json.message;
 
         Dcat.NP.done();
         Dcat.loading(false);// 关闭所有loading效果
         $('.btn-loading').buttonLoading(false);
 
-        var json = xhr.responseJSON || {}, _msg = json.message;
         switch (xhr.status) {
             case 500:
                 return Dcat.error(_msg || (Dcat.lang['500'] || 'Server internal error.'));
@@ -60,4 +62,70 @@ export default class Ajax {
 
         Dcat.error(_msg || (xhr.status + ' ' + msg));
     }
+
+    // 处理接口返回数据
+    handleJsonResponse(response, options) {
+        let Dcat = this.dcat,
+            data = response.data;
+
+        if (typeof response !== 'object') {
+            return Dcat.error({type: 'error', title: 'Oops!'});
+        }
+
+        var then = function (then) {
+            switch (then.action) {
+                case 'refresh':
+                    Dcat.reload();
+                    break;
+                case 'download':
+                    window.open(then.value, '_blank');
+                    break;
+                case 'redirect':
+                    Dcat.reload(then.value || null);
+                    break;
+                case 'location':
+                    setTimeout(function () {
+                        if (then.value) {
+                            window.location = then.value;
+                        } else {
+                            window.location.reload();
+                        }
+                    }, 1000);
+                    break;
+                case 'script':
+                    (function () {
+                        eval(then.value);
+                    })();
+                    break;
+            }
+        };
+
+        if (typeof response.html === 'string' && response.html && options.target) {
+            if (typeof options.html === 'function') {
+                // 处理api返回的HTML代码
+                options.html(options.target, response.html, response);
+            } else {
+                $(target).html(response.html);
+            }
+        }
+
+        let message = data.message || response.message;
+
+        // 判断默认弹窗类型.
+        if (! data.type) {
+            data.type = response.status ? 'success' : 'error';
+        }
+
+        if (typeof message === 'string' && data.type && message) {
+            if (data.alert) {
+                Dcat.swal[data.type](message, data.detail);
+            } else {
+                Dcat[data.type](message);
+            }
+        }
+
+        if (data.then) {
+            then(data.then);
+        }
+    }
 }

+ 1 - 0
resources/assets/dcat/js/extensions/DialogForm.js

@@ -214,6 +214,7 @@ export default class DialogForm {
         Dcat.Form({
             form: _this.$form,
             redirect: false,
+            confirm: Dcat.FormConfirm,
             before: function () {
                 // 验证表单
                 _this.$form.validator('validate');

+ 7 - 30
resources/assets/dcat/js/extensions/Form.js

@@ -28,8 +28,6 @@ class Form {
             errorTemplate: '<label class="control-label" for="inputError"><i class="feather icon-x-circle"></i> {message}</label><br/>',
             // 是否允许跳转
             redirect: true,
-            // 保存成功后自动跳转
-            autoRedirect: false,
             // 自动移除表单错误信息
             autoRemoveError: true,
             // 表单提交之前事件监听,返回false可以中止表单继续提交
@@ -63,8 +61,7 @@ class Form {
     }
 
     submit() {
-        let Dcat = window.Dcat,
-            _this = this,
+        let _this = this,
             $form = _this.$form,
             options = _this.options,
             $submitButton = $form.find('[type="submit"],.submit');
@@ -111,35 +108,15 @@ class Form {
                     return;
                 }
 
-
-                if (! response.status) {
-                    Dcat.error(response.message || 'Save failed!');
-                    return;
-                }
-
-                Dcat.success(response.message || 'Save succeeded!');
-
-                if (typeof response.location !== "undefined") {
-                    return setTimeout(function () {
-                        if (response.location) {
-                            location.href = response.location;
-                        } else {
-                            location.reload();
-                        }
-                    }, 1500)
-                }
-
                 if (response.redirect === false || ! options.redirect) {
-                    return;
-                }
-
-                if (response.redirect) {
-                    return Dcat.reload(response.redirect);
+                    if (response.data && response.data.then) {
+                        delete response.data['then']['redirect'];
+                        delete response.data['then']['location'];
+                        delete response.data['then']['refresh'];
+                    }
                 }
 
-                if (options.autoRedirect) {
-                    history.back(-1);
-                }
+                Dcat.handleJsonResponse(response);
             },
             error: function (response) {
                 $submitButton.buttonLoading(false);

+ 2 - 206
src/Actions/Response.php

@@ -2,212 +2,8 @@
 
 namespace Dcat\Admin\Actions;
 
-use Dcat\Admin\Support\Helper;
-use Illuminate\Validation\ValidationException;
+use Dcat\Admin\Http\JsonResponse;
 
-class Response
+class Response extends JsonResponse
 {
-    /**
-     * @var bool
-     */
-    public $status = true;
-
-    /**
-     * @var \Exception
-     */
-    public $exception;
-
-    /**
-     * @var
-     */
-    protected $plugin;
-
-    /**
-     * @var array
-     */
-    protected $data = [];
-
-    /**
-     * @var string
-     */
-    protected $html = '';
-
-    /**
-     * @param string $message
-     *
-     * @return $this
-     */
-    public function success(string $message = '')
-    {
-        return $this->show('success', $message);
-    }
-
-    /**
-     * @param string $message
-     *
-     * @return $this
-     */
-    public function info(string $message = '')
-    {
-        return $this->show('info', $message);
-    }
-
-    /**
-     * @param string $message
-     *
-     * @return $this
-     */
-    public function warning(string $message = '')
-    {
-        return $this->show('warning', $message);
-    }
-
-    /**
-     * @param string $message
-     *
-     * @return $this
-     */
-    public function error(string $message = '')
-    {
-        return $this->show('error', $message);
-    }
-
-    /**
-     * @param string $type
-     * @param string $title
-     *
-     * @return $this
-     */
-    protected function show($type, $title = '')
-    {
-        return $this->data(['message' => $title, 'type' => $type]);
-    }
-
-    /**
-     * Send a redirect response.
-     *
-     * @param string $url
-     *
-     * @return $this
-     */
-    public function redirect(string $url)
-    {
-        return $this->then(['action' => 'redirect', 'value' => admin_url($url)]);
-    }
-
-    /**
-     * Send a location redirect response.
-     *
-     * @param string $location
-     *
-     * @return $this
-     */
-    public function location(string $location)
-    {
-        return $this->then(['action' => 'location', 'value' => admin_url($location)]);
-    }
-
-    /**
-     * Send a download response.
-     *
-     * @param string $url
-     *
-     * @return $this
-     */
-    public function download($url)
-    {
-        return $this->then(['action' => 'download', 'value' => admin_url($url)]);
-    }
-
-    /**
-     * Send a refresh response.
-     *
-     * @return $this
-     */
-    public function refresh()
-    {
-        return $this->then(['action' => 'refresh', 'value' => true]);
-    }
-
-    /**
-     * @param string $script
-     *
-     * @return $this
-     */
-    public function script($script)
-    {
-        return $this->then(['action' => 'script', 'value' => $script]);
-    }
-
-    /**
-     * @param array $value
-     *
-     * @return $this
-     */
-    protected function then(array $value)
-    {
-        $this->data['then'] = $value;
-
-        return $this;
-    }
-
-    /**
-     * @param array $value
-     *
-     * @return $this
-     */
-    public function data(array $value)
-    {
-        $this->data = array_merge($this->data, $value);
-
-        return $this;
-    }
-
-    /**
-     * Send a html response.
-     *
-     * @param string $html
-     *
-     * @return $this
-     */
-    public function html($html = '')
-    {
-        $this->html = $html;
-
-        return $this;
-    }
-
-    /**
-     * @param \Throwable $exception
-     *
-     * @return $this
-     */
-    public static function withException(\Throwable $exception)
-    {
-        $response = new static();
-
-        $response->status = false;
-
-        if ($exception instanceof ValidationException) {
-            $message = collect($exception->errors())->flatten()->implode("\n");
-        } else {
-            $message = $exception->getMessage();
-        }
-
-        return $response->error($message);
-    }
-
-    /**
-     * @return \Illuminate\Http\JsonResponse
-     */
-    public function send()
-    {
-        $data = ['status' => $this->status, 'data' => $this->data];
-
-        if ($this->html) {
-            $data['html'] = Helper::render($this->html);
-        }
-
-        return response()->json($data);
-    }
 }

+ 3 - 0
src/Application.php

@@ -4,9 +4,12 @@ namespace Dcat\Admin;
 
 use Illuminate\Contracts\Container\Container;
 use Illuminate\Support\Facades\Route;
+use Illuminate\Support\Traits\Macroable;
 
 class Application
 {
+    use Macroable;
+
     const DEFAULT = 'admin';
 
     /**

+ 15 - 1
src/Extend/ServiceProvider.php

@@ -91,9 +91,11 @@ abstract class ServiceProvider extends LaravelServiceProvider
         }
 
         if ($routes = $this->getRoutes()) {
-            return $this->registerRoutes($routes);
+            $this->registerRoutes($routes);
         }
 
+        $this->publishAssets();
+
         $this->registerAssets();
     }
 
@@ -180,6 +182,18 @@ abstract class ServiceProvider extends LaravelServiceProvider
     {
     }
 
+    /**
+     * 发布静态资源.
+     */
+    protected function publishAssets()
+    {
+        if ($assets = $this->getAssetPath()) {
+            $this->publishes([
+                $assets => public_path(Admin::asset()->getRealPath('@extension'))
+            ], $this->getName());
+        }
+    }
+
     /**
      * 注册路由.
      *

+ 7 - 5
src/Extend/UpdateManager.php

@@ -43,10 +43,8 @@ class UpdateManager
 
     public function rollback($name, ?string $stopOnVersion = null)
     {
-        /*
-         * Remove the plugin database and version
-         */
-        if (! ($extension = $this->manager->get($name))
+        if (
+            ! ($extension = $this->manager->get($name))
             && $this->versionManager->purge($name)
         ) {
             $this->note('<info>Purged from database:</info> ' . $name);
@@ -54,7 +52,7 @@ class UpdateManager
             return $this;
         }
 
-        if ($stopOnVersion && !$this->versionManager->hasDatabaseVersion($extension, $stopOnVersion)) {
+        if ($stopOnVersion && ! $this->versionManager->hasDatabaseVersion($extension, $stopOnVersion)) {
             throw new AdminException('Extension version not found');
         }
 
@@ -70,6 +68,10 @@ class UpdateManager
 
         $this->note('<error>Unable to find:</error> '.$name);
 
+        if ($stopOnVersion === null) {
+            // 移除静态资源
+        }
+
         return $this;
     }
 

+ 3 - 3
src/Extend/VersionManager.php

@@ -100,9 +100,9 @@ class VersionManager
 
     public function remove($extension, $stopOnVersion = null, $stopCurrentVersion = false)
     {
-        $name = is_string($extension) ? $extension : $this->manager->getIdentifier($extension);
+        $name = $this->manager->getName($extension);
 
-        if (!$this->hasVersionFile($name)) {
+        if (! $this->hasVersionFile($name)) {
             return false;
         }
 
@@ -296,7 +296,7 @@ class VersionManager
 
     protected function applyDatabaseScript($name, $version, $script)
     {
-        $updateFile = $this->manager->path($name, '/updates/'.$script);
+        $updateFile = $this->manager->path($name, 'updates/'.$script);
 
         if (! is_file($updateFile)) {
             $this->note('- <error>v'.$version.':  Migration file "'.$script.'" not found</error>');

+ 63 - 36
src/Form.php

@@ -88,15 +88,15 @@ use Symfony\Component\HttpFoundation\Response;
  */
 class Form implements Renderable
 {
-    use HasBuilderEvents,
-        HasFormResponse,
-        Concerns\HasEvents,
-        Concerns\HasFiles,
-        Concerns\HasSteps,
-        Concerns\HandleCascadeFields,
-        Concerns\HasRows,
-        Concerns\HasTabs,
-        Macroable {
+    use HasBuilderEvents;
+    use HasFormResponse;
+    use Concerns\HasEvents;
+    use Concerns\HasFiles;
+    use Concerns\HasSteps;
+    use Concerns\HandleCascadeFields;
+    use Concerns\HasRows;
+    use Concerns\HasTabs;
+    use Macroable {
             __call as macroCall;
         }
 
@@ -547,10 +547,8 @@ class Form implements Renderable
                 return $response;
             }
 
-            $response = [
-                'status'  => $result ? true : false,
-                'message' => $result ? trans('admin.delete_succeeded') : trans('admin.delete_failed'),
-            ];
+            $status = $result ? true : false;
+            $message = $result ? trans('admin.delete_succeeded') : trans('admin.delete_failed');
         } catch (\Throwable $exception) {
             $response = $this->handleException($exception);
 
@@ -558,13 +556,17 @@ class Form implements Renderable
                 return $response;
             }
 
-            $response = $response ?: [
-                'status'  => false,
-                'message' => $exception->getMessage() ?: trans('admin.delete_failed'),
-            ];
+            $status = false;
+            $message = $exception->getMessage() ?: trans('admin.delete_failed');
         }
 
-        return response()->json($response);
+        return $this->sendResponse(
+            $this->response()
+                ->alert()
+                ->status($status)
+                ->message($message)
+                ->refreshIf($status)
+        );
     }
 
     /**
@@ -595,7 +597,7 @@ class Form implements Renderable
             $data = $data ?: $this->request->all();
 
             if ($response = $this->beforeStore($data)) {
-                return $response;
+                return $this->sendResponse($response);
             }
 
             $this->updates = $this->prepareInsert($this->updates);
@@ -605,20 +607,24 @@ class Form implements Renderable
             $this->builder->setResourceId($id);
 
             if (($response = $this->callSaved($id))) {
-                return $response;
+                return $this->sendResponse($response);
             }
 
             if ($response = $this->responseMultipleStepsDonePage()) {
-                return $response;
+                return $this->sendResponse($response);
             }
 
             if (! $id) {
-                return $this->error(trans('admin.save_failed'));
+                return $this->sendResponse(
+                    $this->response()
+                        ->error(trans('admin.save_failed'))
+                );
             }
 
-            return $this->redirect(
-                $this->redirectUrl($id, $redirectTo),
-                trans('admin.save_succeeded')
+            return $this->sendResponse(
+                $this->response()
+                    ->redirect($this->getRedirectUrl($id, $redirectTo))
+                    ->success(trans('admin.save_succeeded'))
             );
         } catch (\Throwable $e) {
             $response = $this->handleException($e);
@@ -627,7 +633,11 @@ class Form implements Renderable
                 return $response;
             }
 
-            return $this->error($e->getMessage() ?: trans('admin.save_failed'));
+            return $this->sendResponse(
+                $this->response()
+                    ->error(trans('admin.save_failed'))
+                    ->withExceptionIf($e->getMessage(), $e)
+            );
         }
     }
 
@@ -748,9 +758,15 @@ class Form implements Renderable
                 ? $this->repository->moveOrderUp()
                 : $this->repository->moveOrderDown();
 
-            return $updated
-                ? $this->ajaxResponse(__('admin.update_succeeded'))
-                : $this->error(__('admin.nothing_updated'));
+            $message = $updated
+                ? __('admin.update_succeeded')
+                : __('admin.nothing_updated');
+
+            return $this->sendResponse(
+                $this->response()
+                    ->status((bool) $updated)
+                    ->message($message)
+            );
         }
     }
 
@@ -776,7 +792,7 @@ class Form implements Renderable
             $data = $data ?: $this->request->all();
 
             if ($response = $this->beforeUpdate($id, $data)) {
-                return $response;
+                return $this->sendResponse($response);
             }
 
             $this->updates = $this->prepareUpdate($this->updates);
@@ -784,14 +800,21 @@ class Form implements Renderable
             $updated = $this->repository->update($this);
 
             if (($response = $this->callSaved($updated))) {
-                return $response;
+                return $this->sendResponse($response);
             }
 
             if (! $updated) {
-                return $this->error(trans('admin.update_succeeded'));
+                return $this->sendResponse(
+                    $this->response()
+                        ->error(trans('admin.update_failed'))
+                );
             }
 
-            return $this->redirect($this->redirectUrl($id, $redirectTo), trans('admin.update_succeeded'));
+            return $this->sendResponse(
+                $this->response()
+                    ->success(trans('admin.update_succeeded'))
+                    ->redirect($this->getRedirectUrl($id, $redirectTo))
+            );
         } catch (\Throwable $e) {
             $response = $this->handleException($e);
 
@@ -799,7 +822,11 @@ class Form implements Renderable
                 return $response;
             }
 
-            return $this->error($e->getMessage() ?: trans('admin.save_failed'));
+            return $this->sendResponse(
+                $this->response()
+                    ->error(trans('admin.update_failed'))
+                    ->withExceptionIf($e->getMessage(), $e)
+            );
         }
     }
 
@@ -889,7 +916,7 @@ class Form implements Renderable
      *
      * @return string|null
      */
-    public function redirectUrl($key, $redirectTo = null)
+    public function getRedirectUrl($key, $redirectTo = null)
     {
         if ($redirectTo) {
             return $redirectTo;
@@ -917,7 +944,7 @@ class Form implements Renderable
             return rtrim($resourcesPath, '/')."/{$key}";
         }
 
-        return $this->request->get(Builder::PREVIOUS_URL_KEY) ?: $resourcesPath;
+        return $this->request->get(Builder::PREVIOUS_URL_KEY) ?: ($this->getCurrentUrl() ?: $resourcesPath);
     }
 
     /**

+ 1 - 1
src/Form/Builder.php

@@ -152,7 +152,7 @@ class Builder
     /**
      * @var array
      */
-    protected $confirm = [];
+    public $confirm = [];
 
     /**
      * Builder constructor.

+ 2 - 1
src/Form/Concerns/HasEvents.php

@@ -5,6 +5,7 @@ namespace Dcat\Admin\Form\Concerns;
 use Closure;
 use Dcat\Admin\Form\Events;
 use Dcat\Admin\Contracts\UploadField as UploadFieldInterface;
+use Dcat\Admin\Http\JsonResponse;
 use Illuminate\Support\Facades\Event;
 use Symfony\Component\HttpFoundation\File\UploadedFile;
 use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -146,7 +147,7 @@ trait HasEvents
 
             $ret = $callback($this, ...$event->payload);
 
-            if ($ret instanceof Response) {
+            if ($ret instanceof Response || $ret instanceof JsonResponse) {
                 $event->form->eventResponse = $ret;
 
                 return false;

+ 4 - 1
src/Form/Concerns/HasSteps.php

@@ -91,7 +91,10 @@ trait HasSteps
         // Stash input data.
         $this->multipleSteps()->stash($data);
 
-        return $this->ajaxResponse('Success');
+        return $this
+            ->response()
+            ->success('Success')
+            ->send();
     }
 
     /**

+ 1 - 1
src/Form/Field/Tree.php

@@ -118,7 +118,7 @@ class Tree extends Field
         $this->value = &$value;
 
         if ($this->nodes instanceof \Closure) {
-            $this->nodes = $this->nodes->call($this->values(), $value, $this);
+            $this->nodes = Helper::array($this->nodes->call($this->values(), $value, $this));
         }
 
         if (! $this->nodes) {

+ 13 - 13
src/Grid.php

@@ -19,19 +19,19 @@ use Illuminate\Support\Traits\Macroable;
 
 class Grid
 {
-    use HasBuilderEvents,
-        Concerns\HasNames,
-        Concerns\HasFilter,
-        Concerns\HasTools,
-        Concerns\HasActions,
-        Concerns\HasPaginator,
-        Concerns\HasExporter,
-        Concerns\HasComplexHeaders,
-        Concerns\HasSelector,
-        Concerns\HasQuickCreate,
-        Concerns\HasQuickSearch,
-        Concerns\CanFixColumns,
-        Macroable {
+    use HasBuilderEvents;
+    use Concerns\HasNames;
+    use Concerns\HasFilter;
+    use Concerns\HasTools;
+    use Concerns\HasActions;
+    use Concerns\HasPaginator;
+    use Concerns\HasExporter;
+    use Concerns\HasComplexHeaders;
+    use Concerns\HasSelector;
+    use Concerns\HasQuickCreate;
+    use Concerns\HasQuickSearch;
+    use Concerns\CanFixColumns;
+    use Macroable {
             __call as macroCall;
         }
 

+ 10 - 10
src/Http/Controllers/AuthController.php

@@ -36,7 +36,7 @@ class AuthController extends Controller
     public function getLogin(Content $content)
     {
         if ($this->guard()->check()) {
-            return redirect($this->redirectPath());
+            return redirect($this->getRedirectPath());
         }
 
         return $content->full()->body(view($this->view));
@@ -202,10 +202,10 @@ class AuthController extends Controller
             });
 
             $form->saved(function (Form $form) {
-                return $form->redirect(
-                    admin_url('auth/setting'),
-                    trans('admin.update_succeeded')
-                );
+                return $form
+                    ->response()
+                    ->success(trans('admin.update_succeeded'))
+                    ->redirect('auth/setting');
             });
         });
     }
@@ -225,7 +225,7 @@ class AuthController extends Controller
      *
      * @return string
      */
-    protected function redirectPath()
+    protected function getRedirectPath()
     {
         return $this->redirectTo ?: admin_url('/');
     }
@@ -241,10 +241,10 @@ class AuthController extends Controller
     {
         $request->session()->regenerate();
 
-        return $this->redirectToIntended(
-            $this->redirectPath(),
-            trans('admin.login_successful')
-        );
+        return $this->response()
+            ->success(trans('admin.login_successful'))
+            ->redirectToIntended($this->getRedirectPath())
+            ->send();
     }
 
     /**

+ 5 - 5
src/Http/Controllers/HandleActionController.php

@@ -4,7 +4,7 @@ namespace Dcat\Admin\Http\Controllers;
 
 use Dcat\Admin\Actions\Action;
 use Dcat\Admin\Actions\Response;
-use Exception;
+use Dcat\Admin\Exception\AdminException;
 use Illuminate\Http\Request;
 
 class HandleActionController
@@ -32,27 +32,27 @@ class HandleActionController
     /**
      * @param Request $request
      *
-     * @throws Exception
+     * @throws AdminException
      *
      * @return Action
      */
     protected function resolveActionInstance(Request $request): Action
     {
         if (! $request->has('_action')) {
-            throw new Exception('Invalid action request.');
+            throw new AdminException('Invalid action request.');
         }
 
         $actionClass = str_replace('_', '\\', $request->get('_action'));
 
         if (! class_exists($actionClass)) {
-            throw new Exception("Action [{$actionClass}] does not exist.");
+            throw new AdminException("Action [{$actionClass}] does not exist.");
         }
 
         /** @var Action $action */
         $action = app($actionClass);
 
         if (! method_exists($action, 'handle')) {
-            throw new Exception("Action method {$actionClass}::handle() does not exist.");
+            throw new AdminException("Action method {$actionClass}::handle() does not exist.");
         }
 
         return $action;

+ 16 - 6
src/Http/Controllers/HandleFormController.php

@@ -2,10 +2,11 @@
 
 namespace Dcat\Admin\Http\Controllers;
 
+use Dcat\Admin\Exception\AdminException;
 use Dcat\Admin\Form\Field\File;
+use Dcat\Admin\Http\JsonResponse;
 use Dcat\Admin\Traits\HasUploadedFile;
 use Dcat\Admin\Widgets\Form;
-use Exception;
 use Illuminate\Http\Request;
 
 class HandleFormController
@@ -28,7 +29,7 @@ class HandleFormController
 
         $input = $form->sanitize($request->all());
 
-        return $form->handle($input) ?: $form->success();
+        return $this->sendResponse($form->handle($input));
     }
 
     public function uploadFile(Request $request)
@@ -60,29 +61,38 @@ class HandleFormController
     /**
      * @param Request $request
      *
-     * @throws Exception
+     * @throws AdminException
      *
      * @return Form
      */
     protected function resolveForm(Request $request)
     {
         if (! $request->has(Form::REQUEST_NAME)) {
-            throw new Exception('Invalid form request.');
+            throw new AdminException('Invalid form request.');
         }
 
         $formClass = $request->get(Form::REQUEST_NAME);
 
         if (! class_exists($formClass)) {
-            throw new Exception("Form [{$formClass}] does not exist.");
+            throw new AdminException("Form [{$formClass}] does not exist.");
         }
 
         /** @var Form $form */
         $form = app($formClass);
 
         if (! method_exists($form, 'handle')) {
-            throw new Exception("Form method {$formClass}::handle() does not exist.");
+            throw new AdminException("Form method {$formClass}::handle() does not exist.");
         }
 
         return $form;
     }
+
+    protected function sendResponse($response)
+    {
+        if ($response instanceof JsonResponse) {
+            return $response->send();
+        }
+
+        return $response;
+    }
 }

+ 4 - 5
src/Http/Controllers/MenuController.php

@@ -143,14 +143,13 @@ class MenuController extends AdminController
             $form->display('created_at', trans('admin.created_at'));
             $form->display('updated_at', trans('admin.updated_at'));
         })->saved(function (Form $form, $result) {
+            $response = $form->response()->location('auth/menu');
+
             if ($result) {
-                return $form->location('auth/menu', __('admin.save_succeeded'));
+                return $response->success(__('admin.save_succeeded'));
             }
 
-            return $form->location('auth/menu', [
-                'message' => __('admin.nothing_updated'),
-                'status'  => false,
-            ]);
+            return $response->info(__('admin.nothing_updated'));
         });
     }
 

+ 381 - 0
src/Http/JsonResponse.php

@@ -0,0 +1,381 @@
+<?php
+
+namespace Dcat\Admin\Http;
+
+use Dcat\Admin\Exception\AdminException;
+use Dcat\Admin\Support\Helper;
+use Illuminate\Contracts\Validation\Validator;
+use Illuminate\Support\MessageBag;
+use Illuminate\Support\Str;
+use Illuminate\Validation\ValidationException;
+
+/**
+ * Class JsonResponse
+ *
+ * @method $this redirectIf($condition, ?string $url)
+ * @method $this locationIf($condition, ?string $url)
+ * @method $this refreshIf($condition)
+ * @method $this downloadIf($condition, ?string $url)
+ * @method $this scriptIf($condition, ?string $script)
+ * @method $this alertIf($condition, bool $alert = true)
+ * @method $this htmlIf($condition, $html)
+ * @method $this dataIf($condition, array $data)
+ * @method $this optionsIf($condition, array $data)
+ * @method $this withValidationIf($condition, $errors)
+ * @method $this withExceptionIf($condition, \Throwable $e)
+ */
+class JsonResponse
+{
+    protected $status = true;
+    protected $statusCode = 200;
+    protected $exception;
+    protected $data = [];
+    protected $html;
+    protected $options = [];
+
+    /**
+     * 设置请求结果是否成功.
+     *
+     * @param bool $status
+     *
+     * @return $this
+     */
+    public function status(bool $status)
+    {
+        $this->status = $status;
+
+        return $this;
+    }
+
+    /**
+     * 设置 HTTP 状态码.
+     *
+     * @param int $statusCode
+     *
+     * @return $this
+     */
+    public function statusCode(int $statusCode)
+    {
+        $this->statusCode = $statusCode;
+
+        return $this;
+    }
+
+    /**
+     * 设置提示信息.
+     *
+     * @param string $message
+     *
+     * @return $this
+     */
+    public function message(?string $message)
+    {
+        $this->data['message'] = $message;
+
+        return $this;
+    }
+
+    /**
+     * 显示 成功 提示弹窗.
+     *
+     * @param string $message
+     *
+     * @return $this
+     */
+    public function success(?string $message)
+    {
+        return $this->show('success', $message);
+    }
+
+    /**
+     * @param string $message
+     *
+     * @return $this
+     */
+    public function info(?string $message)
+    {
+        return $this->show('info', $message);
+    }
+
+    /**
+     * @param string $message
+     *
+     * @return $this
+     */
+    public function warning(?string $message)
+    {
+        return $this->show('warning', $message);
+    }
+
+    /**
+     * 显示 错误 信息弹窗.
+     *
+     * @param string $message
+     * @param bool   $alert
+     *
+     * @return $this
+     */
+    public function error(?string $message)
+    {
+        $this->status(false);
+
+        return $this->show('error', $message);
+    }
+
+    /**
+     * 显示确认弹窗.
+     *
+     * @param bool $alert
+     *
+     * @return $this
+     */
+    public function alert(bool $alert = true)
+    {
+        return $this->data(['alert' => $alert]);
+    }
+
+    /**
+     * 显示弹窗描述信息.
+     *
+     * @param string $detail
+     *
+     * @return $this
+     */
+    public function detail(?string $detail)
+    {
+        return $this->data(['detail' => $detail]);
+    }
+
+    /**
+     * 显示弹窗信息.
+     *
+     * @param string $type
+     * @param string $message
+     *
+     * @return $this
+     */
+    protected function show(?string $type, ?string $message = null)
+    {
+        if ($message) {
+            $this->message($message);
+        }
+
+        return $this->data(['type' => $type]);
+    }
+
+    /**
+     * 跳转.
+     *
+     * @param string $url
+     *
+     * @return $this
+     */
+    public function redirect(?string $url)
+    {
+        return $this->then(['action' => 'redirect', 'value' => admin_url($url)]);
+    }
+
+    /**
+     * @param string|null $url
+     *
+     * @return $this
+     */
+    public function redirectToIntended(?string $url)
+    {
+        $path = session()->pull('url.intended');
+
+        return $this->redirect($path ?: $url);
+    }
+
+    /**
+     * Location 跳转.
+     *
+     * @param string $location
+     *
+     * @return $this
+     */
+    public function location(?string $location)
+    {
+        return $this->then(['action' => 'location', 'value' => admin_url($location)]);
+    }
+
+    /**
+     * @param string|null $url
+     *
+     * @return $this
+     */
+    public function locationToIntended(?string $url)
+    {
+        $path = session()->pull('url.intended');
+
+        return $this->location($path ?: $url);
+    }
+
+    /**
+     * 下载.
+     *
+     * @param string $url
+     *
+     * @return $this
+     */
+    public function download($url)
+    {
+        return $this->then(['action' => 'download', 'value' => admin_url($url)]);
+    }
+
+    /**
+     * 刷新页面.
+     *
+     * @return $this
+     */
+    public function refresh()
+    {
+        return $this->then(['action' => 'refresh', 'value' => true]);
+    }
+
+    /**
+     * 执行JS代码.
+     *
+     * @param string $script
+     *
+     * @return $this
+     */
+    public function script($script)
+    {
+        return $this->then(['action' => 'script', 'value' => $script]);
+    }
+
+    /**
+     * @param array $value
+     *
+     * @return $this
+     */
+    protected function then(array $value)
+    {
+        $this->data['then'] = $value;
+
+        return $this;
+    }
+
+    /**
+     * 设置返回数据.
+     *
+     * @param array $value
+     *
+     * @return $this
+     */
+    public function data(array $value)
+    {
+        $this->data = array_merge($this->data, $value);
+
+        return $this;
+    }
+
+    /**
+     * 返回 HTML.
+     *
+     * @param string $html
+     *
+     * @return $this
+     */
+    public function html($html)
+    {
+        $this->html = $html;
+
+        return $this;
+    }
+
+    /**
+     * 设置其他字段.
+     *
+     * @param array $options
+     *
+     * @return $this
+     */
+    public function options(array $options)
+    {
+        $this->options = array_merge($this->options, $options);
+
+        return $this;
+    }
+
+    /**
+     * 设置字段验证错误信息.
+     *
+     * @param $errors
+     *
+     * @return $this
+     */
+    public function withValidation($errors)
+    {
+        if ($errors instanceof Validator) {
+            $errors = $errors->errors();
+        }
+
+        if ($errors instanceof MessageBag) {
+            $errors = $errors->getMessages();
+        }
+
+        return $this
+            ->status(false)
+            ->statusCode(422)
+            ->options(['errors' => $errors]);
+    }
+
+    /**
+     * 响应异常.
+     *
+     * @param \Throwable $exception
+     *
+     * @return $this
+     */
+    public function withException(\Throwable $exception)
+    {
+        if ($exception instanceof ValidationException) {
+            return $this->withValidation($exception->errors());
+        }
+
+        return $this
+            ->status(false)
+            ->error(
+                sprintf('[%s] %s', get_class($exception), $exception->getMessage())
+            );
+    }
+
+    /**
+     * @return \Illuminate\Http\JsonResponse
+     */
+    public function send()
+    {
+        $data = ['status' => $this->status, 'data' => $this->data];
+
+        if ($this->html) {
+            $data['html'] = Helper::render($this->html);
+        }
+
+        return response()->json($this->options + $data, $this->statusCode);
+    }
+
+    public function __call($method, $arguments)
+    {
+        if (Str::endsWith($method, 'If')) {
+            if (array_shift($arguments)) {
+                $method = Str::replaceLast('If', '', $method);
+
+                return $this->$method(...$arguments);
+            }
+        }
+
+        throw new AdminException(sprintf('Call to undefined method "%s"', $method));
+    }
+
+    /**
+     * @param mixed ...$params
+     *
+     * @return $this
+     */
+    public static function make(...$params)
+    {
+        return new static(...$params);
+    }
+}

+ 2 - 2
src/Show.php

@@ -26,8 +26,8 @@ use Illuminate\Support\Traits\Macroable;
 
 class Show implements Renderable
 {
-    use HasBuilderEvents,
-        Macroable {
+    use HasBuilderEvents;
+    use Macroable {
             __call as macroCall;
         }
 

+ 7 - 2
src/Support/DatabaseUpdater.php

@@ -33,7 +33,7 @@ class DatabaseUpdater
 
         Model::unguard();
 
-        Db::connection($this->connection())->transaction(function () use ($object, $callback) {
+        $this->transaction(function () use ($object, $callback) {
             if ($object instanceof Migration) {
                 $object->up();
             }
@@ -64,7 +64,7 @@ class DatabaseUpdater
 
         Model::unguard();
 
-        Db::connection($this->connection())->transaction(function () use ($object, $callback) {
+        $this->transaction(function () use ($object, $callback) {
             if ($object instanceof Migration) {
                 $object->down();
             }
@@ -171,6 +171,11 @@ class DatabaseUpdater
         return trim($namespace) . '\\' . trim($class);
     }
 
+    public function transaction($callback)
+    {
+        return DB::connection($this->connection())->transaction($callback);
+    }
+
     public function connection()
     {
         return config('admin.database.connection') ?: config('database.default');

+ 22 - 122
src/Traits/HasFormResponse.php

@@ -2,75 +2,35 @@
 
 namespace Dcat\Admin\Traits;
 
-use Dcat\Admin\Support\Helper;
+use Dcat\Admin\Http\JsonResponse;
 use Illuminate\Http\Request;
 use Illuminate\Support\MessageBag;
-use Illuminate\Validation\Validator;
 
 trait HasFormResponse
 {
     protected $currentUrl;
 
     /**
-     * Get ajax response.
-     *
-     * @param $message
-     * @param null $redirect
-     * @param bool $status
-     * @param array $options
-     *
-     * @return bool|\Illuminate\Http\JsonResponse
-     */
-    public function ajaxResponse(
-        ?string $message,
-        ?string $redirect = null,
-        bool $status = true,
-        array $options = []
-    ) {
-        $location = $options['location'] ?? false;
-        $urlKey = $location ? 'location' : 'redirect';
-
-        return response()->json([
-            'status'   => $status,
-            'message'  => $message,
-            $urlKey    => $redirect ? admin_url($redirect) : '',
-        ]);
-    }
-
-    /**
-     * Send a location redirect response.
-     *
-     * @param string|null $message
-     * @param string|null $url
-     * @param bool        $status
-     *
-     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
+     * @return JsonResponse
      */
-    public function location($url = null, $options = [])
+    public function response()
     {
-        if (is_string($options)) {
-            $options = ['message' => $options];
-        }
-        $options['location'] = true;
-
-        return $this->redirect($url, $options);
+        return new JsonResponse();
     }
 
     /**
-     * @param string $message
-     * @param string $redirectTo
+     * 返回字段验证错误信息.
+     *
+     * @param array|MessageBag|\Illuminate\Validation\Validator $validationMessages
      *
-     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
+     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
      */
-    public function success($message = null, $redirectTo = null)
+    public function validationErrorsResponse($validationMessages)
     {
-        $redirectTo = $redirectTo ?: $this->getCurrentUrl();
-
-        return $this->redirect($redirectTo, [
-            'message'     => $message,
-            'status'      => true,
-            'status_code' => 200,
-        ]);
+        return $this
+            ->response()
+            ->withValidation($validationMessages)
+            ->send();
     }
 
     /**
@@ -88,6 +48,8 @@ trait HasFormResponse
     }
 
     /**
+     * 获取当前URL.
+     *
      * @param Request|null $request
      *
      * @return string
@@ -115,80 +77,18 @@ trait HasFormResponse
     }
 
     /**
-     * @param string $message
-     * @param string $redirectTo
-     * @param int    $statusCode
+     * 响应数据.
      *
-     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
-     */
-    public function error($message = null, $redirectTo = null, int $statusCode = 200)
-    {
-        if (! $redirectTo) {
-            return $this->ajaxResponse($message, null, false);
-        }
-
-        return $this->redirect($redirectTo, [
-            'message'     => $message,
-            'status'      => false,
-            'status_code' => $statusCode,
-        ]);
-    }
-
-    /**
-     * Get redirect response.
+     * @param $response
      *
-     * @param string|array $url
-     * @param array|string $options
-     *
-     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
+     * @return \Illuminate\Http\JsonResponse
      */
-    public function redirect($url = null, $options = null)
-    {
-        if (is_array($url)) {
-            $options = $url;
-            $url = null;
-        }
-
-        if (is_string($options)) {
-            $message = $options;
-            $options = [];
-        } else {
-            $message = $options['message'] ?? null;
-        }
-
-        $status = (bool) ($options['status'] ?? true);
-
-        $message = $message ?: trans('admin.save_succeeded');
-
-        return $this->ajaxResponse($message, $url, $status, $options);
-    }
-
-    /**
-     * @param string|null  $url
-     * @param array|string $options
-     *
-     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
-     */
-    public function redirectToIntended(?string $url, $options = null)
-    {
-        $path = session()->pull('url.intended');
-
-        return $this->redirect($path ?: $url, $options);
-    }
-
-    /**
-     * @param array|MessageBag|\Illuminate\Validation\Validator $validationMessages
-     *
-     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
-     */
-    public function validationErrorsResponse($validationMessages)
+    protected function sendResponse($response)
     {
-        if ($validationMessages instanceof Validator) {
-            $validationMessages = $validationMessages->getMessageBag();
+        if ($response instanceof JsonResponse) {
+            return $response->send();
         }
 
-        return response()->json([
-            'errors' => is_array($validationMessages) ? $validationMessages : $validationMessages->getMessages(),
-        ], 422);
+        return $response;
     }
 }

+ 1 - 6
src/Traits/HasUploadedFile.php

@@ -7,11 +7,6 @@ use Dcat\Admin\Support\WebUploader;
 use Illuminate\Filesystem\FilesystemAdapter;
 use Illuminate\Support\Facades\Storage;
 
-/**
- * 文件上传辅助功能.
- *
- * Trait HasUploadedFile
- */
 trait HasUploadedFile
 {
     /**
@@ -148,7 +143,7 @@ trait HasUploadedFile
      *
      * @return \Illuminate\Http\JsonResponse
      */
-    public function responseDeleteFail($message = '')
+    public function responseDeleteFailed($message = '')
     {
         return response()->json(['status' => false, 'message' => $message]);
     }

+ 2 - 2
src/Tree.php

@@ -18,8 +18,8 @@ use Illuminate\Support\Traits\Macroable;
 
 class Tree implements Renderable
 {
-    use HasBuilderEvents,
-        Macroable;
+    use HasBuilderEvents;
+    use Macroable;
 
     /**
      * @var array

+ 17 - 2
src/Widgets/DialogForm.php

@@ -229,7 +229,7 @@ class DialogForm
      */
     protected function render()
     {
-        $this->setupOptions();
+        $this->setUpOptions();
 
         $opts = json_encode($this->options);
 
@@ -273,7 +273,7 @@ JS
      *
      * @return void
      */
-    protected function setupOptions()
+    protected function setUpOptions()
     {
         $this->options['lang'] = [
             'submit' => trans('admin.submit'),
@@ -316,8 +316,23 @@ JS
 
         $form->width(9, 2);
 
+        $form->composing(function ($form) {
+            static::addScript($form);
+        });
+
         Content::composing(function (Content $content) {
             $content->view(static::$contentView);
         });
     }
+
+    protected static function addScript(Form $form)
+    {
+        $confirm = json_encode($form->builder()->confirm);
+
+        Admin::script(
+            <<<JS
+Dcat.FormConfirm = {$confirm};
+JS
+        );
+    }
 }