通过表格布局,您可以轻松地以结构化和有序的方式显示大量数据。只需指定您想要在表格中包含的字段,表格布局将处理其余的工作,包括排序、过滤和分页。您还可以通过设置各种选项来自定义表格的外观,例如表格标题、行值等。无论您是在显示用户列表、产品、订单或任何其他数据,表格布局都是以清晰简洁的方式呈现数据的强大而灵活的工具。

要创建新的表格布局,您可以使用以下 Artisan 命令:

php artisan orchid:table PatientListLayout

示例:

namespace App\Orchid\Layouts;

use Orchid\Screen\TD;
use Orchid\Screen\Layouts\Table;

class PatientListLayout extends Table
{
    /**
     * 数据源。
     *
     * 从查询中获取的键名。
     * 其结果将是表格的元素。
     *
     * @var string
     */
    protected $target = 'patients';

    /**
     * @return TD[]
     */
    protected function columns() : iterable
    {
        return [
            TD::make('name'),
            TD::make('created_at')->sort(),
        ];
    }
}

表格还支持通过简短语法编写而无需创建类:

use Orchid\Support\Facades\Layout;
use Orchid\Screen\TD;

Layout::table('clients', [
    TD::make('name'),
    TD::make('created_at')->sort(),
]);

此图显示了一个示例表格,这是以结构化和有序格式显示数据的常见方式。

单元格介绍

表格只是一个通用包装器,您需要为其指定 TD 类。旨在创建单个单元格。

use Orchid\Screen\TD;

TD::make('last_name');

make 方法是主要方法,设置数组中的键名和显示名称。

TD::make('last_name', '姓氏');

列标题以其原始 HTML 内容呈现。这使您可以灵活地在标题中直接包含丰富格式或特殊字符。只需记住确保任何动态内容都是安全和经过清理的,以保持界面的安全和精致。

对齐

内容对齐控制可以使用 align 方法进行控制:

TD::make('last_name')->align(TD::ALIGN_LEFT);
TD::make('last_name')->align(TD::ALIGN_CENTER);
TD::make('last_name')->align(TD::ALIGN_RIGHT);

排序

选择排序应在 query 屏幕中完成,对于模型,您可以使用自动 http 排序和过滤

要启用此列的活动排序,必须指定 sort 方法:

TD::make('last_name')->sort();

过滤

在构建简单表格时,使用单独的过滤器可能显得过于繁琐。因此,您可以直接在列标题上方显示过滤字段。

它只会定义可见部分。您可以自己指定过滤逻辑或依赖于“自动 HTTP 过滤”。您可以在 “Eloquent Filters” 页面了解更多信息。

要添加字段,请调用 filter 方法并传递您想要显示的类实例:

TD::make('SKU')->filter(Input::make()->mask('A-999999'));

注意:无需指定字段名称。它将由列名称自动传递和覆盖。

可以使用 Select 过滤多个值,并使用可选的第二个过滤参数。默认情况下,它允许过滤给定值中的任何/所有值。

TD::make('color')->filter(TD::FILTER_SELECT, [
    'red'   => '红色',
    'green' => '绿色',
]);

在使用过滤器时,可以使用 filterValue() 方法,该方法允许您修改显示的过滤值。例如,您可以将 ID 值替换为显示名称。以下是使用 filterValue() 方法的示例:

TD::make('id')->filterValue(function ($value) {
    $user = User::find($value);
    return $user->name;
})

传递给函数的 $value 值将包含应用的过滤值。

宽度

您可以使用 width 方法控制单元格的宽度:

TD::make('last_name')->width('100px');

显示/隐藏

默认情况下,用户可以为自己隐藏任何列,但您可以通过指定以下内容来禁止这样做:

TD::make('last_name')->cantHide();

也可以默认隐藏,但可以根据用户请求显示。

TD::make('last_name')->defaultHidden();

数据输出

在某些情况下,您可能需要显示组合数据,为此目的可以使用 render 方法。它实现了根据函数生成单元格的能力:

TD::make('full_name')
    ->render(fn($user) => e($user->first_name . ' ' . $user->last_name)),

注意。 返回的字符串不会被转义!您需要使用 e() 助手或使用 Blade 视图自行处理。

回调函数必须返回任何字符串值:

TD::make('full_name')
    ->render(fn($user) => view('blade_template', [
        'user' => $user,
    ])),

请注意,您可以使用字段和操作:

use Orchid\Screen\Actions\Link;

TD::make()
    ->render(fn ($user) => Link::make($user->last_name)->route('platform.user.edit', $user)),

有时您希望在单个列中显示更多元素,例如更多按钮。为此,您可以使用 Group

use Orchid\Screen\Actions\Link;
use Orchid\Screen\Fields\Group;

TD::make()
    ->render(fn($user) => Group::make([
        Link::make('显示')->route('platform.user.show', $user),
        Link::make('编辑')->route('platform.user.edit', $user),
    ])),

例如,为每行显示一个复选框以进行批量操作:

TD::make()
    ->render(fn(User $user) => CheckBox::make('users[]')
        ->value($user->id)
        ->placeholder($user->name)
        ->checked(false)
    ),

有时可能需要从 query 屏幕获取值,而不是仅依赖于 target。您可以按如下方式获取值:

use Orchid\Screen\Actions\Link;

TD::make('price')
    ->render(fn ($product) => $product->price + $this->query->get('tax')),

使用循环变量

$loop 变量将在 render 闭包函数的第二个参数中可用。此变量提供了一些有用的信息,例如当前循环索引以及这是否是循环的第一次或最后一次迭代:

TD::make()->render(fn (Model $model, object $loop) => $loop->index),

$loop 变量包含许多其他有用的属性,您可以在 Laravel 文档中找到。

使用组件

复杂或动态数据在 render 方法中指定可能显得繁琐或令人不知所措。因此,单元格支持使用 Laravel 组件进行渲染。它允许您提取显示逻辑,并且可以重用。

例如,有一个 Order 模型,根据状态,我们可以在组件中显示不同的描述。这比直接在视图中指定内容或为此类处理创建特定区域要好得多。

namespace App\View\Components;

use Illuminate\View\Component;
use App\Models\Order;

class OrderShortInformation extends Component
{
    public function __construct(
        public readonly Order $order
    ) {}

    /**
     * 获取状态描述。
     *
     * @return string
     */
    public function status(): string
    {
        return match ($this->order->status) {
            Order::STATUS_PROCESS      => __('处理中'),
            Order::STATUS_PAID         => __('已支付'),
            Order::STATUS_CANCELLATION => __('取消'),
            Order::STATUS_REFUND       => __('退款'),
            default                    => __('未知'),
        };
    }

    /**
     * 获取表示组件的视图/内容。
     *
     * @return \Illuminate\View\View|\Closure|string
     */
    public function render(): \Illuminate\View\View|\Closure|string
    {
        return view('components.order.short-information');
    }
}

要在单元格中使用组件,您必须传递它:

use App\View\Components\OrderShortInformation;

TD::make('status')->component(OrderShortInformation::class);

组件将接收整行作为其第一个参数,而不仅仅是单元格数据。

因此,如果您在组件中使用深度注入,必须指定变量的名称。

public function __construct(Application $application, Order $order, int $limit = 300)
{
    $this->order = $order;
    // ...
}

其他附加参数,例如限制。您可以按以下方式指定:

TD::make('status')->component(OrderShortInformation::class, [
    'limit' => 100
]);

自定义组件值

这与上面使用组件非常相似,只是前面的示例获取了一个对象。但这并不总是必要的,有时只需要处理一个值。

为此,您需要添加一个新方法,该方法默认情况下只接收单元格值而不接收其他信息。例如,我想以特定格式显示值:

namespace App\View\Components;

use Illuminate\View\Component;

class Numeric extends Component
{
    /**
     * @var float
     */
    public float $value;

    /**
     * 创建新的组件实例。
     *
     * @param  float        $value
     * @param  int          $decimals
     * @param  string|null  $decimal_separator
     * @param  string|null  $thousands_separator
     */
    public function __construct(
        float   $value,
        int     $decimals = 0,
        ?string $decimal_separator = ".",
        ?string $thousands_separator = ","
    ) {
        $this->value = number_format($value, $decimals, $decimal_separator, $thousands_separator);
    }

    /**
     * 获取表示组件的视图/内容。
     *
     * @return \Illuminate\Contracts\View\View|\Closure|string
     */
    public function render()
    {
        return <<<'blade'
    {{ $value }}
blade;
    }
}

然后表格调用可能如下所示:

TD::make('price')->asComponent(Numeric::class);
// 1,235

也可以使用附加参数:

TD::make('price')->asComponent(Numeric::class, [
    'decimals' => 2,
    'decimal_separator' => ',',
    'thousands_separator' => ' ',
]);
// 1 234,56

表格选项

您可以指定表格为空时要显示的文本,指定方法:

/**
 * @return string
 */
protected function iconNotFound(): string
{
    return 'table';
}

/**
 * @return string
 */
protected function textNotFound(): string
{
    return __('此视图中没有记录');
}

/**
 * @return string
 */
protected function subNotFound(): string
{
    return '';
}

如果表格行对您来说不够对比鲜明,那么您可以启用 striped 模式:

/**
 * @return bool
 */
protected function striped(): bool
{
    return true;
}

您可以通过以下方法动态更改表格分页中显示的链接数量:

/**
 * 在当前页面链接的每一侧显示的链接数量。
 *
 * @return int
 */
protected function onEachSide(): int
{
    return 3;
}

添加总计行

在表格底部添加汇总行,为此您需要定义 total 方法并描述所需的单元格。例如:

public function total():array
{
    return [
        TD::make('total')
            ->align(TD::ALIGN_RIGHT)
            ->colspan(2)
            ->render(fn () => '总计:'),

        TD::make('total_count')
            ->align(TD::ALIGN_RIGHT),

        TD::make('total_active_count')
            ->align(TD::ALIGN_RIGHT),
    ];
}

此行将忽略基于 query 屏幕结果的指定 target

public function query(): array
{
    return [
        'total_active_count' => '$93 960',
        'total_count' => '$103 783',
        // ...
    ];
}

我们的朋友