1

前言

前段时间研究了DetailView插件结合自定义字段的显示与编辑,DetailView插件本身没有这么灵活,要实现这种效果必须得改造插件。显示与编辑DetailView插件都有了,只需要我们把自定义的字段加进去就行可以实现了。但是怎么加进去呢?既然是自定义字段,那肯定是可有可无,不能写死的,我来分享下具体实现思路吧!

效果图

提交时间、昵称、联系人分类、性别属于自定义字段

图片描述

找插件源码

找插件的源码(vendor\kartik-v\yii2-detail-view\DetailView.php),可以通过它方法的命名或者打印测试找到相关的代码,知点击详细时走插件的如下方法:

 /**
     * Renders the main detail view widget
     *
     * @return string the detail view content
     */
    protected function renderDetailView()
    {  
        $rows = [];
        foreach ($this->attributes as $attribute) {
            $rows[] = $this->renderAttributeRow($attribute);
        }
        $tag = ArrayHelper::remove($this->options, 'tag', 'table');
        $output = Html::tag($tag, implode("\n", $rows), $this->options);
        return ($this->bootstrap && $this->responsive) ?
            '<div class="table-responsive kv-detail-view">' . $output . '</div>' :
            '<div class="kv-detail-view">' . $output . '</div>';
    }

重写类及其方法

1、类与方法都找到了,就重写。把DetailView类以及renderDetailView方法里涉及的方法、组件都拷贝过来重写一遍,并继承父类\kartik\detail\DetailView。

2、仿造父类的public $attributes;属性给子类增加public $custom;属性,原因是$attributes属性的字段与自定义字段走的不是同一个model,所以必须得定义一个新的属性来用。

3、重写完了之后代码如下(重点注意的部分我在代码中已加上注释):

<?php

namespace common\components\detailview;

use Yii;
use yii\helpers\ArrayHelper;
use kartik\helpers\Html;
use kartik\base\Config;

class DetailView extends \kartik\detail\DetailView
{
    public $custom;

    // inputs list
    private static $_inputsList = [
        self::INPUT_HIDDEN => 'hiddenInput',
        self::INPUT_TEXT => 'textInput',
        self::INPUT_PASSWORD => 'passwordInput',
        self::INPUT_TEXTAREA => 'textArea',
        self::INPUT_CHECKBOX => 'checkbox',
        self::INPUT_RADIO => 'radio',
        self::INPUT_LIST_BOX => 'listBox',
        self::INPUT_DROPDOWN_LIST => 'dropDownList',
        self::INPUT_CHECKBOX_LIST => 'checkboxList',
        self::INPUT_RADIO_LIST => 'radioList',
        self::INPUT_HTML5_INPUT => 'input',
        self::INPUT_FILE => 'fileInput',
        self::INPUT_WIDGET => 'widget',
    ];

    // dropdown inputs
    private static $_dropDownInputs = [
        self::INPUT_LIST_BOX => 'listBox',
        self::INPUT_DROPDOWN_LIST => 'dropDownList',
        self::INPUT_CHECKBOX_LIST => 'checkboxList',
        self::INPUT_RADIO_LIST => 'radioList',
    ];
    protected function renderDetailView()
    { 
        $rows = [];
        foreach ($this->attributes as $attribute) {
            $rows[] = $this->renderAttributeRow($attribute);
        }
        //注意点一:增加循环获取自定义字段的值
        foreach ($this->custom['attributes'] as $attribute) {
            $rows[] = $this->renderCustomAttributeRow($attribute);
        }
        $tag = ArrayHelper::remove($this->options, 'tag', 'table');
        $output = Html::tag($tag, implode("\n", $rows), $this->options);
        return ($this->bootstrap && $this->responsive) ?
            '<div class="table-responsive kv-detail-view">' . $output . '</div>' :
            '<div class="kv-detail-view">' . $output . '</div>';
    }
    //自定义字段的显示
    protected function renderCustomAttributeRow($attribute)
    {    
        //注意点二:只留这三行代码,把其它多余的删除
        $this->_rowOptions = ArrayHelper::getValue($attribute, 'rowOptions', $this->rowOptions);
        $content = $this->renderCustomAttributeItem($attribute);
        return Html::tag('tr', $content, $this->_rowOptions);
    }
    protected function renderCustomAttributeItem($attribute)
    {
        $labelColOpts = ArrayHelper::getValue($attribute, 'labelColOptions', $this->labelColOptions);
        $valueColOpts = ArrayHelper::getValue($attribute, 'valueColOptions', $this->valueColOptions);
        if (ArrayHelper::getValue($attribute, 'group', false)) {
            $groupOptions = ArrayHelper::getValue($attribute, 'groupOptions', []);
            $label = ArrayHelper::getValue($attribute, 'label', '');
            if (empty($groupOptions['colspan'])) {
                $groupOptions['colspan'] = 2;
            }
            return Html::tag('th', $label, $groupOptions);
        }
        if ($this->hideIfEmpty === true && empty($attribute['value'])) {
            Html::addCssClass($this->_rowOptions, 'kv-view-hidden');
        }
        if (ArrayHelper::getValue($attribute, 'type', 'text') === self::INPUT_HIDDEN) {
            Html::addCssClass($this->_rowOptions, 'kv-edit-hidden');
        }
        $value = $attribute['value'];
        if ($this->notSetIfEmpty && ($value === '' || $value === null)) {
            $value = null;
        }
        $dispAttr = $this->formatter->format($value, $attribute['format']);
        Html::addCssClass($this->viewAttributeContainer, 'kv-attribute');
        Html::addCssClass($this->editAttributeContainer, 'kv-form-attribute');
        $output = Html::tag('div', $dispAttr, $this->viewAttributeContainer) . "\n";
        //var_dump($this->editAttributeContainer);exit;
        if ($this->enableEditMode) {
          $editInput = ArrayHelper::getValue($attribute, 'displayOnly', false) ? $dispAttr : $this->renderFormCustomAttribute($attribute);
          $output .= Html::tag('div', $editInput, $this->editAttributeContainer);
        }
        return Html::tag('th', $attribute['label'], $labelColOpts) . "\n" . Html::tag('td', $output, $valueColOpts);
    }
    protected function renderFormCustomAttribute($config)
    {
        if (empty($config['attribute'])) {
            return '';
        }
        //注意点三:把这里的model注释掉,不能用,这是attribute的model
        
        //$model = ArrayHelper::getValue($config, 'editModel', $this->model);
        //if (!$model instanceof Model) {
            $model = $this->model;
        //}
        $attr = ArrayHelper::getValue($config, 'updateAttr', $config['attribute']);
        $input = ArrayHelper::getValue($config, 'type', self::INPUT_TEXT);
        $fieldConfig = ArrayHelper::getValue($config, 'fieldConfig', []);
        $inputWidth = ArrayHelper::getValue($config, 'inputWidth', '');
        $container = ArrayHelper::getValue($config, 'inputContainer', []);
        if ($inputWidth != '') {
            Html::addCssStyle($container, "width: {$inputWidth}"); // deprecated since v1.7.4
        }
        $template = ArrayHelper::getValue($fieldConfig, 'template', "{input}\n{error}\n{hint}");
        $row = Html::tag('div', $template, $container);
        if (static::hasGridCol($container)) {
            $row = '<div class="row">' . $row . '</div>';
        }
        $fieldConfig['template'] = $row;
        if (substr($input, 0, 8) == "\\kartik\\") {
            Config::validateInputWidget($input, 'as an input widget for DetailView edit mode');
        } elseif ($input !== self::INPUT_WIDGET && !in_array($input, self::$_inputsList)) {
            throw new InvalidConfigException(
                "Invalid input type '{$input}' defined for the attribute '" . $config['attribute'] . "'."
            );
        }
        $options = ArrayHelper::getValue($config, 'options', []);
        $widgetOptions = ArrayHelper::getValue($config, 'widgetOptions', []);
        $class = ArrayHelper::remove($widgetOptions, 'class', '');
        if (!empty($config['options'])) {
            $widgetOptions['options'] = $config['options'];
        }
        //return array("options"=> array("class"=> "form-group"));
        //return "<a>yyyy</a>";

        if (Config::isInputWidget($input)) {
            $class = $input;
            return $this->_form->field($model, $attr, $fieldConfig)->widget($class, $widgetOptions);
        }
        if ($input === self::INPUT_WIDGET) {
            if ($class == '') {
                throw new InvalidConfigException("Widget class not defined in 'widgetOptions' for {$input}'.");
            }
            return $this->_form->field($model, $attr, $fieldConfig)->widget($class, $widgetOptions);
        }
        if (in_array($input, self::$_dropDownInputs)) {
            $items = ArrayHelper::getValue($config, 'items', []);
            return $this->_form->field($model, $attr, $fieldConfig)->$input($items, $options);
        }
        if ($input == self::INPUT_HTML5_INPUT) {
            $inputType = ArrayHelper::getValue($config, 'inputType', self::INPUT_TEXT);
            return $this->_form->field($model, $attr, $fieldConfig)->$input($inputType, $options);
        }
        
        /*
            注意点四:把原来的返回值改写。至于为什么要改写成如下,原因有:
             1、用$this->_form->field()的话会走attribute的model,改写失败;
             2、可以参照$this->_form->field()的用法,然后查看原码生成功div、input的规律你就明白为什么要这么改写了。
        */
        $name = $this->custom['id']."[".$config['attribute']."]";
        $id   = strtolower($this->custom['id'])."-".$config['attribute'];
        $html = Html::input('text', $name, $config['value'], ['class' => "form-control",'id'=> $id]);
        $html .= Html::tag('div', "", ['class' => 'help-block']);
        $html = Html::tag('div', $html, []);
        $fieldclass = "field-".$id;
        $html = Html::tag('div', $html, ['class' => $fieldclass]);
        return $html;
    }
}

视图用custom属性

    <?= DetailView::widget([
        'id'=>'contact-view',
        
        'formOptions'=>[
            'id'=>'contact-form-view',
            'action'=>'/contact/update?id='.$model->id."&salesId=".$model->sale_id,
            'options'=>['data-form'=>'public-form-submit'],
         ],
        'model' => $model,
        'condensed'=>false,
        'hover'=>true,
        'mode'=>Yii::$app->request->get('edit')=='t' ? DetailView::MODE_EDIT : DetailView::MODE_VIEW,
        'panel'=>[
           'heading'=>$this->title,
           'type'=>DetailView::TYPE_INFO,
        ],
        'custom' => $custom, //custom属性放这
        'attributes' => $attributes,
        'deleteOptions'=>[
            'class'=>"prohibit deletet",
            'label'=>'<span class="glyphicon"></span>',
        ],
        'enableEditMode'=>true,
        'fadeDelay'=>0, //关闭动画
    ]) ?>

$custom的数组格式如下:

Array
(
    [id] => ClassForm
    [attributes] => Array
        (
            [0] => Array
                (
                    [attribute] => add_time
                    [value] => 
                    [label] => 提交时间
                    [format] => text
                )

            [1] => Array
                (
                    [attribute] => nickname
                    [value] => 测试姓名
                    [label] => 昵称
                    [format] => text
                )

            [2] => Array
                (
                    [attribute] => sort
                    [value] => 
                    [label] => 联系人分类
                    [format] => text
                )

            [3] => Array
                (
                    [attribute] => sex
                    [value] => 男
                    [label] => 性别
                    [format] => text
                )

        )

)

注意:如果编辑与保存刷新的页面分开写,那么记得也要在刷新的页面加上custom属性,不然保存刷新后加上的自定义字段就消失了。

总结分析

以上就是我要给大家分享的DetailView插件结合自定义字段的显示与编辑的实现过程。
搞程序,水很深,不常总结,将来更费劲。
(完)

相关资料

1、DetailView


zacklee
704 声望56 粉丝