starfolksoftware/inertia-table

Inertia.js 前端组件,用于 Spatie 的 Laravel 查询构建器

v2.2.2 2024-04-21 17:35 UTC

This package is auto-updated.

Last update: 2024-09-21 18:34:22 UTC


README

Latest Version on NPM npm Latest Version on Packagist Software License run-tests

特性

  • 自动填充:自动生成带有自定义单元格支持的 theadtbody
  • 全局搜索
  • 按字段搜索
  • 选择过滤器
  • 切换列
  • 排序列
  • 分页(支持 Eloquent/API Resource/Simple/Cursor)
  • 自动更新查询字符串(通过使用 Inertia 的 replace 功能

兼容性

注意:目前在使用此包与 Vite 一起使用时存在一个 问题

安装

您需要安装服务器端包和客户端包。请注意,此包仅与 Laravel 9、Vue 3.0 兼容,并需要 Tailwind Forms 插件。

服务器端安装(Laravel)

您可以通过 composer 安装此包

composer require starfolksoftware/inertia-table

该包将自动注册 Service Provider,它提供了一个您可以在 Inertia 响应中使用的 table 方法。

搜索字段

使用 searchInput 方法,您可以指定哪些属性是可搜索的。搜索查询作为 filter 传递到 URL 查询中。这与 Laravel 查询构建器包的 过滤功能无缝集成。

尽管传递列键就足够了,但您也可以指定自定义标签和默认值。

use InertiaTable\InertiaTable;

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
    $table->searchInput('name');

    $table->searchInput(
        key: 'framework',
        label: 'Find your framework',
        defaultValue: 'Laravel'
    );
});

选择过滤器

选择过滤器与搜索字段类似,但使用 select 元素而不是 input 元素。这样,您可以向用户展示一组预定义的选项。底层,它使用与 Laravel 查询构建器包相同的过滤功能。

selectFilter 方法需要两个参数:键和一个包含选项的键值数组。

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
    $table->selectFilter('language_code', [
        'en' => 'Engels',
        'nl' => 'Nederlands',
    ]);
});

默认情况下,selectFilter 会向数组添加一个 无过滤器 选项。您可以禁用此选项或为其指定一个自定义标签。

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
    $table->selectFilter(
        key: 'language_code',
        options: $languages,
        label: 'Language',
        defaultValue: 'nl',
        noFilterOption: true,
        noFilterOptionLabel: 'All languages'
    );
});

使用 column 方法,您可以指定哪些列可以切换、排序和搜索。您必须为每个列至少传递一个键或标签。

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
    $table->column('name', 'User Name');

    $table->column(
        key: 'name',
        label: 'User Name',
        canBeHidden: true,
        hidden: false,
        sortable: true,
        searchable: true
    );
});

searchable 选项是 searchInput 方法的快捷方式。下面的示例实际上将调用 $table->searchInput('name', 'User Name')

全局搜索

您可以使用 withGlobalSearch 方法启用全局搜索,并可选地指定一个占位符。

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
    $table->withGlobalSearch();

    $table->withGlobalSearch('Search through the data...');
});

如果您想默认为每个表格启用全局搜索,您可以使用静态 defaultGlobalSearch 方法,例如,在 AppServiceProvider 类中

InertiaTable::defaultGlobalSearch();
InertiaTable::defaultGlobalSearch('Default custom placeholder');
InertiaTable::defaultGlobalSearch(false); // disable

示例控制器

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Support\Collection;
use Inertia\Inertia;
use InertiaTable\InertiaTable;
use Spatie\QueryBuilder\AllowedFilter;
use Spatie\QueryBuilder\QueryBuilder;

class UserIndexController
{
    public function __invoke()
    {
        $globalSearch = AllowedFilter::callback('global', function ($query, $value) {
            $query->where(function ($query) use ($value) {
                Collection::wrap($value)->each(function ($value) use ($query) {
                    $query
                        ->orWhere('name', 'LIKE', "%{$value}%")
                        ->orWhere('email', 'LIKE', "%{$value}%");
                });
            });
        });

        $users = QueryBuilder::for(User::class)
            ->defaultSort('name')
            ->allowedSorts(['name', 'email', 'language_code'])
            ->allowedFilters(['name', 'email', 'language_code', $globalSearch])
            ->paginate()
            ->withQueryString();

        return Inertia::render('Users/Index', [
            'users' => $users,
        ])->table(function (InertiaTable $table) {
            $table
              ->withGlobalSearch()
              ->defaultSort('name')
              ->column(key: 'name', searchable: true, sortable: true, canBeHidden: false)
              ->column(key: 'email', searchable: true, sortable: true)
              ->column(key: 'language_code', label: 'Language')
              ->column(label: 'Actions')
              ->selectFilter(key: 'language_code', label: 'Language', options: [
                  'en' => 'English',
                  'nl' => 'Dutch',
              ]);
    }
}

客户端安装(Inertia)

您可以通过 npmyarn 安装此包

npm install @starfolksoftware/inertia-table --save

yarn add @starfolksoftware/inertia-table

将存储库路径添加到您的 Tailwind 配置文件 中的 content 数组。这确保了样式在生产构建中也有效。

module.exports = {
  content: [
    './node_modules/@starfolksoftware/inertia-table/**/*.{js,vue}',
  ]
}

表格组件

要使用 Table 组件及其所有相关功能,您必须导入 Table 组件并将 users 数据传递给组件。

<script setup>
import { Table } from "@starfolksoftware/inertia-table";

defineProps(["users"])
</script>

<template>
  <Table :resource="users" />
</template>

自动检测数据和相关分页元数据的 resource 属性。您也可以通过 datameta 属性手动传递此属性到组件中。

<template>
  <Table :data="users.data" :meta="users.meta" />
</template>

如果您想手动渲染表格,例如在本包的 v1 中,您可以使用 headbody 插槽。此外,您仍然可以使用 meta 属性来渲染分页器。

<template>
  <Table :meta="users">
    <template #head>
      <tr>
        <th>User</th>
      </tr>
    </template>

    <template #body>
      <tr
        v-for="(user, key) in users.data"
        :key="key"
      >
        <td>{{ user.name }}</td>
      </tr>
    </template>
  </Table>
</template>

Table 还有一些额外的属性来调整其前端行为。

<template>
  <Table
    :striped="true"
    :prevent-overlapping-requests="false"
    :input-debounce-ms="1000"
    :preserve-scroll="true"
  />
</template>

自定义列单元格

当使用 auto-fill 时,您可能希望在保持其他列不变的情况下,转换特定列显示的数据。为此,您可以使用单元格模板。以下示例取自上面的 示例控制器

<template>
  <Table :resource="users">
    <template #cell(actions)="{ item: user }">
      <a :href="`/users/${user.id}/edit`">
        Edit
      </a>
    </template>
  </Table>
</template>

每页多个表格

您可能想在每页使用多个表格组件。显示数据很简单,但使用如过滤、排序和分页等特性需要稍微不同的设置。例如,默认情况下,使用 page 查询键进行数据集分页,但现在您想为每个表格使用不同的键。幸运的是,这个包处理了这个问题,并提供了一个辅助方法来支持 Spatie 的查询包。要使其工作,您需要为表格 命名

让我们看看 Spatie 的 QueryBuilder。在这个示例中,有一个公司表格和一个用户表格。我们相应地命名这些表格。首先,调用静态 updateQueryBuilderParameters 方法来告诉包使用不同的查询参数集。现在,filter 变为 companies_filtercolumn 变为 companies_column 等。其次,更改数据库分页器的 pageName

InertiaTable::updateQueryBuilderParameters('companies');

$companies = QueryBuilder::for(Company::query())
    ->defaultSort('name')
    ->allowedSorts(['name', 'email'])
    ->allowedFilters(['name', 'email'])
    ->paginate(pageName: 'companiesPage')
    ->withQueryString();

InertiaTable::updateQueryBuilderParameters('users');

$users = QueryBuilder::for(User::query())
    ->defaultSort('name')
    ->allowedSorts(['name', 'email'])
    ->allowedFilters(['name', 'email'])
    ->paginate(pageName: 'usersPage')
    ->withQueryString();

然后,我们需要将这些更改应用到 InertiaTable 类。有一个 namepageName 方法可以做到这一点。

return Inertia::render('TwoTables', [
    'companies' => $companies,
    'users'     => $users,
])->table(function (InertiaTable $inertiaTable) {
    $inertiaTable
        ->name('users')
        ->pageName('usersPage')
        ->defaultSort('name')
        ->column(key: 'name', searchable: true)
        ->column(key: 'email', searchable: true);
})->table(function (InertiaTable $inertiaTable) {
    $inertiaTable
        ->name('companies')
        ->pageName('companiesPage')
        ->defaultSort('name')
        ->column(key: 'name', searchable: true)
        ->column(key: 'address', searchable: true);
});

最后,将正确的 name 属性传递到 Vue 模板中的每个表格。可选地,您可以设置 preserve-scroll 属性为 table-top。这确保在新的数据时滚动到表格的顶部。例如,当更改 第二个 表格的页面时,您希望滚动到表格的顶部,而不是页面的顶部。

<script setup>
import { Table } from "@starfolksoftware/inertia-table";

defineProps(["companies", "users"])
</script>

<template>
  <Table
    :resource="companies"
    name="companies"
    preserve-scroll="table-top"
  />

  <Table
    :resource="users"
    name="users"
    preserve-scroll="table-top"
  />
</template>

分页翻译

您可以使用 setTranslations 方法覆盖默认的分页翻译。您可以在主 JavaScript 文件中这样做。

import { setTranslations } from "@starfolksoftware/inertia-table";

setTranslations({
  next: "Next",
  no_results_found: "No results found",
  of: "of",
  per_page: "per page",
  previous: "Previous",
  results: "results",
  to: "to"
});

Table.vue 插槽

Table.vue 有几个插槽,您可以使用它们来注入自己的实现。

每个插槽都提供了与父 Table 组件交互的 props。

<template>
  <Table>
    <template v-slot:tableGlobalSearch="slotProps">
      <input
        placeholder="Custom Global Search Component..."
        @input="slotProps.onChange($event.target.value)"
      />
    </template>
  </Table>
</template>

测试

app 目录中可以找到一个巨大的 Laravel Dusk E2E 测试套件。在这里您会找到一个 Laravel + Inertia 应用程序。

cd app
cp .env.example .env
composer install
npm install
npm run production
touch database/database.sqlite
php artisan migrate:fresh --seed
php artisan dusk:chrome-driver
php artisan serve
php artisan dusk

从 v1 升级

服务器端

  • 已将 addColumn 方法重命名为 column
  • 已将 addFilter 方法重命名为 selectFilter
  • 已将 addSearch 方法重命名为 searchInput
  • 对于所有重命名的方法,请检查参数,因为一些已经更改。
  • 已删除 addColumnsaddSearchRows 方法。
  • 全局搜索现在默认不再启用。

客户端

  • 已删除 InteractsWithQueryBuilder 混合,不再需要。
  • Table 组件不再需要 filterssearchcolumnson-update 属性。
  • 当使用自定义的 theadtbody 插槽时,您需要手动提供 样式
  • 当使用自定义 thead 时,showColumn 方法已被重命名为 show
  • setTranslations 方法不再是 Pagination 组件的一部分,但需要导入。
  • 组件的模板和逻辑不再分离。使用插槽来注入您的自定义实现。

v2.1 路线图

  • 布尔过滤器
  • 日期过滤器
  • 日期范围过滤器
  • 将演示应用切换到 Vite

变更日志

有关最近更改的更多信息,请参阅 变更日志

贡献

有关详细信息,请参阅 贡献指南

安全

如果您发现任何与安全相关的问题,请通过电子邮件 faruk@starfolksoftware.com 联系我们,而不是使用问题跟踪器。

鸣谢

许可证

MIT 许可证 (MIT)。有关更多信息,请参阅 许可证文件