Explorar o código

重构表格行内编辑功能

Jiang qinghua %!s(int64=4) %!d(string=hai) anos
pai
achega
0c770a3626

+ 18 - 0
resources/views/grid/displayer/editinline/input.blade.php

@@ -0,0 +1,18 @@
+@extends('admin::grid.displayer.editinline.template')
+
+@section('field')
+    <input class="form-control ie-input"/>
+@endsection
+
+<script>
+@section('popover-content')
+    $template.find('input').attr('value', $trigger.data('value'));
+@endsection
+
+@section('popover-shown')
+    $popover.find('.ie-input').focus();
+    @if(! empty($mask))
+    $popover.find('.ie-input').inputmask(@json($mask));
+    @endif
+@endsection
+</script>

+ 147 - 0
resources/views/grid/displayer/editinline/template.blade.php

@@ -0,0 +1,147 @@
+<span class="ie-wrap">
+    <a
+        href="javascript:void(0);"
+        class="{{ $class }}"
+        data-editinline="popover"
+        data-temp="grid-editinline-{{ $type }}-{{ $name }}"
+        data-value="{{ $value }}"
+        data-original="{{ $value }}"
+        data-key="{{ $key }}"
+        data-name="{{ $name }}"
+        data-url="{!! $url !!}"
+        data-refresh="{{ $refresh }}"
+    >
+        <span class="ie-display">{{ $display }}</span>
+
+        <i class="feather icon-edit-2" style="visibility: hidden;"></i>
+    </a>
+</span>
+
+<template>
+    <template id="grid-editinline-{{ $type }}-{{ $name }}">
+        <div class="ie-content ie-content-{{ $name }}" data-type="{{ $type }}">
+            <div class="ie-container">
+                @yield('field')
+                <div class="error"></div>
+            </div>
+            <div class="ie-action">
+                <button class="btn btn-primary btn-sm ie-submit">{{ __('admin.submit') }}</button>
+                <button class="btn btn-white btn-sm ie-cancel">{{ __('admin.cancel') }}</button>
+            </div>
+        </div>
+    </template>
+</template>
+
+<style>
+    .ie-wrap>a {
+        padding: 3px;
+        border-radius: 3px;
+        color:@primary;
+    }
+
+    table tr:hover .ie-wrap>a>i {
+        visibility: visible !important;
+    }
+
+    .ie-action button {
+        margin: 10px 0 10px 10px;
+        float: right;
+    }
+</style>
+
+<script>
+    function hide() {
+        $('[data-editinline="popover"]').popover('hide');
+    }
+
+    $('.{{ $class }}').popover({
+        html: true,
+        container: 'body',
+        trigger: 'manual',
+        sanitize: false,
+        placement: function (context, source) {
+            var position = $(source).position();
+            if (position.left < 100) return "right";
+            if (position.top < 110) return "bottom";
+            if ($(window).height() - $(source).offset().top < 370) {
+                return 'top';
+            }
+            return "bottom";
+        },
+        content: function () {
+            var $trigger = $(this);
+            var $template = $($('template#'+$(this).data('temp')).html());
+
+            @yield('popover-content')
+
+            return $template.prop("outerHTML");
+        }
+    }).on('shown.bs.popover', function (e) {
+
+        var $popover = $($(this).data('bs.popover').tip).find('.ie-content');
+        var $display = $(this).parents('.ie-wrap').find('.ie-display');
+        var $trigger = $(this);
+
+        $popover.data('display', $display);
+        $popover.data('trigger', $trigger);
+
+        @yield('popover-shown')
+
+    }).click(function () {
+        hide();
+        $(this).popover('toggle');
+    });
+</script>
+
+<script>
+    function hide() {
+        $('[data-editinline="popover"]').popover('hide');
+    }
+
+    $(document).off('click', '.ie-content .ie-cancel').on('click', '.ie-content .ie-cancel', hide)
+
+    $(document).off('click', '.ie-content .ie-submit').on('click', '.ie-content .ie-submit', function () {
+        var $popover = $(this).closest('.ie-content'),
+            $trigger = $popover.data('trigger'),
+            original = $trigger.data('original'),
+            refresh = $trigger.data('refresh'),
+            val,
+            label;
+
+        switch($popover.data('type')) {
+            case 'input':
+            case 'textarea':
+                val = $popover.find('.ie-input').val();
+                label = val;
+                break;
+        }
+
+        if (val == original) {
+            hide();
+            return;
+        }
+
+        var data = {};
+        data[$trigger.data('name')] = val;
+        data['_inline_edit_'] = 1;
+
+        $.put({
+            url: $trigger.data('url'),
+            data: data,
+            error:function(a,b,c) {
+                Dcat.handleAjaxError(a, b, c);
+            },
+        }).done(function (res) {
+            var data = res.data;
+            if (res.status === true) {
+                Dcat.success(data.message);
+                $popover.data('display').html(label);
+                $trigger.data('value', val).data('original', val);
+                hide();
+                refresh && Dcat.reload();
+            } else {
+                Dcat.error(data.message);
+            }
+        });
+    });
+</script>

+ 17 - 0
resources/views/grid/displayer/editinline/textarea.blade.php

@@ -0,0 +1,17 @@
+@extends('admin::grid.displayer.editinline.template')
+
+@section('field')
+    <textarea class="form-control ie-input" rows="{{ $rows }}"></textarea>
+@endsection
+
+<script>
+@section('popover-content')
+    $template.find('textarea').text($trigger.data('value'));
+@endsection
+
+@section('popover-shown')
+    $popover.find('.ie-input').focus();
+@endsection
+</script>
+
+

+ 6 - 2
src/Grid/Column.php

@@ -16,7 +16,9 @@ use Illuminate\Support\Str;
 use Illuminate\Support\Traits\Macroable;
 use Illuminate\Support\Traits\Macroable;
 
 
 /**
 /**
- * @method $this editable(bool $refresh = false)
+ * @method $this input(bool|array $options = [])
+ * @method $this textarea(bool|array $options = [])
+ * @method $this editable(bool|array $options = [])
  * @method $this switch(string $color = '', $refresh = false)
  * @method $this switch(string $color = '', $refresh = false)
  * @method $this switchGroup($columns = [], string $color = '', $refresh = false)
  * @method $this switchGroup($columns = [], string $color = '', $refresh = false)
  * @method $this image($server = '', int $width = 200, int $height = 200)
  * @method $this image($server = '', int $width = 200, int $height = 200)
@@ -90,7 +92,9 @@ class Column
         'copyable'         => Displayers\Copyable::class,
         'copyable'         => Displayers\Copyable::class,
         'orderable'        => Displayers\Orderable::class,
         'orderable'        => Displayers\Orderable::class,
         'limit'            => Displayers\Limit::class,
         'limit'            => Displayers\Limit::class,
-        'editable'         => Displayers\Editable::class,
+        'editable'         => Displayers\Input::class,
+        'input'            => Displayers\Input::class,
+        'textarea'         => Displayers\Textarea::class,
     ];
     ];
 
 
     /**
     /**

+ 32 - 97
src/Grid/Displayers/Editable.php

@@ -5,118 +5,53 @@ namespace Dcat\Admin\Grid\Displayers;
 use Dcat\Admin\Admin;
 use Dcat\Admin\Admin;
 use Dcat\Admin\Support\Helper;
 use Dcat\Admin\Support\Helper;
 
 
-class Editable extends AbstractDisplayer
+abstract class Editable extends AbstractDisplayer
 {
 {
-    protected $selector = 'grid-column-editable';
+    protected $type;
 
 
-    public function display($refresh = false)
-    {
-        $this->addScript();
-        $this->addStyle();
+    protected $view;
+
+    protected $options = [
+        // 是否刷新页面
+        'refresh' => false,
+    ];
 
 
-        $value = Helper::render($this->value);
+    public function display($options = [])
+    {
+        if (is_bool($options)) {
+            $options = ['refresh' => $options];
+        }
 
 
-        $label = __('admin.save');
+        $this->options = array_merge($this->options, $options);
 
 
-        return <<<HTML
-<div class="d-inline">
-    <span class="{$this->selector}" contenteditable="true">{$value}</span>
-    <span class="save hidden" 
-        data-value="{$this->value}" 
-        data-name="{$this->column->getName()}" 
-        data-id="{$this->getKey()}" 
-        data-refresh="{$refresh}"
-        data-url="{$this->getUrl()}">
-        {$label}
-    </span>
-    <div class="d-none"></div>
-</div>
-HTML;
+        return admin_view($this->view, array_merge($this->variables(), $this->defaultOptions() + $this->options));
     }
     }
 
 
-    protected function getUrl()
+    protected function defaultOptions()
     {
     {
-        return $this->resource().'/'.$this->getKey();
+        return [];
     }
     }
 
 
-    protected function addStyle()
+    public function variables()
     {
     {
-        $color = Admin::color()->link();
-        $primary = Admin::color()->primary();
-
-        Admin::style(
-            <<<CSS
-.{$this->selector}{border-bottom:dashed 1px $color;color: $color;display: inline-block; -webkit-user-modify: read-write-plaintext-only;}
-.{$this->selector}+.save{margin-left: 0.4rem;color: $color}
-body.dark-mode .{$this->selector}{color: $primary;border-color: $primary;}
-body.dark-mode .{$this->selector}+.save{color: $primary}
-CSS
-        );
+        return [
+            'key'     => $this->getKey(),
+            'class'   => $this->getSelector(),
+            'type'    => $this->type,
+            'display' => Helper::render($this->value),
+            'value'   => $this->column->getOriginal(),
+            'name'    => $this->column->getName(),
+            'url'     => $this->getUrl(),
+        ];
     }
     }
 
 
-    protected function addScript()
+    protected function getSelector()
     {
     {
-        $script = <<<JS
-$(".{$this->selector}").on("click focus", function() {
-    $(this).next().removeClass("hidden");
-}).on('blur', function () {
-    var icon = $(this).next();
-    setTimeout(function () {
-        icon.addClass("hidden")
-    }, 200)
-});
-$('.{$this->selector}+.save').on("click",function() {
-    var obj = $(this),
-        url = obj.data('url'),
-        name = obj.data('name'),
-        refresh = obj.data('refresh'),
-        old_value = obj.data('value'),
-        value = obj.prev().html(),
-        tmp = obj.next();
-    
-    tmp.html(value);
-
-    value = tmp.text().replace(new RegExp("<br>","g"), '').replace(new RegExp("&nbsp;","g"), '').trim();
-    
-    var data = {};
-    if (name.indexOf('.') === -1) {
-        data[name] = value;
-    } else {
-        name = name.split('.');
-        
-        data[name[0]] = {};
-        data[name[0]][name[1]] = value;
+        return 'grid-editable-'.$this->type;
     }
     }
-    
-    Dcat.NP.start();
-    $.put({
-        url: url,
-        data: data,
-        success: function (d) {
-            var msg = d.data.message || d.message;
-            if (d.status) {
-                obj.attr('data-value',value).addClass("hidden").prev().html(value);
-                Dcat.success(msg);
-                
-                refresh && Dcat.reload()
-            } else {
-                obj.prev().html(old_value);
-                Dcat.error(msg);
-            }
-        },
-        error:function(a,b,c) {
-            obj.prev().html(old_value);
-            Dcat.handleAjaxError(a, b, c);
-        },
-        complete:function(a,b) {
-            Dcat.NP.done();
-        }
-    });
-    
-    return false;
-})
-JS;
 
 
-        Admin::script($script);
+    protected function getUrl()
+    {
+        return $this->resource().'/'.$this->getKey();
     }
     }
 }
 }

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

@@ -0,0 +1,10 @@
+<?php
+
+namespace Dcat\Admin\Grid\Displayers;
+
+class Input extends Editable
+{
+    protected $type = 'input';
+
+    protected $view = 'admin::grid.displayer.editinline.input';
+}

+ 17 - 0
src/Grid/Displayers/Textarea.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace Dcat\Admin\Grid\Displayers;
+
+class Textarea extends Editable
+{
+    protected $type = 'textarea';
+
+    protected $view = 'admin::grid.displayer.editinline.textarea';
+
+    public function defaultOptions()
+    {
+        return [
+            'rows' => 5,
+        ];
+    }
+}

+ 1 - 1
src/Http/Controllers/UserController.php

@@ -23,7 +23,7 @@ class UserController extends AdminController
         return Grid::make(Administrator::with(['roles']), function (Grid $grid) {
         return Grid::make(Administrator::with(['roles']), function (Grid $grid) {
             $grid->column('id', 'ID')->sortable();
             $grid->column('id', 'ID')->sortable();
             $grid->column('username');
             $grid->column('username');
-            $grid->column('name');
+            $grid->column('name')->textarea();
 
 
             if (config('admin.permission.enable')) {
             if (config('admin.permission.enable')) {
                 $grid->column('roles')->pluck('name')->label('primary', 3);
                 $grid->column('roles')->pluck('name')->label('primary', 3);

+ 28 - 1
src/Traits/HasHtml.php

@@ -8,7 +8,7 @@ use DOMElement;
 
 
 trait HasHtml
 trait HasHtml
 {
 {
-    protected static $shouldResolveTags = ['style', 'script'];
+    protected static $shouldResolveTags = ['style', 'script', 'template', 'link'];
 
 
     /**
     /**
      * @param string|array $content
      * @param string|array $content
@@ -103,6 +103,33 @@ trait HasHtml
         return static::{$method}($element);
         return static::{$method}($element);
     }
     }
 
 
+    /**
+     * @param DOMElement $element
+     *
+     * @return void
+     */
+    protected static function resolveLink(DOMElement $element)
+    {
+        if ($element->getAttribute('rel') == 'stylesheet' && $href = $element->getAttribute('href')) {
+            static::css(admin_asset($href));
+        }
+    }
+
+    /**
+     * @param DOMElement $element
+     *
+     * @return void
+     */
+    protected static function resolveTemplate(DOMElement $element)
+    {
+        $html = '';
+        foreach ($element->childNodes as $childNode) {
+            $html .= $element->ownerDocument->saveHTML($childNode);
+        }
+
+        $html && static::html($html);
+    }
+
     /**
     /**
      * @param DOMElement $element
      * @param DOMElement $element
      *
      *