初始化仓库

This commit is contained in:
2024-09-19 11:32:39 +08:00
commit 8f5315e936
921 changed files with 76455 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
<?php
namespace admin\components\UI\Table;
use admin\components\UI\Tools;
use admin\components\UI\Widget\Link;
use admin\components\UI\Widget\Menu;
/**
* 操作动作
* Class Column
* @package backend\components\UI\Filter
*/
class Action
{
protected array $button = [];
protected array $menu = [];
/**
* 按钮
* @param string $name
* @param string $route
* @param array $params
* @param string $type
* @return Link
*/
public function button(string $name, string $route = '', array $params = [], string $type = 'primary'): Link
{
$link = new Link($name, $route, $params);
$link->button($type);
$this->button[] = $link;
return $link;
}
/**
* 菜单按钮
* @param string $name
* @param string $type
* @return Menu
*/
public function menu(string $name, string $type = 'default'): Menu
{
$menu = new Menu($name, $type);
$this->menu[] = $menu;
return $menu;
}
/**
* 渲染组件
* @return array
*/
public function render(): array
{
$node = [];
foreach ($this->menu as $menu) {
$node[] = $menu->getRender();
}
foreach ($this->button as $class) {
$node[] = $class->getRender();
}
return $node;
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace admin\components\UI\Table;
use admin\components\UI\Widget\Link;
use admin\components\UI\Form\Select;
/**
* 操作批量
* Class Column
* @package backend\components\UI\Filter
*/
class Batch
{
protected array $nodes = [];
protected array $select = [];
protected array $url = [];
/**
* 按钮
* @param string $name
* @param string $type
* @param string $route
* @param array $params
* @param string $btnType
* @return $this
*/
public function button(string $name, string $type = '', string $route = '', array $params = [], string $btnType = 'normal'): self
{
$params['bath_type'] = $type;
$url = route($route, $params);
$this->nodes[] = [
'nodeName' => 'a-button',
'type' => 'secondary',
'status' => $btnType,
'child' => $name,
'vOn:click' => "footer.checkAction('$url', '确定执行$name\操作?')"
];
return $this;
}
/**
* @param string $name
* @param string $route
* @param array $params
* @return $this
*/
public function select(string $name, string $route = '', array $params = []): self
{
$url = route($route, $params);
$this->select[] = [
'nodeName' => 'a-doption',
'child' => $name,
'vOn:click' => "footer.checkAction('$url' '确定执行$name\操作?')"
];
return $this;
}
/**
* 渲染组件
* @return array
*/
public function render(): array
{
if ($this->select) {
$this->nodes[] = [
'nodeName' => 'a-dropdown',
'child' => [
[
'nodeName' => 'a-button',
'type' => 'secondary',
'child' => '批量操作',
],
[
'vSlot:content' => '',
'nodeName' => 'div',
'child' => $this->select
]
],
];
}
return $this->nodes;
}
}

View File

@@ -0,0 +1,467 @@
<?php
namespace admin\components\UI\Table;
use Exception;
use Illuminate\Support\Collection;
use admin\components\UI\Widget\Link;
use admin\components\UI\Table;
use admin\components\UI\Tools;
/**
* 表格列
* Class Column
* @method static Column\Hidden hidden()
* @method static Column\Progress progress(string $color = 'default')
* @method static Column\Status status(array $map, array $color, string $type = 'badge')
* @method static Column\Chart chart(int $day = 7, string $has = 'viewsData', string $key = 'pv', string $name = '访问量', string $type = 'area')
* @method static Column\Tags tags(array $map, array $color)
* @method Column\Toggle toggle(string $field, string $url, array $params = [])
* @method static Column\Input input(string $field, $url, array $params = [])
* @package backend\components\UI\Table
*/
class Column
{
protected string $name;
protected string $label = '';
protected ?\Closure $callback = null;
protected array $node = [];
protected array $attr = [];
protected array $function = [];
protected array $children = [];
protected $width = '';
protected $align = '';
protected $fixed = '';
protected $class = [];
protected $replace = '';
protected ?int $colspan = null;
protected ?\Closure $show = null;
protected ?int $sort = null;
protected $sorter = null;
protected $layout;
protected $relation;
protected $model;
protected $element;
protected $extend;
/**
* Column constructor.
* @param string $name
* @param string $label
* @param null $callback
*/
public function __construct(string $name = '', string $label = '', $callback = null)
{
$this->name = $name;
$this->label = $label;
$this->callback = $callback;
}
/**
* 设置父级对象
* @param Table $layout
*/
public function setLayout(Table $layout): void
{
$this->layout = $layout;
}
/**
* 关联数据
* @param $relation
* @return $this
*/
public function setRelation($relation): self
{
$this->relation = $relation;
return $this;
}
/**
* 宽度
* @param $width
* @return $this
*/
public function width($width): self
{
$this->width = $width;
return $this;
}
/**
* 自定义节点数据
* @param $node
* @return $this
*/
public function node($node): self
{
$this->node = $node;
return $this;
}
/**
* 对齐
* @param string $align
* @return $this
* @throws Exception
*/
public function align(string $align): self
{
$this->align = $align;
return $this;
}
/**
* 固定列
* @param string $fixed
* @return $this
*/
public function fixed(string $fixed = 'right'): self
{
$this->fixed = $fixed;
return $this;
}
/**
* 设置样式类
* @param string $class
* @return $this
*/
public function class(string $class): self
{
$this->class[] = $class;
return $this;
}
/**
* 设置附加属性
* @param string $name
* @param $value
* @return $this
*/
public function attr(string $name, $value): self
{
$this->attr[$name] = $value;
return $this;
}
/**
* 设置颜色
* @param string $name
* @return $this
*/
public function color(string $name): self
{
$this->class[] = 'text-' . $name;
return $this;
}
/**
* 字符串替换标签(数字字符串处理使用)
* @param $replace
* @return $this
*/
public function replace($replace): self
{
$this->replace = $replace;
return $this;
}
/**
* 添加链接
* @param string $name
* @param string $route
* @param array $params
* @param bool $absolute
* @return Link
*/
public function link(string $name, string $route, array $params = [], bool $absolute = false): Link
{
if (!$this->element) {
$this->element = new Table\Column\Link();
$this->element->fields($this->layout->fields);
}
return $this->element->add($name, $route, $params, $absolute);
}
/**
* 添加菜单
* @param string $name
* @param string $route
* @param array $params
* @return Link
*/
public function menu(string $name, string $route, array $params = []): Link
{
if (!$this->element) {
$this->element = new Table\Column\Menu();
}
return $this->element->add($name, $route, $params);
}
/**
* 副标题
* @param string $label
* @param callable|null $callback
* @return $this
*/
public function desc(string $label, callable $callback = null): self
{
if (!$this->element && !$this->element instanceof Table\Column\RichText) {
$this->element = new Table\Column\RichText();
$this->element->setRelation($this->relation);
}
$this->element->desc($label, $callback);
return $this;
}
/**
* 图片显示
* @param string $label
* @param callable|null $callback
* @param int $width
* @param int $height
* @param string $placeholder
* @return $this
*/
public function image(string $label, callable $callback = null, int $width = 10, int $height = 10, string $placeholder = ''): self
{
if (!$this->element && !$this->element instanceof Table\Column\RichText) {
$this->element = new Table\Column\RichText();
}
$this->element->image($label, $width, $height, $placeholder, $callback);
return $this;
}
/**
* 格式化时间
* @param $format
* @return $this
*/
public function date($format): self
{
$this->function[] = [
'fun' => 'date',
'params' => $format
];
return $this;
}
/**
* 显示隐藏
* @param callable $callback
* @return $this
*/
public function show(callable $callback): self
{
$this->show = $callback;
return $this;
}
/**
* 列排序
* @param int $num
* @return $this
*/
public function sort(int $num): self
{
$this->sort = $num;
return $this;
}
/**
* 排序条件
*/
public function sorter($sorter = true): self
{
$this->sorter = $sorter;
return $this;
}
/**
* 列合并
* @param int $num
* @return $this
*/
public function colspan(int $num): self
{
$this->colspan = $num;
return $this;
}
// 分组表格
public function children(string $name = '', string $label = '', $callback = null): self
{
$this->children[] = new Column($name, $label, $callback);
return $this;
}
/**
* 获取字段名
* @return string
*/
public function getLabel(): string
{
return Tools::converLabel2($this->label, $this->relation);
}
/**
* 获取列配置
*/
public function getRender(): array
{
$render = $this->node;
if ($this->node instanceof \Closure) {
$render = call_user_func($this->node);
}
if ($this->element) {
$render = $this->element->render($this->getLabel());
}
$node = [
'title' => $this->name,
'dataIndex' => $this->label,
'width' => $this->width,
'className' => implode(' ', $this->class),
'colSpan' => $this->colspan,
'sort' => $this->sort,
'align' => $this->align,
];
if ($this->fixed) {
$node['fixed'] = $this->fixed;
}
if($this->replace){
$node['replace'] = $this->replace;
}
if ($this->children) {
$children = [];
foreach ($this->children as $item) {
$children[] = $item->getRender();
}
$node['children'] = $children;
}
if ($this->sorter) {
$node['vBind:sortable'] = 'colSortable';
}
if ($render) {
$node['render:rowData, rowIndex'] = $render;
}
return array_merge($node, $this->attr);
}
/**
* 行数据
* @param $rowData
* @return array
*/
public function getData($rowData): array
{
if ($this->relation) {
// 解析关联数组
$parsingData = Tools::parsingObjData($rowData, $this->relation, $this->label);
} else {
// 解析普通数组
$parsingData = Tools::parsingArrData($rowData, $this->label);
}
// 回调处理
if ($this->callback instanceof \Closure) {
$callback = call_user_func($this->callback, $parsingData, $rowData);
if ($callback) {
$parsingData = $callback;
}
} else {
$parsingData = $this->callback ?: $parsingData;
}
// 函数处理
if ($this->function) {
foreach ($this->function as $vo) {
if (function_exists($vo['fun'])) {
$parsingData = call_user_func($vo['fun'], $vo['params'], $parsingData);
}
}
}
if ($this->label) {
$data = [
$this->getLabel() => $parsingData
];
} else {
$data = [];
}
// 元素数据
if ($this->element && method_exists($this->element, 'getData')) {
$data = array_merge($data, $this->element->getData($rowData, $this->getLabel(), $parsingData));
}
if ($this->children) {
foreach ($this->children as $item) {
$data = array_merge($data, $item->getData($rowData));
}
}
return $data;
}
/**
* 列条件
* @param $query
* @return false|void
*/
public function execute($query)
{
$sort = \Yii::$app->request->get('_sort');
$value = $sort && $sort[$this->label] ? $sort[$this->label] : null;
if (!$this->sorter || $value === null) {
return false;
}
if ($this->sorter instanceof \Closure) {
call_user_func($this->sorter, $query, $value);
} else if ($this->sorter !== false) {
$query->orderBy(is_string($this->sorter) ? $this->sorter : $this->label, $value === 'desc' ? 'desc' : 'asc');
}
}
/**
* 执行元素处理
* @param callable $callback
* @return $this
*/
public function element(callable $callback){
$callback($this->element);
return $this;
}
/**
* @param $method
* @param $arguments
* @return $this
* @throws Exception
*/
public function __call($method, $arguments)
{
$class = 'backend\\components\\UI\\Table\\Column\\' . ucfirst($method);
if (!class_exists($class)) {
if (!$this->extend[$method]) {
throw new \Exception('There is no form method "' . $method . '"');
} else {
$class = $this->extend[$method];
}
}
$object = new $class(...$arguments);
if (method_exists($object, 'fields')) {
$object->fields($this->layout->fields);
}
$this->element = $object;
return $this;
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace admin\components\UI\Table\Column;
use admin\components\UI\Tools;
use admin\components\UI\yii\Arr;
use admin\components\UI\yii\Str;
/**
* Class Chart
*/
class Chart implements Component
{
private int $day;
private string $has;
private string $key;
private string $name;
private string $type;
/**
* Chart constructor.
* @param int $day
* @param string $has
* @param string $key
* @param string $name
* @param string $type
*/
public function __construct(int $day = 7, string $has = 'viewsData', string $key = 'pv', string $name = '访问量', string $type = 'area')
{
$this->day = $day;
$this->has = $has;
$this->key = $key;
$this->name = $name;
$this->type = $type;
}
/**
* @param $value
* @param $data
* @return string
*/
public function render($value, $data): string
{
$chartData = $data->{$this->has};
$tmpData = [];
$chartData->each(function ($item) use (&$tmpData) {
$tmpData[$item->date] += $item[$this->key];
});
$tmpChart = [];
foreach ($tmpData as $key => $vo) {
$tmpChart[] = [
"value" => $vo,
"label" => $key,
"name" => $this->name
];
}
$viewId = 'chart-chart-' . Str::random(5);
$chart = (new \backend\components\Util\ApexCharts)->{$this->type}($tmpChart)->type('day', [
'start' => date('Y-m-d', strtotime('-' . $this->day . ' day')),
])->render($viewId, function ($config) {
Arr::set($config, 'chart.height', 35);
Arr::set($config, 'chart.sparkline.enabled', true);
Arr::set($config, 'chart.animations.enabled', false);
Arr::set($config, 'tooltip.enabled', false);
return $config;
});
return "<div id='$viewId' class='p-1'></div> $chart";
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace admin\components\UI\Table\Column;
/**
* 组件接口
* Class Component
* @package backend\components\UI
*/
interface Component
{
public function render($label): array;
}

View File

@@ -0,0 +1,42 @@
<?php
namespace admin\components\UI\Table\Column;
/**
* Class Hidden
*/
class Hidden implements Component
{
/**
* @param $label
* @return array
*/
public function render($label): array
{
$name = $label . '_show';
return [
'nodeName' => 'div',
'child' => [
[
'nodeName' => 'span',
'class' => 'mr-2',
'vIf' => "rowData.record['$name']",
'child' => "{{rowData.record['$label']}}",
],
[
'nodeName' => 'a-button',
'shape' => 'round',
'size' => 'mini',
'type' => 'outline',
'vBind:status' => "rowData.record['$name'] ? 'warning' : ''",
'class' => 'mr-2',
'vOn:click' => "rowData.record['$name'] = !rowData.record['$name']",
'child' => "{{rowData.record['$name'] ? '隐藏' : '显示'}}",
],
]
];
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace admin\components\UI\Table\Column;
use admin\components\UI\Widget\Badge;
/**
* Class Images
*/
class Images implements Component
{
private int $size;
/**
* Tags constructor.
*/
public function __construct(int $size = 3)
{
$this->size = $size;
}
/**
* @param $label
* @return array
*/
public function render($label): array
{
$node = [
'nodeName' => 'div',
'vFor' => "item in rowData.record['$label']",
'class' => "flex-none bg-cover w-{$this->size} h-{$this->size}",
'vBind:style' => "{'background-image': 'url(' + item + ')'}"
];
return [
'nodeName' => 'div',
'class' => 'flex gap-2',
'child' => $node
];
}
public function getData($rowData, $field, $value): array
{
return [$field => !is_array($value) ? json_decode($value, true) : $value];
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace admin\components\UI\Table\Column;
use admin\components\UI\Tools;
/**
* Class Input
*/
class Input implements Component
{
private string $route;
private array $params;
private string $field;
private array $fields;
/**
* @param string $field
* @param string $route
* @param array $params
*/
public function __construct(string $field, string $route, array $params = [])
{
$this->field = $field;
$this->params = $params;
$this->route = $route;
}
/**
* @param array $fields
* @return $this
*/
public function fields(array $fields = []): Input
{
$this->fields = $fields;
return $this;
}
/**
* @param $label
* @return array
*/
public function render($label): array
{
$url = app_route($this->route, $this->params, false, 'rowData.record', $this->fields);
return [
'nodeName' => 'n-input',
'class' => 'shadow-sm',
'vModel:value' => "rowData.record['$label']",
'vOn:blur' => "editValue($url, {'field': '$this->field', '$this->field': rowData.record['$label']})",
];
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace admin\components\UI\Table\Column;
use admin\components\UI\Tools;
/**
* Class Link
*/
class Link implements Component
{
private array $link;
private array $fields;
/**
* @param array $fields
* @return $this
*/
public function fields(array $fields = []): Link
{
$this->fields = $fields;
return $this;
}
/**
* 添加条目
* @param string $name
* @param string $route
* @param array $params
* @param bool $absolute
* @return \backend\components\UI\Widget\Link
*/
public function add(string $name, string $route, array $params = [], bool $absolute = false): \backend\components\UI\Widget\Link
{
$link = new \backend\components\UI\Widget\Link($name, $route, $params, $absolute);
$link->fields($this->fields);
$link = $link->model('rowData.record');
$this->link[] = $link;
return $link;
}
/**
* @param $label
* @return array
*/
public function render($label): array
{
$link = [];
foreach ($this->link as $class) {
$type = $class->getType();
$data = $class->render();
if (($type === 'ajax' || $type === 'dialog') && (!isset($data['vBind:before']) || !isset($data['before']))) {
$data['vBind:before'] = "() => rowData.record.__loading = true";
}
if (($type === 'ajax' || $type === 'dialog') && (!isset($data['vBind:after']) || !isset($data['after']))) {
$data['vBind:after'] = "() => rowData.record.__loading = false";
}
$link[] = [
'nodeName' => 'span',
'child' => $data
];
}
$link = array_filter($link);
return [
'nodeName' => 'a-spin',
'vBind:loading' => 'rowData.record.__loading',
'child' => [
'nodeName' => 'div',
'class' => 'inline-flex gap-2',
'child' => $link
]
];
}
}

View File

@@ -0,0 +1,105 @@
<?php
namespace admin\components\UI\Table\Column;
use admin\components\UI\Tools;
/**
* Class Menu
*/
class Menu implements Component
{
private array $link;
private array $routes = [];
/**
* 添加条目
* @param string $name
* @param string $route
* @param array $params
* @return \backend\components\UI\Widget\Link
*/
public function add(string $name, string $route, array $params = []): \backend\components\UI\Widget\Link
{
$label = $route . '?' . http_build_query($params);
$link = new \backend\components\UI\Widget\Link($name, $route, $params);
$link = $link->model('rowData');
$this->link[] = $link;
$this->routes[$label] = [
'route' => $route,
'params' => $params
];
return $link;
}
/**
* 获取数据
* @param $rowData
* @return array
*/
public function getData($rowData): array
{
$urls = [];
foreach ($this->routes as $key => $vo) {
$params = [];
foreach ($vo['params'] as $k => $v) {
$params[$k] = Tools::parsingArrData($rowData, $v, true);
}
$urls[$key] = route($vo['route'], $params, false);
}
return $urls;
}
/**
* @param $label
* @return array
*/
public function render($label): array
{
$options = [];
foreach ($this->link as $key => $class) {
$data = $class->render();
$route = [
'nodeName' => 'route',
'type' => $data['type'],
'title' => $data['title'],
'href' => $class->getRoute(),
];
$options[] = [
'label' => $data['name'],
'key' => $key,
'route' =>$route,
];
}
$options = array_filter($options);
return [
'nodeName' => 'n-dropdown',
'width' => '80',
'placement' => 'right-start',
'overlap' => true,
'trigger' => 'click',
'options' => $options,
'render-label:option' => [
'nodeName' => 'route',
'class' => 'block',
'vBind:href' => 'rowData.record[option.route.href]',
'vBind:title' => 'option.route.title',
'vBind:type' => 'option.route.type',
'child' => '{{option.label}}'
],
'child' => [
'nodeName' => 'n-icon',
'class' => 'cursor-pointer',
'size' => 16,
'child' => [
'nodeName' => 'dots-vertical-icon'
]
],
];
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace admin\components\UI\Table\Column;
/**
* Class Progress
*/
class Progress implements Component
{
private string $color;
/**
* @param string $color
*/
public function __construct(string $color = 'default')
{
$this->color = $color;
}
/**
* @param $label
* @return array
*/
public function render($label): array
{
return (new \backend\components\UI\Widget\Progress("rowData.record['$label']"))->color($this->color)->render();
}
}

View File

@@ -0,0 +1,153 @@
<?php
namespace admin\components\UI\Table\Column;
use admin\components\UI\Tools;
/**
* Class RichText
*/
class RichText implements Component
{
private array $desc = [];
private array $image = [];
protected $relation;
/**
* 设置关系
* @param $relation
* @return $this
*/
public function setRelation($relation = null): self
{
$this->relation = $relation;
return $this;
}
/**
* @param string $label
* @param callable|null $callback
* @return $this
*/
public function desc(string $label, ?callable $callback = null): self
{
$this->desc[] = ['label' => $label, 'callback' => $callback];
return $this;
}
/**
* @param string $label
* @param int $width
* @param int $height
* @param string $placeholder
* @param callable|null $callback
* @return $this
*/
public function image(string $label, int $width = 10, int $height = 10, string $placeholder = '', ?callable $callback = null): self
{
$this->image[] = [
'label' => $label,
'width' => $width,
'height' => $height,
'placeholder' => $placeholder,
'callback' => $callback
];
return $this;
}
/**
* @param $rowData
* @return array
*/
public function getData($rowData): array
{
$data = [];
foreach ($this->image as $vo) {
if ($this->relation) {
// 解析关联数组
$url = Tools::parsingObjData($rowData, $this->relation, $vo['label']);
} else {
// 解析普通数组
$url = Tools::parsingArrData($rowData, $vo['label'], true);
}
if ($vo['callback'] instanceof \Closure) {
$url = call_user_func($vo['callback'], $url, $rowData);
}
if (filter_var($url, FILTER_VALIDATE_URL) === false) {
$url = route('service.image.placeholder', ['w' => 100, 'h' => 100, 't' => $vo['placeholder'] ?: '暂无']);
}
$data[Tools::converLabel($vo['label'], $this->relation)] = $url;
}
foreach ($this->desc as $key => $vo) {
if ($this->relation) {
// 解析关联数组
$var = Tools::parsingObjData($rowData, $this->relation, $vo['label']);
} else {
// 解析普通数组
$var = Tools::parsingArrData($rowData, $vo['label']);
}
if ($vo['callback'] instanceof \Closure) {
$var = call_user_func($vo['callback'], $var, $rowData);
}
$data[Tools::converLabel($vo['label'], $this->relation)] = $var;
}
return $data;
}
/**
* @param $label
* @return array
*/
public function render($label): array
{
$imageNode = [];
if ($this->image) {
foreach ($this->image as $vo) {
$itemLabel = Tools::converLabel2($vo['label']);
$imageNode[] = [
'nodeName' => 'div',
'class' => "flex-none bg-cover w-{$vo['width']} h-{$vo['height']}",
'vBind:style' => "{'background-image': 'url(' + rowData.record['$itemLabel'] + ')'}"
];
}
}
$descNode = [];
if ($this->desc) {
foreach ($this->desc as $vo) {
$itemLabel = Tools::converLabel2($vo['label']);
$descNode[] = [
'nodeName' => 'div',
'class' => "text-gray-500 overflow-ellipsis max-w-md",
'child' => "{{rowData.record['$itemLabel']}}"
];
}
}
return [
'nodeName' => 'div',
'class' => 'flex items-center gap-2',
'child' => [
...$imageNode,
[
'nodeName' => 'div',
'class' => 'flex-grow',
'child' => [
[//注释是因为图片显示会把url也显示
'nodeName' => 'div',
'class' => 'overflow-ellipsis max-w-md',
'child' => "{{rowData.record['$label']}}"
],
...$descNode
]
]
]
];
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace admin\components\UI\Table\Column;
use admin\components\UI\Widget\Badge;
/**
* Class Status
*/
class Status implements Component
{
private array $map;
private array $color;
private string $type;
/**
* @param array $map
* @param array $color
* @param string $type
*/
public function __construct(array $map, array $color, string $type = 'badge')
{
$this->map = $map;
$this->color = $color;
$this->type = $type;
}
/**
* @param $label
* @return array
*/
public function render($label): array
{
$statusArr = [];
foreach ($this->map as $key => $vo) {
$statusArr[$key]['name'] = $vo;
$statusArr[$key]['color'] = $this->color[$key] ?? 'blue';
}
$node = [];
foreach ($statusArr as $key => $vo) {
if ($this->type === 'badge') {
$item = (new Badge($vo['name']))->color($vo['color'])->render();
$item['vIf'] = "rowData.record['{$label}'] == " . (is_numeric($key) ? $key : "'$key'");
} else {
$item = [
'nodeName' => 'div',
'class' => 'text-' . $vo['color'] . '-900',
'child' => $vo['name']
];
}
$node[] = $item;
}
return $node;
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace admin\components\UI\Table\Column;
use admin\components\UI\Widget\Badge;
/**
* Class Tags
*/
class Tags implements Component
{
private array $map;
private array $color;
/**
* Tags constructor.
* @param array $map
* @param array $color
*/
public function __construct(array $map, array $color)
{
$this->map = $map;
$this->color = $color;
}
/**
* @param $label
* @return array
*/
public function render($label): array
{
$tagsArr = [];
foreach ($this->map as $key => $vo) {
$tagsArr[$key]['name'] = $vo;
}
foreach ($this->color as $key => $vo) {
$tagsArr[$key]['color'] = $vo;
}
$node = [];
foreach ($tagsArr as $key => $vo) {
$item = (new Badge($vo['name']))->color($vo['color'])->render();
$item['vIf'] = "~rowData.record['{$label}'].indexOf(". (is_numeric($key) ? $key : "'$key'") .")";
$node[] = $item;
}
return [
'nodeName' => 'div',
'class' => 'flex gap-2',
'child' => $node
];
}
public function getData($rowData, $field, $value): array
{
return [$field => !is_array($value) ? explode(',', $value) : $value];
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace admin\components\UI\Table\Column;
use admin\components\UI\Tools;
/**
* Class Toggle
*/
class Toggle implements Component
{
private string $route;
private array $params;
private string $field;
private array $fields;
private $checkedValue = 1;
private $uncheckedValue = 0;
/**
* @param string $field
* @param string $route
* @param array $params
*/
public function __construct(string $field, string $route, array $params = [])
{
$this->field = $field;
$this->params = $params;
$this->route = $route;
}
/**
* 设置列字段
* @param array $fields
* @return $this
*/
public function fields(Array $fields = []): self
{
$this->fields = $fields;
return $this;
}
/**
* 开关数据
* @param string|number|boolean $checkedValue
* @param string|number|boolean $uncheckedValue
* @return $this
*/
public function data($checkedValue,$uncheckedValue){
$this->checkedValue = $checkedValue;
$this->uncheckedValue = $uncheckedValue;
return $this;
}
/**
* @param $label
* @return string[]
*/
public function render($label): array
{
$url = app_route($this->route, $this->params, false, 'rowData.record', $this->fields);
return [
'nodeName' => 'a-switch',
'vModel:model-value' => "rowData.record['$label']",
'vOn:change' => "rowData.record['$label'] = \$event, editValue($url, {'field': '$this->field', '$this->field': rowData.record['$label']})",
'checkedValue' => $this->checkedValue,
'uncheckedValue' => $this->uncheckedValue
];
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace admin\components\UI\Table;
use Closure;
use admin\components\Util\Excel;
use admin\components\UI\Tools;
/**
* 数据导出
* Class Column
* @package backend\components\UI\Filter
*/
class Export
{
public array $column = [];
public string $title = '数据导出';
public string $subtitle = '';
/**
* 标题
* @param $name
* @return $this
*/
public function title($name): self
{
$this->title = $name;
return $this;
}
/**
* 副标题
* @param $name
* @return $this
*/
public function subtitle($name): self
{
$this->subtitle = $name;
return $this;
}
/**
* 列设置
* @param string $name
* @param string|Closure $value
* @param int $width
* @return $this
*/
public function column(string $name, $value, int $width = 10): self
{
$this->column[] = [
'name' => $name,
'value' => $value,
'width' => $width
];
return $this;
}
/**
* 输出表单
* @param $data
*/
public function render($data): void
{
$header = [];
$cellData = [];
foreach ($data as $vo) {
$tmp = [];
foreach ($this->column as $column) {
if (is_string($column['value'])) {
$tmp[] = Tools::parsingArrData($vo, $column['value']);
} else {
$tmp[] = call_user_func($column['value'], $vo);
}
}
$cellData[] = $tmp;
}
foreach ($this->column as $vo) {
$header[] = [
'name' => $vo['name'],
'width' => $vo['width']
];
}
Excel::export($this->title, $this->subtitle, $header, $cellData);
}
}

View File

@@ -0,0 +1,291 @@
<?php
namespace admin\components\UI\Table;
use admin\components\UI\Form\Cascader;
use admin\components\UI\Form\Date;
use admin\components\UI\Form\Daterange;
use admin\components\UI\Form\Datetime;
use admin\components\UI\Form\Select;
use admin\components\UI\Form\Text;
use admin\components\UI\Table;
/**
* 表格筛选
* Class Column
* @package backend\components\UI\Filter
*/
class Filter
{
protected string $name;
protected string $field;
protected $default;
protected $where = true;
protected Table $layout;
protected string $type = '';
protected string $placeholder = '';
protected bool $quick = false;
protected string $condition = '';
protected ?\Closure $callback;
protected $data;
protected $model;
protected $value;
protected $filterLayout;//rows
/**
* Filter constructor.
* @param string $name
* @param string $field
* @param bool $where
* @param null $default
*/
public function __construct(string $name, string $field, $where = true, $default = null)
{
$this->name = $name;
$this->field = $field;
$this->where = $where;
$this->default = $default;
$this->value = \Yii::$app->request->get($field, $this->default);
}
/**
* 设置父级对象
* @param Table $layout
*/
public function setLayout(Table $layout): void
{
$this->layout = $layout;
$this->model = $layout->model();
}
/**
* 级联选择
* @param callable|array $data
* @param callable|null $callback
* @return $this
*/
public function cascader($data = [], callable $callback = NULL): self
{
$this->data = $data;
$this->type = 'cascader';
$this->callback = $callback;
return $this;
}
/**
* 下拉框
* @param callable|array $data
* @param callable|null $callback
* @return $this
*/
public function select($data = [], callable $callback = NULL): self
{
$this->data = $data;
$this->type = 'select';
$this->callback = $callback;
return $this;
}
/**
* 文本库
* @param string $placeholder
* @param callable|null $callback
* @return $this
*/
public function text(string $placeholder = '', callable $callback = NULL): self
{
$this->placeholder = $placeholder;
$this->type = 'text';
$this->callback = $callback;
return $this;
}
/**
* 日期
* @param string $placeholder
* @param callable|null $callback
* @return $this
*/
public function date(string $placeholder = '', callable $callback = NULL): self
{
$this->placeholder = $placeholder;
$this->type = 'date';
$this->callback = $callback;
return $this;
}
/**
* 日期时间
* @param string $placeholder
* @param callable|null $callback
* @return $this
*/
public function datetime(string $placeholder = '', callable $callback = NULL): self
{
$this->placeholder = $placeholder;
$this->type = 'datetime';
$this->callback = $callback;
return $this;
}
/**
* 日期范围
* @param string $placeholder
* @param callable|null $callback
* @return $this
*/
public function daterange(string $placeholder = '', callable $callback = NULL): self
{
$this->placeholder = $placeholder;
$this->type = 'daterange';
$this->callback = $callback;
return $this;
}
/**
* 快捷筛选
* @return $this
*/
public function quick(): self
{
$this->quick = true;
return $this;
}
/**
* 筛选条件
* @param $type
* @return $this
*/
public function condition($type): self
{
$this->condition = $type;
return $this;
}
/**
* 执行筛选
* @param $query
* @return false
*/
public function execute($query): bool
{
if ($this->value === null) {
return false;
}
if (is_array($this->value) && empty($this->value)) {
return false;
}
if ($this->where instanceof \Closure) {
call_user_func($this->where, $query, $this->value, $this->data);
} elseif ($this->where !== false) {
$field = is_string($this->where) ? $this->where : $this->field;
$condition = '=';
$value = $this->value;
if ($this->condition === 'like') {
$condition = 'like';
$value = '%' . $value . '%';
}
$query->where($field, $condition, $value);
}
return true;
}
/**
* 渲染组件
* @return array
*/
public function render(): array
{
if (!$this->type) {
$this->layout->filterParams($this->field, $this->value);
return [];
}
switch ($this->type) {
case 'select':
$object = new Select($this->name, $this->field, $this->data);
$object->tip(true);
break;
case 'cascader':
$object = new Cascader($this->name, $this->field, $this->data);
break;
case 'date':
$object = new Date($this->name, $this->field);
break;
case 'datetime':
$object = new Datetime($this->name, $this->field);
break;
case 'daterange':
$object = new Daterange($this->name, $this->field);
break;
case 'text':
default:
$object = new Text($this->name, $this->field);
}
$object->model('data.filter.');
$this->layout->filterParams($this->field, $this->value);
if ($this->callback instanceof \Closure) {
call_user_func($this->callback, $object);
}
$data = [
'status' => $this->value !== null,
'quick' => $this->quick,
'where' => $this->where,
'value' => $this->value,
'field' => $this->field,
'data' => $this->data,
'name' => $this->name
];
if($this->layout->filterLayout=='rows'){
$data['render'] = [
'nodeName'=>'a-col',
"span"=>"6",
'child'=>[
'nodeName'=>'a-form-item',
'field'=>$this->field,
'label'=>$this->name,
'child' => $object->placeholder($this->placeholder)->getRender()
]
];
return $data;
}
if ($this->quick) {
$data['render'] = [
'nodeName' => 'div',
'class' => 'lg:w-40',
'child' => $object->placeholder($this->placeholder)->getRender()
];
} else {
$data['render'] = [
'nodeName' => 'div',
'class' => 'my-2',
'child' => [
[
'nodeName' => 'div',
'child' => $this->name,
],
[
'nodeName' => 'div',
'class' => 'mt-2',
'child' => $object->placeholder($this->placeholder)->getRender()
]
],
];
}
return $data;
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace admin\components\UI\Table;
use admin\components\UI\Form\Date;
use admin\components\UI\Form\Daterange;
use admin\components\UI\Form\Datetime;
use admin\components\UI\Form\Select;
use admin\components\UI\Form\Text;
use admin\components\UI\Table;
use admin\components\UI\Widget\Icon;
/**
* 类型筛选
* Class Column
* @package backend\components\UI\Filter
*/
class FilterType
{
protected string $name;
protected ?\Closure $callback;
protected ?\Closure $where;
protected string $icon = '';
protected ?int $num = null;
protected ?Table $layout;
protected $model;
protected $value;
/**
* @param string $name
* @param callable|null $where
* @param int $value
*/
public function __construct(string $name, callable $where = null, int $value = 0)
{
$this->name = $name;
$this->where = $where;
$this->value = $value;
}
public function setLayout(Table $layout): void
{
$this->layout = $layout;
$this->model = $layout->model();
}
public function num($num = 0): self
{
$this->num = $num;
return $this;
}
public function icon($content): self
{
$this->icon = $content;
return $this;
}
public function execute($query, $key): void
{
if ($this->where instanceof \Closure && $this->value == $key) {
call_user_func($this->where, $this->model);
}
}
/**
* @param $key
* @return array
*/
public function render($key): array
{
return [
'nodeName' => 'a-radio',
'value' => $key,
'child' => [
$this->icon ? [
'nodeName' => $this->icon
]: [],
[
'nodeName' => 'span',
'child' => ' ' . $this->name
]
]
];
}
}

View File

@@ -0,0 +1,591 @@
<?php
namespace admin\components\UI\Table;
use admin\components\UI\Form\Cascader;
use admin\components\UI\Tools;
use admin\components\UI\Widget\Icon;
/**
* 表格节点
* @package backend\components\UI\Table
*/
class Node
{
private string $url;
private ?string $key;
private ?string $title;
private bool $tree = false;
private bool $urlBind = true;
private string $class = '';
private array $params = [];
private array $data = ['show'=>[],'filter'=>[]];
private array $columns = [];
private array $expand = [];
private array $filter = [];
private array $quickFilter = [];
private array $action = [];
private array $bath = [];
private array $page = ['left'=>[],'right'=>[]];
private array $type = [];
private array $side = ['left'=>[],'right'=>[]];
private array $sideSize = ['left'=>'','right'=>''];
private array $header = [];
private array $footer = [];
private array $script = [];
private array $scriptReturn = [];
private array $scriptData = [];
private ?string $eventName = null;
private $filterLayout =null;
/**
* Node constructor.
* @param string $url
* @param string $key
* @param string|null $title
*/
public function __construct(string $url, string $key, ?string $title = '')
{
$this->url = $url;
$this->key = $key;
$this->title = $title;
}
/**
* @param $urlBind
* @return $this
*/
public function urlBind($urlBind): self
{
$this->urlBind = $urlBind;
return $this;
}
/**
* @param $class
* @return $this
*/
public function class($class): self
{
$this->class .= ' ' . $class;
return $this;
}
/**
* @param $params
* @return $this
*/
public function params($params): self
{
$this->params = $params;
return $this;
}
/**
* 附加参数
* @param $key
* @param $value
* @return $this
*/
public function nParams($key,$value){
$this->params[$key] = $value;
return $this;
}
/**
* @param array $filter
* @return $this
*/
public function data(array $filter): self
{
$this->data['filter'] = $filter;
return $this;
}
/**
* @param $content
* @param $return
* @return $this
*/
public function script($content, $return): self
{
if ($content instanceof \Closure) {
$this->script[] = $content();
} else {
$this->script[] = $content;
}
$this->scriptReturn[] = $return;
return $this;
}
/**
* @param $data
* @return $this
*/
public function scriptData($data): self
{
$this->scriptData = array_merge($this->scriptData, $data);
return $this;
}
/**
* @param $config
* @return $this
*/
public function tree($config): self
{
$this->tree = $config;
return $this;
}
/**
* @param array $node
* @return $this
*/
public function columns(array $node): self
{
$this->columns = $node;
return $this;
}
/**
* @param array $node
* @return $this
*/
public function expand(array $node): self
{
$this->expand = $node;
return $this;
}
/**
* @param array $node
* @return $this
*/
public function type(array $node): self
{
$this->type = $node;
return $this;
}
/**
* @param array $node
* @return $this
*/
public function filter(array $node): self
{
$this->filter = array_values(array_filter($node));
return $this;
}
public function filterLayout($l):self{
$this->filterLayout =$l;
return $this;
}
/**
* @param array $node
* @return $this
*/
public function quickFilter(array $node): self
{
if ($node) {
$this->quickFilter = $node;
}
return $this;
}
/**
* @param array $node
* @return $this
*/
public function action(array $node): self
{
$this->action = $node;
return $this;
}
/**
* @param array $node
* @return $this
*/
public function bath(array $node): self
{
$this->bath = $node;
return $this;
}
/**
* @param string|null $eventName
* @return $this
*/
public function eventName(?string $eventName): self
{
$this->eventName = $eventName;
return $this;
}
/**
* @param $node
* @param string $type
* @param false $resize
* @param string $width
* @return $this
*/
public function side($node, string $type = 'left', bool $resize = false, string $width = '100px'): self
{
$this->side[$type] = is_callable($node) ? $node() : $node;
if ($resize) {
$this->sideSize[$type] = $width;
}
return $this;
}
/**
* @param $node
* @param string $type
* @return $this
*/
public function page($node, string $type = 'left'): self
{
$this->page[$type] = is_callable($node) ? $node() : $node;
return $this;
}
/**
* @param $node
* @return $this
*/
public function header($node): self
{
$this->header = $node;
return $this;
}
/**
* @param $node
* @return $this
*/
public function footer($node): self
{
$this->footer = $node;
return $this;
}
/**
* @return array[]
*/
private function headNode(): array
{
if($this->filterLayout=='rows'){
$ret[] = [
'nodeName'=>'a-row',
'child'=>[
[
'nodeName'=>'a-col',
'child'=>[
'nodeName' => 'a-form',
"label-align"=>"left",
"vBind:label-col-props"=>"{'span':'8'}",
"vBind:wrapper-col-props"=>"{'span':'16'}",
'child'=>[
[
'nodeName'=>'a-row',
"vBind:gutter"=>"16",
'child' => $this->filter
],
]
]
],
]
];
if(!empty($this->action)){
if(!empty($this->filter)){
$ret[] = [
'nodeName'=>'a-divider',
'style'=>'margin-top: 0',
];
}
$ret[] = [
'nodeName'=>'a-row',
"style"=>"margin-bottom: 16px",
'child'=>[
'nodeName' => 'a-col',
'vBind:span'=>'16',
'child'=>[
'nodeName' => 'a-space',
'child'=>$this->action
]
]
];
}
return $ret;
}
if ($this->filter) {
$this->quickFilter[] = [
'nodeName' => 'a-trigger',
'position' => 'br',
'trigger' => 'click',
'child' => [
[
'nodeName' => 'a-button',
'type' => 'secondary',
'child' => [
'筛选',
[
'vSlot:icon' => '',
'nodeName' => 'icon-filter',
]
]
],
[
'vSlot:content' => '',
'nodeName' => 'div',
'class' => 'flex flex-col rounded shadow bg-white dark:bg-blackgray-1 dark:text-gray-400 p-2 w-56',
'child' => $this->filter
]
]
];
}
$header=[];
$ret = [];
if ($this->type) {
$header = [
'nodeName' => 'a-radio-group',
'name' => 'type',
'type' => 'button',
'vModel:modelValue' => 'data.filter.type',
'child' => $this->type
];
$ret[] = [
'nodeName' => 'div',
'class' => 'flex-grow lg:w-10 flex justify-center lg:justify-start',
'child' => $header
];
}
$ret[] = [
'nodeName' => 'div',
'class' => 'flex-none flex gap-2',
'child' => array_filter(array_merge($this->quickFilter, $this->action))
];
return $ret;
}
/**
* @return array
*/
public function tableNode(): array
{
// 指定行key
$this->params['row-key'] = $this->key;
// 设置扩展行
if ($this->expand) {
$this->params['vChild:expandable'] = $this->expand;
}
// 分组表头线
$children = false;
foreach ($this->columns as $col) {
if (isset($col['children']) && $col['children']) {
$children = true;
break;
}
}
if ($children) {
$this->params['bordered'] = [
'headerCell' => true
];
}
return [
'nodeName' => 'app-table',
'requestEventName' => $this->eventName,
'class' => $this->class,
'url' => $this->url,
'urlBind' => $this->urlBind,
'n-params' => $this->params,
'columns' => $this->columns,
'vBind:filter' => 'data.filter',
'select' => (bool)$this->bath,
'table-layout-fixed' => true,
'child' => [
'vSlot:footer' => 'footer',
'nodeName' => 'div',
'class' => 'flex gap-2',
'child' => $this->bath
]
];
}
/**
* @return array
*/
public function render(): array
{
/*$this->script[] = <<<JS
JS;
$this->scriptReturn[] = 'dataRef';*/
$value = [
'filter' => $this->data['filter'] ?: [],
'show' => $this->data['show'] ?: []
];
$value = array_merge($this->scriptData, $value);
return [
'node' => [
'nodeName' => 'app-form',
'value' => $value,
'child' => [
'nodeName' => 'div',
'class' => 'flex h-screen',
'vSlot' => '{value: data}',
'child' => [
$this->side['left'] ? [
'nodeName' => isset($this->sideSize['left']) ? 'a-resize-box' : 'div',
'style' => $this->sideSize['left'] ? 'width:' . $this->sideSize['left'] : '',
'directions' => ['right'],
'class' => 'border-r border-gray-200 dark:border-gray-700 flex-none bg-white dark:bg-blackgray-4 h-screen',
'child' => $this->side['left']
] : [],
[
'nodeName' => 'app-layout',
'class' => 'flex-grow w-10',
'title' => $this->title ?: '列表数据',
'child' => [
[
'vSlot' => '',
'nodeName' => 'div',
'class' => 'flex flex-row items-start gap-4 p-4',
'child' => [
$this->page['left'] ?: [],
[
'nodeName' => 'div',
'class' => 'flex-grow lg:w-10 p-4 bg-white dark:bg-blackgray-4 rounded shadow',
'child' => [
$this->header ? [
'nodeName' => 'div',
'class' => 'pb-4',
'child' => $this->header
] : [],
[
'nodeName' => 'div',
'class' => 'flex-none flex-row gap-2 items-center pb-4',
'child' => $this->headNode()
],
$this->tableNode(),
$this->footer ? [
'nodeName' => 'div',
'class' => 'pb-4',
'child' => $this->footer
] : [],
],
],
$this->page['right'] ?: [],
]
]
]
],
$this->side['right'] ? [
'nodeName' => $this->sideSize['right'] ? 'a-resize-box' : 'div',
'style' => $this->sideSize['right'] ? 'width:' . $this->sideSize['left'] : '',
'directions' => ['left'],
'class' => 'border-l border-gray-200 dark:border-gray-700 flex-none bg-white dark:bg-blackgray-4 h-screen',
'child' => $this->side['right']
] : [],
]
],
],
'setupScript' => implode("\n", $this->script) . "\n" . ' return {' . implode(",", $this->scriptReturn) . '}'
];
}
/**
* 渲染table核心
* @return array[]
*/
public function renderTableCore(): array
{
$value = [
'filter' => $this->data['filter'] ?: [],
'show' => $this->data['show'] ?: []
];
$value = array_merge($this->scriptData, $value);
return [
'node' => [
'nodeName' => 'app-form',
'value' => $value,
'child' => [
'nodeName' => 'app-dialog',
'vSlot' => '{value: data}',
'title' => $this->title ?: '信息详情',
'class' => 'flex-grow',
'child' => [
[
'nodeName' => 'div',
'class' => 'flex',
'vSlot' => '{value: data}',
'child' => [
$this->side['left'] ? [
'nodeName' => 'div',
'class' => 'flex-none',
'child' => $this->side['left']
] : [],
[
'nodeName' => 'div',
'class' => 'flex-grow lg:w-10 p-4 bg-white dark:bg-blackgray-4 rounded shadow',
'child' => [
$this->header ? [
'nodeName' => 'div',
'class' => 'pb-4',
'child' => $this->header
] : [],
[
'nodeName' => 'div',
'class' => 'flex-none flex flex-row gap-2 items-center pb-4',
'child' => $this->headNode()
],
$this->tableNode(),
$this->footer ? [
'nodeName' => 'div',
'class' => 'pb-4',
'child' => $this->footer
] : [],
],
],
$this->side['right'] ? [
'nodeName' => 'div',
'class' => 'flex-none',
'child' => $this->side['right']
] : []
]
],
[
'nodeName' => 'div',
'vSlot:footer' => '',
'class' => 'arco-modal-footer',
'child' => [
[
'nodeName' => 'route',
'type' => 'back',
'child' => [
'nodeName' => 'a-button',
'child' => '关闭'
]
]
]
]
]
]
]
];
}
}

View File

@@ -0,0 +1,99 @@
<?php
namespace admin\components\UI\Table;
use admin\components\UI\Widget\Link;
/**
* 树形列表
* @package backend\components\UI\Filter
*/
class Tree
{
protected string $label = '';
protected array $node = [];
protected array $prefix = [];
protected array $suffix = [];
protected Column\Link $link;
/**
* @param string $label
* @param array $node
*/
public function __construct(string $label, array $node = [])
{
$this->label = $label;
$this->node = $node;
}
/**
* @param $node
* @return $this
*/
public function prefix($node): self
{
$this->prefix = $node;
return $this;
}
/**
* @param $node
* @return $this
*/
public function suffix($node): self
{
$this->suffix = $node;
return $this;
}
/**
* 添加链接
* @param string $name
* @param string $route
* @param array $params
* @return Link
*/
public function link(string $name, string $route, array $params = []): Link
{
if (!$this->link) {
$this->link = new Column\Link();
}
return $this->link->add($name, $route, $params);
}
/**
* 渲染组件
* @return array
*/
public function render(): array
{
$suffix = $this->suffix;
if ($this->link) {
$suffix = $this->link->render('');
}
return [
'node' => $this->node ?: ['nodeName' => 'div', 'child' => "{{rowData.record['$this->label']}}"],
'prefix' => $this->prefix,
'suffix' => $suffix
];
}
/**
* 组件行数据
* @param $rowData
* @return array
*/
public function getData($rowData): array
{
$data = [];
// 元素数据
if ($this->link) {
$data = array_merge($data, $this->link->getData($rowData));
}
return $data;
}
}