Ver Fonte

增加Action模块处理接口

jqh há 5 anos atrás
pai
commit
1e04aae69b

+ 151 - 0
src/Actions/Action.php

@@ -0,0 +1,151 @@
+<?php
+
+namespace Dcat\Admin\Actions;
+
+use Dcat\Admin\Traits\HasHtmlAttributes;
+use Illuminate\Contracts\Support\Renderable;
+
+abstract class Action implements Renderable
+{
+    use HasHtmlAttributes,
+        Authorizable,
+        ActionHandler;
+
+    /**
+     * @var array
+     */
+    protected static $selectors = [];
+
+    /**
+     * @var mixed
+     */
+    protected $primaryKey;
+
+    /**
+     * @var string
+     */
+    protected $name;
+
+    /**
+     * @var string
+     */
+    protected $selector;
+
+    /**
+     * @var string
+     */
+    public $selectorPrefix = '.admin-action-';
+
+    /**
+     * @var string
+     */
+    protected $method = 'POST';
+
+    /**
+     * @var string
+     */
+    protected $event = 'click';
+
+    /**
+     * Get primary key value of action.
+     *
+     * @return mixed
+     */
+    public function key()
+    {
+        return $this->primaryKey;
+    }
+
+    /**
+     * Set primary key value of action.
+     *
+     * @param mixed $key
+     *
+     * @return void
+     */
+    public function setKey($key)
+    {
+        $this->primaryKey = $key;
+    }
+
+    /**
+     * @return string
+     */
+    protected function elementClass()
+    {
+        return ltrim($this->selector($this->selectorPrefix), '.');
+    }
+
+    /**
+     * Get batch action title.
+     *
+     * @return string
+     */
+    public function name()
+    {
+        return $this->name;
+    }
+
+    /**
+     * @param string $prefix
+     *
+     * @return mixed|string
+     */
+    public function selector($prefix)
+    {
+        if (is_null($this->selector)) {
+            return static::makeSelector(get_called_class(), $prefix);
+        }
+
+        return $this->selector;
+    }
+
+    /**
+     * @param string $class
+     * @param string $prefix
+     *
+     * @return string
+     */
+    public static function makeSelector($class, $prefix)
+    {
+        if (! isset(static::$selectors[$class])) {
+            static::$selectors[$class] = uniqid($prefix);
+        }
+
+        return static::$selectors[$class];
+    }
+
+    /**
+     * @return void
+     */
+    protected function addScript()
+    {
+    }
+
+    /**
+     * @return string
+     */
+    protected function html()
+    {
+    }
+
+    protected function prepareHandle()
+    {
+        if (! method_exists($this, 'handle')) {
+            return;
+        }
+
+        $this->addHandlerScript();
+    }
+
+    /**
+     * @return mixed
+     */
+    public function render()
+    {
+        $this->prepareHandle();
+        $this->addScript();
+
+        return $this->html();
+    }
+}

+ 222 - 0
src/Actions/ActionHandler.php

@@ -0,0 +1,222 @@
+<?php
+
+namespace Dcat\Admin\Actions;
+
+use Dcat\Admin\Admin;
+
+trait ActionHandler
+{
+    /**
+     * @var Response
+     */
+    protected $response;
+
+    /**
+     * @return Response
+     */
+    public function response()
+    {
+        if (is_null($this->response)) {
+            $this->response = new Response();
+        }
+
+        return $this->response;
+    }
+
+    /**
+     * @return string
+     */
+    public function getMethod()
+    {
+        return $this->method;
+    }
+
+    /**
+     * @return array
+     */
+    public function parameters()
+    {
+        return [];
+    }
+
+    /**
+     * Return the confirm message.
+     *
+     * @return string|void
+     */
+    protected function confirm()
+    {
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getCalledClass()
+    {
+        return str_replace('\\', '_', get_called_class());
+    }
+
+    /**
+     * @return string
+     */
+    public function getHandleRoute()
+    {
+        return admin_url('_handle_action_');
+    }
+
+    /**
+     * @return void
+     */
+    protected function addHandlerScript()
+    {
+        $parameters = json_encode($this->parameters());
+
+        $resolveScript = <<<JS
+target.data('working', 1);
+Object.assign(data, {$parameters});
+{$this->actionScript()}
+{$this->buildActionPromise()}
+{$this->handleActionPromise()}
+JS;
+
+        $script = <<<JS
+(function ($) {
+    $('{$this->selector($this->selectorPrefix)}').off('{$this->event}').on('{$this->event}', function() {
+        var data = $(this).data(),
+            target = $(this);
+        if (target.data('working')) {
+            return;
+        }
+        {$this->confirmScript($resolveScript)}
+    });
+})(jQuery);
+JS;
+
+        Admin::script($script);
+    }
+
+    /**
+     * @param string $resolveScript
+     *
+     * @return string
+     */
+    protected function confirmScript($resolveScript)
+    {
+        if (! $message = $this->confirm()) {
+            return $resolveScript;
+        }
+
+        return <<<JS
+LA.confirm('{$message}', function () {
+    {$resolveScript}
+});
+JS;
+    }
+
+    /**
+     * @return string
+     */
+    protected function actionScript()
+    {
+        return '';
+    }
+
+    /**
+     * @return string
+     */
+    protected function buildActionPromise()
+    {
+        return <<<JS
+var process = new Promise(function (resolve,reject) {
+    
+    Object.assign(data, {
+        _token: LA.token,
+        _action: '{$this->getCalledClass()}',
+    });
+
+    $.ajax({
+        method: '{$this->getMethod()}',
+        url: '{$this->getHandleRoute()}',
+        data: data,
+        success: function (data) {
+            target.data('working', 0);
+            resolve([data, target]);
+        },
+        error:function(request){
+            target.data('working', 0);
+            reject([request, target]);
+        }
+    });
+});
+JS;
+    }
+
+    /**
+     * @return string
+     */
+    public function handleActionPromise()
+    {
+        Admin::script($this->buildDefaultPromiseCallbacks());
+
+        return <<<'JS'
+process.then(window.ACTION_RSOLVER).catch(window.ACTION_CATCHER);
+JS;
+    }
+
+    /**
+     * @return string
+     */
+    protected function buildDefaultPromiseCallbacks()
+    {
+        return <<<'JS'
+window.ACTION_RSOLVER = function (data) {
+    var response = data[0],
+        target   = data[1];
+        
+    if (typeof response !== 'object') {
+        return LA.error({type: 'error', title: 'Oops!'});
+    }
+    
+    response = response.data;
+    
+    var then = function (then) {
+        switch (then.action) {
+            case 'refresh':
+                LA.reload();
+                break;
+            case 'download':
+                window.open(then.value, '_blank');
+                break;
+            case 'redirect':
+                LA.reload(then.value);
+                break;
+            case 'location':
+                window.location = then.value;
+                break;
+        }
+    };
+    
+    if (typeof response.html === 'string') {
+        target.html(response.html);
+    }
+
+    if (typeof response.message === 'string' && response.type) {
+        LA[response.type](response.message);
+    }
+    
+    if (response.then) {
+      then(response.then);
+    }
+};
+
+window.ACTION_CATCHER = function (data) {
+    var request = data[0], target = data[1];
+    
+    if (request && typeof request.responseJSON === 'object') {
+        LA.error(request.responseJSON.message)
+    }
+    console.error(request);
+};
+JS;
+    }
+}

+ 28 - 0
src/Actions/Authorizable.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace Dcat\Admin\Actions;
+
+use Dcat\Admin\Admin;
+
+trait Authorizable
+{
+    /**
+     * @return bool
+     */
+    public function passesAuthorization()
+    {
+        if (method_exists($this, 'authorize')) {
+            return $this->authorize(Admin::user()) == true;
+        }
+
+        return true;
+    }
+
+    /**
+     * @return Response
+     */
+    public function failedAuthorization()
+    {
+        return $this->response()->error(__('admin.deny'));
+    }
+}

+ 202 - 0
src/Actions/Response.php

@@ -0,0 +1,202 @@
+<?php
+
+namespace Dcat\Admin\Actions;
+
+use Illuminate\Validation\ValidationException;
+
+class Response
+{
+    /**
+     * @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' => $url]);
+    }
+
+    /**
+     * Send a location redirect response.
+     *
+     * @param string $location
+     *
+     * @return $this
+     */
+    public function location(string $location)
+    {
+        return $this->then(['action' => 'location', 'value' => $location]);
+    }
+
+    /**
+     * Send a download response.
+     *
+     * @param string $url
+     *
+     * @return $this
+     */
+    public function download($url)
+    {
+        return $this->then(['action' => 'download', 'value' => $url]);
+    }
+
+    /**
+     * Send a refresh response.
+     *
+     * @return $this
+     */
+    public function refresh()
+    {
+        return $this->then(['action' => 'refresh', 'value' => true]);
+    }
+
+    /**
+     * @param array $value
+     *
+     * @return $this
+     */
+    protected function then(array $value)
+    {
+        $this->data['then'] = $value;
+
+        return $this;
+    }
+
+    /**
+     * @param array $value
+     *
+     * @return $this
+     */
+    protected 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 \Exception $exception
+     *
+     * @return mixed
+     */
+    public static function withException(\Exception $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'] = $this->html;
+        }
+
+        return response()->json($data);
+    }
+}

+ 1 - 0
src/Admin.php

@@ -279,6 +279,7 @@ class Admin
                     $router->resource('auth/permissions', 'PermissionController');
                 }
 
+                $router->post('_handle_action_', 'HandleActionController@handle')->name('admin.handle-action');
                 $router->post('_handle_form_', 'HandleFormController@handle')->name('admin.handle-form');
             });
 

+ 1 - 1
src/Console/PublishCommand.php

@@ -18,7 +18,7 @@ class PublishCommand extends Command
      *
      * @var string
      */
-    protected $description = "re-publish dcat-admin's assets, configuration, language and migration files. If you want overwrite the existing files, you can add the `--force` option";
+    protected $description = "Re-publish dcat-admin's assets, configuration, language and migration files. If you want overwrite the existing files, you can add the `--force` option";
 
     /**
      * Execute the console command.

+ 64 - 0
src/Controllers/HandleActionController.php

@@ -0,0 +1,64 @@
+<?php
+
+namespace Dcat\Admin\Controllers;
+
+use Dcat\Admin\Actions\Response;
+use Dcat\Admin\Grid\GridAction;
+use Exception;
+use Illuminate\Http\Request;
+
+class HandleActionController
+{
+    /**
+     * @param Request $request
+     *
+     * @return $this|\Illuminate\Http\JsonResponse
+     */
+    public function handle(Request $request)
+    {
+        try {
+            $action = $this->resolveActionInstance($request);
+
+            $action->setKey($request->get('_key'));
+
+            if (! $action->passesAuthorization()) {
+                return $action->failedAuthorization();
+            }
+
+            $response = $action->handle($request);
+        } catch (\Throwable $exception) {
+            return Response::withException($exception)->send();
+        }
+
+        return $response instanceof Response ? $response->send() : $response;
+    }
+
+    /**
+     * @param Request $request
+     *
+     * @throws Exception
+     *
+     * @return GridAction
+     */
+    protected function resolveActionInstance(Request $request): GridAction
+    {
+        if (! $request->has('_action')) {
+            throw new Exception('Invalid action request.');
+        }
+
+        $actionClass = str_replace('_', '\\', $request->get('_action'));
+
+        if (! class_exists($actionClass)) {
+            throw new Exception("Action [{$actionClass}] does not exist.");
+        }
+
+        /** @var GridAction $action */
+        $action = app($actionClass);
+
+        if (! method_exists($action, 'handle')) {
+            throw new Exception("Action method {$actionClass}::handle() does not exist.");
+        }
+
+        return $action;
+    }
+}

+ 1 - 2
src/Controllers/HandleFormController.php

@@ -5,10 +5,9 @@ namespace Dcat\Admin\Controllers;
 use Dcat\Admin\Widgets\Form;
 use Exception;
 use Illuminate\Http\Request;
-use Illuminate\Routing\Controller;
 use Symfony\Component\HttpFoundation\Response;
 
-class HandleFormController extends Controller
+class HandleFormController
 {
     /**
      * @param Request $request

+ 19 - 0
src/Grid/Displayers/Actions.php

@@ -3,6 +3,7 @@
 namespace Dcat\Admin\Grid\Displayers;
 
 use Dcat\Admin\Form;
+use Dcat\Admin\Grid\RowAction;
 use Dcat\Admin\Support\Helper;
 
 class Actions extends AbstractDisplayer
@@ -40,6 +41,10 @@ class Actions extends AbstractDisplayer
      */
     public function append($action)
     {
+        if ($action instanceof RowAction) {
+            $this->prepareAction($action);
+        }
+
         array_push($this->appends, $action);
 
         return $this;
@@ -54,11 +59,25 @@ class Actions extends AbstractDisplayer
      */
     public function prepend($action)
     {
+        if ($action instanceof RowAction) {
+            $this->prepareAction($action);
+        }
+
         array_unshift($this->prepends, $action);
 
         return $this;
     }
 
+    /**
+     * @param RowAction $action
+     */
+    protected function prepareAction(RowAction $action)
+    {
+        $action->setGrid($this->grid)
+            ->setColumn($this->column)
+            ->setRow($this->row);
+    }
+
     /**
      * Disable view action.
      *

+ 0 - 10
src/Grid/Displayers/DropdownActions.php

@@ -127,16 +127,6 @@ JS;
         }
     }
 
-    /**
-     * @param RowAction $action
-     */
-    protected function prepareAction(RowAction $action)
-    {
-        $action->setGrid($this->grid)
-            ->setColumn($this->column)
-            ->setRow($this->row);
-    }
-
     /**
      * Disable view action.
      *

+ 2 - 67
src/Grid/GridAction.php

@@ -2,32 +2,14 @@
 
 namespace Dcat\Admin\Grid;
 
+use Dcat\Admin\Actions\Action;
 use Dcat\Admin\Grid;
-use Dcat\Admin\Traits\HasHtmlAttributes;
-use Illuminate\Contracts\Support\Renderable;
 
 /**
  * Class GridAction.
  */
-abstract class GridAction implements Renderable
+abstract class GridAction extends Action
 {
-    use HasHtmlAttributes;
-
-    /**
-     * @var array
-     */
-    protected static $selectors = [];
-
-    /**
-     * @var string
-     */
-    protected $name;
-
-    /**
-     * @var string
-     */
-    protected $selector;
-
     /**
      * @var Grid
      */
@@ -59,51 +41,4 @@ abstract class GridAction implements Renderable
     {
         return $this->parent->resource();
     }
-
-    /**
-     * @return string
-     */
-    protected function elementClass()
-    {
-        return ltrim($this->selector($this->selectorPrefix), '.');
-    }
-
-    /**
-     * Get batch action title.
-     *
-     * @return string
-     */
-    public function name()
-    {
-        return $this->name;
-    }
-
-    /**
-     * @param string $prefix
-     *
-     * @return mixed|string
-     */
-    public function selector($prefix)
-    {
-        if (is_null($this->selector)) {
-            return static::makeSelector(get_called_class(), $prefix);
-        }
-
-        return $this->selector;
-    }
-
-    /**
-     * @param string $class
-     * @param string $prefix
-     *
-     * @return string
-     */
-    public static function makeSelector($class, $prefix)
-    {
-        if (! isset(static::$selectors[$class])) {
-            static::$selectors[$class] = uniqid($prefix);
-        }
-
-        return static::$selectors[$class];
-    }
 }

+ 7 - 9
src/Grid/RowAction.php

@@ -26,9 +26,13 @@ abstract class RowAction extends GridAction
      *
      * @return mixed
      */
-    protected function key()
+    public function key()
     {
-        return $this->row->get($this->parent->keyName());
+        if ($this->row) {
+            return $this->row->get($this->parent->keyName());
+        }
+
+        return parent::key();
     }
 
     /**
@@ -90,10 +94,8 @@ abstract class RowAction extends GridAction
      *
      * @return string
      */
-    public function render()
+    public function html()
     {
-        $this->addScript();
-
         if (! $href = $this->href()) {
             $href = 'javascript:void(0);';
         }
@@ -108,8 +110,4 @@ abstract class RowAction extends GridAction
             $this->name()
         );
     }
-
-    protected function addScript()
-    {
-    }
 }