vue3-smart-datatable
v1.0.9
Published
A **smart datatable for Vue 3** with search, filters, sorting, horizontal scroll, and pagination. Supports both **global search** and **column-based filtering** (with `like` and `equal`).
Downloads
8
Readme
vue3-smart-datatable
A smart datatable for Vue 3 with search, filters, sorting, horizontal scroll, and pagination.
Supports both global search and column-based filtering (with like and equal).
🔗 Live Demo
📦 Installation
npm install vue3-smart-datatable
npm i element-plus🚀 Usage Example (Vue 3)
<template>
<SmartDatatable
:columns="columns"
api-url="http://localhost/npm/bkend/api/test"
:per-page="100"
:pdfPerPage="pdfSettings.perPage"
:directionPDF="pdfSettings.directionPDF"
:pdfColumns="pdfSettings.columns"
:HeaderPDF="pdfSettings.HeaderPDF"
:pdfShowPageNumbers="pdfSettings.showPageNumbers"
scroll-height="400px"
scroll-width="100%"
export-file-name="test"
:i18n="i18n"
>
<template #export-buttons="{ exportFile }">
<button class="btn btn-success me-2" @click="exportFile('csv')">
<i class="bi bi-download me-1"></i> CSV
</button>
<button v-if="user.role === 'admin'" class="btn btn-danger" @click="exportFile('pdf')">
<i class="bi bi-file-earmark-pdf me-1"></i> PDF (Admins only)
</button>
</template>
</SmartDatatable>
</template>
<script setup>
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap-icons/font/bootstrap-icons.css'
import SmartDatatable from "@/npm/vue3-smart-datatable/src/SmartDatatable.vue"
import { reactive, ref } from "vue"
const user = ref({
name: "Mohamed",
role: "admin"
})
const columns = [
{ name: 'id', label: 'ID', width: '10%' },
{ name: 'site', label: 'Site', width: '30%', searchType: 'equal' },
{ name: 'code', label: 'Code', width: '20%' }
]
const pdfSettings = reactive({
perPage: 15,
directionPDF:'rtl',
columns: [
{ name: 'id', label: 'ID' },
{ name: 'site', label: 'Site' },
{ name: 'sap_asset_code', label: 'SAP Asset Code' }
],
HeaderPDF: 'تقرير الأصول',
showPageNumbers: true
});
const i18n = {
title: 'جدول البيانات',
searchAll: 'بحث في جميع الأعمدة...',
searchIn: 'بحث في',
noData: 'لا توجد بيانات',
loading: 'جاري التحميل...',
exportCsv: 'تصدير CSV',
exportPdf: 'تصدير PDF',
prev: 'السابق',
next: 'التالي',
showing: 'عرض',
to: 'إلى',
of: 'من',
entries: 'عنصر'
}
</script>⚙️ Example Backend (Laravel / PHP)
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\AssetDetail;
use App\Services\MpdfService;
use Illuminate\Support\Facades\Schema;
class AssetDetailController extends Controller
{
/**
* Retrieve paginated test with search, filter, and sort.
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function index(Request $request)
{
$query = $this->buildQuery($request);
$perPage = $request->get('per_page', 10);
$test = $query->paginate($perPage);
return response()->json($test);
}
/**
* Export test to CSV or PDF.
*
* @param Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function export(Request $request)
{
// Get columns and labels from request
$columnsJson = $request->query('columns', '{"id":"ID"}');
$columnsArray = json_decode($columnsJson, true) ?: [];
// Extract columns (keys) and labels (values)
$columns = array_keys($columnsArray); // e.g., ['id', 'site', 'sap_asset_code']
$labels = array_values($columnsArray); // e.g., ['ID', 'Site', 'SAP Asset Code']
// Get valid columns from the test_details table
$validColumns = Schema::getColumnListing('test_details');
// Filter to only valid columns
$validColumns = array_intersect($columns, $validColumns);
$columns = !empty($validColumns) ? array_values($validColumns) : ['id'];
// Ensure direction is a string
$direction = $request->query('directionPDF', 'ltr');
$HeaderPDF = $request->query('HeaderPDF', 'HeaderPDF');
\Log::info('Received columnsJson: ' . json_encode($direction));
if (is_array($direction)) {
$direction = reset($direction) ?: 'ltr'; // Take first element if array, or default to 'ltr'
}
$direction = in_array($direction, ['ltr', 'rtl']) ? $direction : 'ltr';
$type = $request->query('export_type', 'csv');
$perPagePdf = (int) $request->query('pdfPerPage', 5);
// Build query with all conditions
$query = $this->buildQuery($request);
// Select only valid columns and get results
$rows = $query->select($columns)
->get()
->map(function ($row) use ($columns) {
return collect($row)->only($columns)->toArray();
})
->toArray();
return (new MpdfService)->exporttest($rows, $columns, $labels, $direction, $type, $perPagePdf,$HeaderPDF);
}
/**
* Build query with search, filter, and sort.
*
* @param Request $request
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function buildQuery(Request $request)
{
$query = AssetDetail::query();
$allowedColumns = (new AssetDetail)->getFillable();
// Global search
if ($search = $request->get('global')) {
$query->where(function ($q) use ($search, $allowedColumns) {
foreach ($allowedColumns as $col) {
$q->orWhere($col, 'like', "%$search%");
}
});
}
// Column filters
if ($filters = $request->get('filter')) {
foreach ($filters as $col => $val) {
if (!in_array($col, $allowedColumns, true)) {
continue; // Skip invalid columns
}
if (!empty($val)) {
if (str_starts_with($val, 'like:')) {
$realVal = substr($val, 5);
$query->where($col, 'like', "%$realVal%");
} elseif (str_starts_with($val, 'equal:')) {
$realVal = substr($val, 6);
$query->where($col, $realVal);
}
}
}
}
// Sorting
if ($sortBy = $request->get('sort_by')) {
if (in_array($sortBy, $allowedColumns, true)) {
$sortDir = $request->get('sort_dir', 'asc');
$query->orderBy($sortBy, in_array($sortDir, ['asc', 'desc']) ? $sortDir : 'asc');
}
}
return $query;
}
}
MpdfService.php
<?php
namespace App\Services;
use Mpdf\Mpdf;
use Mpdf\Config\ConfigVariables;
use Mpdf\Config\FontVariables;
use Symfony\Component\HttpFoundation\StreamedResponse;
class MpdfService
{
/**
* Export test to PDF or CSV.
*
* @param array $test
* @param array $columns
* @param array $labels
* @param string $direction
* @param string $type
* @param int $perPagePdf
* @return \Symfony\Component\HttpFoundation\Response
*/
public function exporttest(array $test, array $columns, array $labels, string $direction, string $type, int $perPagePdf, string $HeaderPDF)
{
if ($type === 'pdf') {
return $this->exportPdf($test, $columns, $labels, $direction, $perPagePdf, $HeaderPDF);
}
if ($type === 'csv') {
return $this->exportCsv($test, $columns, $labels);
}
abort(400, 'Unsupported export type');
}
/**
* Export to PDF with pagination.
*
* @param array $test
* @param array $columns
* @param array $labels
* @param string $direction
* @param int $perPagePdf
* @return \Illuminate\Http\Response
*/
private function exportPdf(array $test, array $columns, array $labels, string $direction, int $perPagePdf , string $HeaderPDF)
{
// إعدادات Mpdf
$defaultConfig = (new ConfigVariables())->getDefaults();
$fontDirs = $defaultConfig['fontDir'];
$defaultFontConfig = (new FontVariables())->getDefaults();
$fontData = $defaultFontConfig['fontdata'];
$mpdf = new Mpdf([
'mode' => 'utf-8',
'format' => 'A4',
'orientation' => 'L',
'margin_top' => 35,
'margin_bottom' => 20,
'margin_header' => 10,
'margin_footer' => 10,
'fontDir' => array_merge($fontDirs, [
storage_path('fonts'),
]),
'fontdata' => $fontData + [
'arabic' => [
'R' => 'Janna LT Bold.ttf',
'useOTL' => 0xFF,
],
],
'default_font' => $direction === 'rtl' ? 'arabic' : 'dejavusans',
'tempDir' => storage_path('app/mpdf/tmp'),
]);
$mpdf->SetDirectionality($direction === 'rtl' ? 'rtl' : 'ltr');
// الهيدر: عنوان التقرير + المستخدم
$username = auth()->user()->name ?? 'Guest';
$reportTitle = $HeaderPDF;
$mpdf->SetHTMLHeader('
<div style="text-align:center; font-weight:bold; font-size:14pt;">
' . $reportTitle . '
</div>
<div style="text-align:right; font-size:10pt;">
User: ' . $username . '
</div>
');
$mpdf->SetHTMLFooter('
<div style="text-align:center; font-size:10pt;">
Page {PAGENO} of {nb}
</div>
');
// تقسيم الصفحات
$chunks = array_chunk($test, $perPagePdf);
foreach ($chunks as $i => $rows) {
if ($i > 0) {
$mpdf->AddPage();
}
$html = '<style>
table { border-collapse: collapse; width: 100%; font-family: ' . ($direction === 'rtl' ? 'arabic' : 'dejavusans') . '; }
th { background-color: #f2f2f2; font-weight: bold; padding: 8px; text-align: ' . ($direction === 'rtl' ? 'right' : 'left') . '; border: 1px solid #ddd; }
td { padding: 8px; border: 1px solid #ddd; text-align: ' . ($direction === 'rtl' ? 'right' : 'left') . '; }
tr:nth-child(even) { background-color: #f9f9f9; }
</style>';
$html .= '<table>';
$html .= '<thead><tr>';
foreach ($labels as $label) {
$html .= "<th>" . htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . "</th>";
}
$html .= '</tr></thead><tbody>';
foreach ($rows as $row) {
$html .= '<tr>';
foreach ($columns as $columnName) {
$value = $row[$columnName] ?? '';
$html .= '<td>' . htmlspecialchars($value, ENT_QUOTES, 'UTF-8') . '</td>';
}
$html .= '</tr>';
}
$html .= '</tbody></table>';
$mpdf->WriteHTML($html);
}
return response($mpdf->Output('test.pdf', 'I'), 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'inline; filename="test.pdf"',
]);
}
/**
* Export to CSV.
*
* @param array $test
* @param array $columns
* @param array $labels
* @return \Symfony\Component\HttpFoundation\StreamedResponse
*/
private function exportCsv(array $test, array $columns, array $labels)
{
return new StreamedResponse(function () use ($test, $columns, $labels) {
$handle = fopen('php://output', 'w');
// Add BOM for UTF-8 to support special characters in Excel
fwrite($handle, "\xEF\xBB\xBF");
// Header
fputcsv($handle, $labels);
// Data rows
foreach ($test as $row) {
$line = [];
foreach ($columns as $columnName) {
$value = $row[$columnName] ?? '';
if (is_array($value)) {
$value = implode(', ', $value);
}
$line[] = $value;
}
fputcsv($handle, $line);
}
fclose($handle);
}, 200, [
'Content-Type' => 'text/csv; charset=UTF-8',
'Content-Disposition' => 'attachment; filename="test-' . date('Y-m-d') . '.csv"',
'Pragma' => 'no-cache',
'Expires' => '0',
]);
}
}✨ Features
- 🔍 Global Search across all columns.
- 🎯 Column Filters with
likeorequal. - ⏫ Sorting (asc/desc).
- 📑 Pagination (server-side).
- ↔️ Horizontal Scroll for wide tables.
- 📤 Export Options: CSV & PDF (PDF restricted to Admin role if needed).
- 📝 Customizable PDF:
- Per page row limit (
pdfPerPage) - Layout direction (
directionPDF) - Selected columns (
pdfColumns) - Custom header (
HeaderPDF) - Show/Hide page numbers
- Per page row limit (
- 🎨 Built-in support for Bootstrap 5 & Bootstrap Icons.
- 🌍 i18n Ready (multi-language labels).
❤️ Support the Project
If this package helps you, consider supporting my work:
