|
|
<?php
/* * The MIT License * * Copyright 2019 Blobt. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */
namespace iron\grid;
use Closure; use Yii; use yii\base\InvalidConfigException; use yii\base\Model; use yii\helpers\Html; use yii\helpers\Json; use yii\helpers\Url; use yii\helpers\ArrayHelper; use yii\i18n\Formatter; use yii\widgets\BaseListView; use iron\web\GridViewAsset; use blobt\grid\DataColumn;
/** * @author Blobt * @email 380255922@qq.com * @created Aug 13, 2019 */ class GridView extends BaseListView {
/** * @var string 渲染列数据的类,默认是'yii\grid\DataColumn' */ public $dataColumnClass;
/** * @var array 表格说明的html属性 */ public $captionOptions = [];
/** * @var array 表格外层div的属性 */ public $options = ['class' => 'card'];
/** * @var array table的html属性 */ public $tableOptions = ['class' => 'table table-bordered table-striped dataTable'];
/** * @var array 表格头部html属性 */ public $headerRowOptions = [];
/** * @var array 表格脚部html属性 */ public $footerRowOptions = [];
/** * @var array|Cloure 表格每一行的html属性 * 这个参数除了可以是一个options数组外,还可以是一个匿名函数,该函数必须返回一个options数组, * 渲染每一行都会调用该函数 * 该函数必须遵循以下声明规则 * ```php * function ($model, $key, $index, $grid) * ``` * * - `$model`: 每行的模型 * - `$key`: id值 * - `$index`: [[dataProvider]]提供的索引号 * - `$grid`: GridView 对象 */ public $rowOptions = [];
/** * @var Closure an 一个匿名函数(结构和[[rowOptions]]一样),每行渲染前后都会被调用 */ public $beforeRow; public $afterRow;
/** * @var bool 是否显示表格头 */ public $showHeader = true;
/** * @var bool 是否显示表格脚 */ public $showFooter = false;
/** * @var bool 没有数据情况下是否显示 */ public $showOnEmpty = true;
/** * @var array|Formatter 用来格式化输出属性值 */ public $formatter;
/** * @var string 摘要的显示样式 * * * - `{begin}`: 开始条数 * - `{end}`: 结束条数 * - `{count}`: 显示条数 * - `{totalCount}`: 总记录条数 * - `{page}`: 显示分页 * - `{pageCount}`: 总分页数 * - `{select}`: 显示页数 */ public $summary = "{select} 显示{begin}~{end}条 共{totalCount}条";
/** * @var array 摘要的html属性 */ public $summaryOptions = ['class' => 'summary'];
/** * @var array 列配置数组. 数组每一项代表一个列,列数组可以包括class、attribute、format、label等。 * 例子: * ```php * [ * ['class' => SerialColumn::className()], * [ * 'class' => DataColumn::className(), //渲染用到的类,没一列都默认使用[[DataColumn]]渲染,所以这里可以忽略
* 'attribute' => 'name', //代表每一行的数据原
* 'format' => 'text', //输出的格式
* 'label' => 'Name', //label
* '' => '' * ], * ['class' => CheckboxColumn::className()], * ] * ``` * * 当然,也支持简写成这样:[[DataColumn::attribute|attribute]], [[DataColumn::format|format]], * 或 [[DataColumn::label|label]] options: `"attribute:format:label"`. * 所以上面例子的 "name" 列能简写成这样 : `"name:text:Name"`. * 甚至"format"和"label"都是可以不制定的,因为它们都有默认值。 * * 其实大多数情况下都可以使用简写: * * ```php * [ * 'id', * 'amount:currency:Total Amount', * 'created_at:datetime', * ] * ``` * * 当[[dataProvider]]提供active records, 且active record又和其它 active record建立了关联关系的, * 例如 the `name` 属性是 和 `author` 关联,那么你可以这样制定数据: * * ```php * // shortcut syntax
* 'author.name', * // full syntax
* [ * 'attribute' => 'author.name', * // ...
* ] * ``` */ public $columns = [];
/** * @var string 当单元格数据为空时候显示的内容。 */ public $emptyCell = ' ';
/** * @var string TODO:这个目前用来做页数选择,具体原理没有研究清楚 */ public $filterSelector = 'select[name="per-page"]';
/** * @var type */ public $filter;
/** * @var array 批量操作的选项 */ public $batch;
/** * @var string 表格的layout: * * - `{summary}`: 摘要. * - `{items}`: 表格项. * - `{pager}`: 分页. * - `{batch}`: 批量处理 */ public $layout = <<< HTML <div class="card-body"> <div id="example2_wrapper" class="dataTables_wrapper dt-bootstrap4"> <div class="row"> <div class="col-sm-12 col-md-6"> {batch} <a href="{url}/create" class="btn btn-default"><i class="fas fa-plus-square mr-2"></i>添加</a> <!-- <a href="#" data-url='export' class="export btn btn-default"><i class="fa fa-file-excel-o"></i>导出</a>--> <div class="btn-group"> <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><i class="fas fa-file-upload mr-2"></i>导出</button> <ul class="dropdown-menu" role="menu"> <li> <a class="dropdown-item export-page" href="#" data-url="{url}/export">本页</a></li> <li> <a class="dropdown-item export-all" href="#" data-url="{url}/export">全部</a></li> </ul> </div> <!-- <button type="button" id="export" class="btn btn-default"><i class="fa fa-file-excel-o"></i>导出</button>--> </div> <div class="col-sm-12 col-md-6"> {filter} </div> </div> <div class="row"> <div class="col-sm-12"> {items} </div> </div> <div class="row"> <div class="col-sm-12 col-md-5"> <div class="dataTables_length" id="example2_info" role="status" aria-live="polite"> {summary} </div> </div> <div class="col-sm-12 col-md-7"> <div class="dataTables_paginate paging_simple_numbers"> {pager} </div> </div> </div> </div> </div> HTML; public $batchTemplate = <<< HTML <div class="btn-group"> <button type="button" class="btn btn-default btn checkbox-toggle"><i class="far fa-square"></i></button> <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false">批量操作</button> <ul class="dropdown-menu" role="menu"> {items} </ul> </div> HTML;
/** * 初始化 grid view. * 初始化必须的属性和每个列对象 * @return */ public function init() { parent::init(); if ($this->formatter === null) { $this->formatter = Yii::$app->getFormatter(); } elseif (is_array($this->formatter)) { $this->formatter = Yii::createObject($this->formatter); } if (!$this->formatter instanceof Formatter) { throw new InvalidConfigException('The "formatter" property must be either a Format object or a configuration array.'); } $this->pager = [ 'options' => ['class' => ['justify-content-end', 'pagination']], 'linkOptions' => ['class' => 'page-link'], 'pageCssClass' => 'paginate_button page-item', 'disabledPageCssClass' => 'page-link disabled', 'firstPageLabel' => '«', 'prevPageLabel' => '‹', 'nextPageLabel' => '›', 'lastPageLabel' => '»',]; $this->initColumns(); }
public function run() { $view = $this->getView(); GridViewAsset::register($view); $this->registerGridJs(); $this->registerIcheckJs(); $this->registerConfirmJs(); $this->registerExportJs(); parent::run(); }
/** * 注册GridView Js */ protected function registerGridJs() { $options = Json::htmlEncode(['filterUrl' => Url::to(Yii::$app->request->url), 'filterSelector' => $this->filterSelector]); $id = $this->options['id']; $this->getView()->registerJs("jQuery('#$id').yiiGridView($options);"); }
/** * 注册icheck Js */ protected function registerIcheckJs() { $js = <<<SCRIPT $('.dataTable input[type="checkbox"]').iCheck({ checkboxClass: 'icheckbox_flat-blue', radioClass: 'iradio_flat-blue' }); $(".checkbox-toggle").click(function () { var clicks = $(this).data('clicks'); if (clicks) { //Uncheck all checkboxes
$(".dataTable input[type='checkbox']").iCheck("uncheck"); $(".far", this).removeClass("fa-check-square").addClass('fa-square'); } else { //Check all checkboxes
$(".dataTable input[type='checkbox']").iCheck("check"); $(".far", this).removeClass("fa-square").addClass('fa-check-square'); } $(this).data("clicks", !clicks); }); SCRIPT; $this->getView()->registerJs($js); }
/** * 注册批量操作js */ protected function registerBatchJs() { $js = <<<SCRIPT $("a.batch_item").click(function(){ var url = $(this).data("url"); var act = $(this).text(); var selected = []; $(".checked input").each(function(){ selected.push($(this).val()); }); if(selected.length > 0){ alertify.confirm('系统提示', "确定执行批量 '"+act+"' 操作?", function(){ $.ajax({ type: "POST", url: url, traditional:true, data:{ 'ids[]':selected}, dataType: "json", async:false }); window.location.reload(); },function(){ }); } return false; }) SCRIPT; $this->getView()->registerJs($js); }
protected function registerConfirmJs() { $js = <<<SCRIPT $("a[alertify-confirm]").click(function(){ var message = $(this).attr('alertify-confirm'); var url = $(this).attr('href'); var id = $(this).data('id'); alertify.confirm('系统提示', message,function(){ $.ajax({ type: "POST", url: url, traditional:true, data:{ id:id }, dataType: "json", async:false }); window.location.reload(); },function(){ }); return false; }); SCRIPT; $this->getView()->registerJs($js); }
protected function registerExportJs() { $js = <<<SCRIPT $("a.export-all").click(function(url){ var url = $(this).data("url"); if(!location.search){ window.location.replace(url+"?page-type=all"); }else{ window.location.replace(url+location.search+"&page-type=all"); } }); $("a.export-page").click(function(url){ var url = $(this).data("url")+location.search; if(!location.search){ window.location.replace(url+"?page-type=page"); }else{ window.location.replace(url+location.search+"&page-type=page"); } }); SCRIPT; $this->getView()->registerJs($js); }
/** * 渲染局部 * @return string|bool */ public function renderSection($name) { switch ($name) { case '{summary}': return $this->renderSummary(); case '{items}': return $this->renderItems(); case '{pager}': return $this->renderPager(); case '{sorter}': return $this->renderSorter(); case '{filter}': return $this->renderFilter(); case '{batch}': return $this->renderBatch(); case '{url}': return Yii::$app->request->url; default: return false; } }
/** * 渲染表格的html真实table * @return string */ public function renderItems() { $tableHeader = $this->showHeader ? $this->renderTableHeader() : false; $tableBody = $this->renderTableBody();
$content = array_filter([ $tableHeader, $tableBody ]);
return Html::tag('table', implode("\n", $content), $this->tableOptions); }
/** * 初始化每列 * @throws InvalidConfigException */ protected function initColumns() { if (empty($this->columns)) { throw new InvalidConfigException('The "columns" property must be set.'); }
foreach ($this->columns as $i => $column) { if (is_string($column)) { $column = $this->createDataColumn($column); } else { $column = Yii::createObject(array_merge([ 'class' => $this->dataColumnClass ?: DataColumn::className(), 'grid' => $this, ], $column)); } if (!$column->visible) { unset($this->columns[$i]); continue; }
$this->columns[$i] = $column; } }
/** * 渲染表头 * @return string */ public function renderTableHeader() { $cells = []; foreach ($this->columns as $column) { /* @var $column Column */ $cells[] = $column->renderHeaderCell(); }
$content = Html::tag('tr', implode('', $cells), $this->headerRowOptions); return "<thead>\n" . $content . "\n</thead>"; }
/** * 渲染表格体 * @return string */ public function renderTableBody() { $models = $this->dataProvider->getModels(); $keys = $this->dataProvider->getKeys(); $rows = [];
foreach ($models as $index => $model) { $key = $keys[$index];
if ($this->beforeRow !== null) { $row = call_user_func($this->beforeRow, $model, $key, $index, $this); if (!empty($row)) { $rows[] = $row; } }
$rows[] = $this->renderTableRow($model, $key, $index);
if ($this->afterRow !== null) { $row = call_user_func($this->afterRow, $model, $key, $index, $this); if (!empty($row)) { $rows[] = $row; } } }
if (empty($rows) && $this->emptyText !== false) { $colspan = count($this->columns);
return "<tbody>\n<tr><td colspan=\"$colspan\">" . $this->renderEmpty() . "</td></tr>\n</tbody>";
}
return "<tbody>\n" . implode("\n", $rows) . "\n</tbody>"; }
/** * 渲染表格的每行 * @param Objetc $model * @param int $key * @param int $index * @return string */ public function renderTableRow($model, $key, $index) { $cells = []; foreach ($this->columns as $column) { $cells[] = $column->renderDataCell($model, $key, $index); } if ($this->rowOptions instanceof Closure) { $options = call_user_func($this->rowOptions, $model, $key, $index, $this); } else { $options = $this->rowOptions; } $options['data-key'] = is_array($key) ? json_encode($key) : (string)$key;
//TODO 各行变色放到这里不合理
if ($index % 2 == 0) { $oddEven = 'odd'; } else { $oddEven = 'even'; }
if (isset($options['class'])) { $options['class'] += " " . $oddEven; } else { $options['class'] = $oddEven; }
return Html::tag('tr', implode('', $cells), $options); }
/** * 渲染摘要显示 * @return string */ public function renderSummary() { $count = $this->dataProvider->getCount(); if ($count <= 0) { return ''; } $summaryOptions = $this->summaryOptions; $tag = ArrayHelper::remove($summaryOptions, 'tag', 'div'); if (($pagination = $this->dataProvider->getPagination()) !== false) { $totalCount = $this->dataProvider->getTotalCount(); $begin = $pagination->getPage() * $pagination->pageSize + 1; $end = $begin + $count - 1; if ($begin > $end) { $begin = $end; } $page = $pagination->getPage() + 1; $pageCount = $pagination->pageCount; }
return Yii::$app->getI18n()->format($this->summary, [ 'begin' => $begin, 'end' => $end, 'count' => $count, 'totalCount' => $totalCount, 'page' => $page, 'pageCount' => $pageCount, 'select' => $this->renderCountSelect() ], Yii::$app->language); }
/** * 渲染批量操作 */ public function renderBatch() { if (empty($this->batch) && !is_array($this->batch)) { return ""; }
$this->registerBatchJs();
$items = ""; foreach ($this->batch as $item) { $items .= Html::tag('li', Html::a(Html::encode($item['label']), '#', ["data-url" => Html::encode($item['url']), "class" => "batch_item dropdown-item"])); }
return strtr($this->batchTemplate, [ "{items}" => $items ]); }
/** * 渲染表格的页数select * @return string */ protected function renderCountSelect() { $items = [ "20" => 20, "50" => 50, "100" => 100 ];
$per = "条/页";
$options = []; foreach ($items as $key => $val) { $options[$val] = "{$key}{$per}"; }
$perPage = !empty($_GET['per-page']) ? $_GET['per-page'] : 20; return Html::dropDownList('per-page', $perPage, $options, ["class" => "custom-select"]); }
/** * 渲染表格的筛选部分 * @return string */ protected function renderFilter() { return $this->filter; }
/** * 根据给定格式,创建一个 [[DataColumn]] 对象 * @param string $text DataColumn 格式 * @return DataColumn 实例 * @throws InvalidConfigException */ protected function createDataColumn($text) { if (!preg_match('/^([^:]+)(:(\w*))?(:(.*))?$/', $text, $matches)) { throw new InvalidConfigException('The column must be specified in the format of "attribute", "attribute:format" or "attribute:format:label"'); }
return Yii::createObject([ 'class' => $this->dataColumnClass ?: DataColumn::className(), 'grid' => $this, 'attribute' => $matches[1], 'format' => isset($matches[3]) ? $matches[3] : 'text', 'label' => isset($matches[5]) ? $matches[5] : null, ]); }
}
|