tldr
这是我使用react-table
和prisma
Orm在React JS项目中管理DataTable的完整指南。让我们开始!
客户端
// Component Filename: TablePagination.js
import {
ArrowLongDownIcon,
ArrowLongUpIcon,
FunnelIcon,
} from "@heroicons/react/24/outline"
import { ClockIcon } from "@heroicons/react/24/solid"
import React from "react"
import {
useAsyncDebounce,
useGlobalFilter,
usePagination,
useSortBy,
useTable,
} from "react-table"
function TablePagination({
columns,
data,
fetchData,
loading,
pageCount: controlledPageCount,
totalRow,
actions: Actions,
}) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page,
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
state: { pageIndex, pageSize, globalFilter, sortBy },
preGlobalFilteredRows,
setGlobalFilter,
} = useTable(
{
columns,
data,
manualPagination: true,
manualGlobalFilter: true,
manualSortBy: true,
initialState: {
pageIndex: 0,
pageSize: 10,
}, // Pass our hoisted table state
pageCount: controlledPageCount,
autoResetSortBy: false,
autoResetExpanded: false,
autoResetPage: false,
},
useGlobalFilter,
useSortBy,
usePagination
)
const GlobalFilter = ({
preGlobalFilteredRows,
globalFilter,
setGlobalFilter,
}) => {
const count = preGlobalFilteredRows
const [value, setValue] = React.useState(globalFilter)
const onChange = useAsyncDebounce((value) => {
setGlobalFilter(value || undefined)
}, 700)
return (
<div
className={
Actions !== undefined
? "flex flex-row justify-between"
: "flex flex-col"
}
>
{Actions !== undefined ? <Actions /> : null}
<input
value={value || ""}
onChange={(e) => {
setValue(e.target.value)
onChange(e.target.value)
}}
placeholder={`${count} records...`}
type="search"
className={`input input-bordered input-sm w-full max-w-xs focus:outline-0 mb-2 ${
Actions !== undefined ? "" : "self-end"
}`}
/>
</div>
)
}
React.useEffect(() => {
let search = globalFilter === undefined ? "" : globalFilter
fetchData(pageSize, pageIndex, search, sortBy)
}, [fetchData, pageIndex, pageSize, globalFilter, sortBy])
return (
<>
<GlobalFilter
preGlobalFilteredRows={totalRow}
globalFilter={globalFilter}
setGlobalFilter={setGlobalFilter}
/>
<div className="overflow-x-auto relative">
<table
{...getTableProps()}
className="table table-compact table-zebra w-full"
>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th
{...column.getHeaderProps(
column.getSortByToggleProps()
)}
>
<span>
{column.isSorted ? (
column.isSortedDesc ? (
<ArrowLongDownIcon className="h-4 w-4 inline mr-1" />
) : (
<ArrowLongUpIcon className="h-4 w-4 inline mr-1" />
)
) : (
<FunnelIcon className="h-4 w-4 inline mr-1" />
)}
</span>
{column.render("Header")}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.length > 0 ? (
page.map((row, i) => {
prepareRow(row)
return (
<tr
{...row.getRowProps()}
className="hover"
>
{row.cells.map((cell) => {
return (
<td {...cell.getCellProps()}>
{cell.render("Cell")}
</td>
)
})}
</tr>
)
})
) : (
<tr className="hover">
<td colSpan={10000} className="text-center">
Data not found!
</td>
</tr>
)}
</tbody>
</table>
{loading ? (
<div className="absolute top-0 bottom-0 left-0 right-0 bg-black bg-opacity-5 rounded-md z-20 flex items-center justify-center">
<div className="absolute p-3 bg-white w-36 shadow-md rounded-md text-center">
<div className="flex animate-pulse">
<ClockIcon className="w-6 h-6 mr-1" />{" "}
<span>Loading...</span>
</div>
</div>
</div>
) : null}
</div>
<div className="flex flex-row justify-between">
<div className="mt-2">
<span>
Halaman{" "}
<strong>
{pageIndex + 1} dari {pageOptions.length}
</strong>{" "}
Total <strong>{preGlobalFilteredRows.length}</strong>{" "}
</span>
<span>
| Lompat ke halaman:{" "}
<input
type="number"
defaultValue={pageIndex + 1}
onChange={(e) => {
const page = e.target.value
? Number(e.target.value) - 1
: 0
gotoPage(page)
}}
className="input input-bordered input-sm w-20 max-w-xs focus:outline-0"
/>
</span>{" "}
<select
value={pageSize}
onChange={(e) => {
setPageSize(Number(e.target.value))
}}
className="select select-bordered select-sm w-30 max-w-xs focus:outline-0"
>
{[10, 20, 30, 40, 50].map((pageSize) => (
<option key={pageSize} value={pageSize}>
Tampilkan {pageSize} baris
</option>
))}
</select>
</div>
<div className="mt-2">
<button
className="btn btn-xs"
onClick={() => gotoPage(0)}
disabled={!canPreviousPage}
>
{"<<"}
</button>{" "}
<button
className="btn btn-xs"
onClick={() => previousPage()}
disabled={!canPreviousPage}
>
{"<"}
</button>{" "}
<button
className="btn btn-xs"
onClick={() => nextPage()}
disabled={!canNextPage}
>
{">"}
</button>{" "}
<button
className="btn btn-xs"
onClick={() => gotoPage(pageCount - 1)}
disabled={!canNextPage}
>
{">>"}
</button>{" "}
</div>
</div>
</>
)
}
export default TablePagination
依赖性
上面的组件生成一个具有全局过滤,分页,排序和加载功能的表。它使用以下依赖项:图标的@heroicons/react
,基本库的react
,react-table
用于生成表。
表实例
TablePagination
是一个接收用于显示表的道具的函数。这些道具是表列的columns
,要显示的数据的data
,在更改分页时获取数据的fetchData
,loading
以显示加载图标,pageCount
,pageCount
,totalRow
for the Row Count和actions
for actions
for actions
for actions
for actions
for actions
过滤器行上的额外动作按钮。
该函数使用useTable
创建一个表实例,并将表的状态初始化为第一页,每页十行。它还将manualPagination
,manualGlobalFilter
和manualSortBy
设置为true
,以便组件可以控制这些功能。
全局过滤
GlobalFilter
组件显示用于过滤表数据的输入搜索框。它还收到预先过滤的行计数,并使用useAsyncDebounce
延迟搜索过滤器,直到用户停止键入。这有助于减少搜索时对服务器的不必要调用。
桌子主体
然后,使用React-Table库中的getTableProps
和getTableBodyProps
方法创建表主体和标头。 headerGroups
和页面用于使用MAP函数分别在标题列和表数据上映射。每行都调用prepareRow
方法,以实现getRowProps
和getCellProps
方法的使用来定型行和单元格。
排序
通过将getHeaderProps
方法添加到列标题并使用column.getSortByToggleProps()
方法来启用排序功能。此方法在表状态中更新sortBy
对象,并将适当的类和图标添加到排序的列中。
分页
使用usePagination
和pageCount
,canPreviousPage
,canNextPage
,pageOptions
,pageOptions
,gotoPage
,nextPage
,nextPage
,previousPage
和setPageSize
方法启用了分页功能。这些方法用于生成分页控件并在用户与它们交互时更新表状态。
加载
最后,通过检查loading
是否为true并在从服务器获取数据时在表中显示加载图标。
帮助者
当我们使用API进行分页时,我们还需要一个助手到发送到服务器之前的序列化端点URL。
// Filename: uriSerialized.js
const Util = {
isArray: function (val) {
return Object.prototype.toString.call(val) === "[object Array]"
},
isNil: function (val) {
return val === null || Util.typeOf(val)
},
typeOf: function (val, type) {
return (type || "undefined") === typeof val
},
funEach: function (obj, fun) {
if (Util.isNil(obj)) return // empty value
if (!Util.typeOf(obj, "object")) obj = [obj] // Convert to array
if (Util.isArray(obj)) {
// Iterate over array
for (var i = 0, l = obj.length; i < l; i++)
fun.call(null, obj[i], i, obj)
} else {
// Iterate over object
for (var key in obj)
Object.prototype.hasOwnProperty.call(obj, key) &&
fun.call(null, obj[key], key, obj)
}
},
}
export const uriSerialized = (params) => {
let pair = []
const encodeValue = (v) => {
if (Util.typeOf(v, "object")) v = JSON.stringify(v)
return encodeURIComponent(v)
}
Util.funEach(params, (val, key) => {
let isNil = Util.isNil(val)
if (!isNil && Util.isArray(val)) key = `${key}[]`
else val = [val]
Util.funEach(val, (v) => {
pair.push(`${key}=${isNil ? "" : encodeValue(v)}`)
})
})
return pair.join("&")
}
此代码定义了一个名为Util
的对象,该对象包含多个实用程序函数:
-
isArray
检查给定值是否为数组。它通过使用Object.prototype.toString.call
方法来完成此操作,该方法返回代表对象类型的字符串。如果字符串匹配预期值“[object Array]
”,则该值被视为数组。 -
isNil
检查给定值是null还是未定义。它通过使用typeOf
方法来检查该值的类型是否为“未定义”。 -
typeOf
检查给定值是否为某种类型。它通过将值的类型与提供的类型作为参数进行比较来做到这一点。如果类型匹配,则返回true。 -
funEach
是一个实用程序函数,可以在数组或对象上迭代并为每个元素执行给定函数。如果给定值为null或未定义,则该函数仅返回。如果该值不是对象,则将其转换为数组。如果值是数组,则它会在每个元素上迭代,并以元素,索引和数组为参数调用给定函数。如果值是一个对象,则它会在每个键值对上迭代,并以值,键和对象为参数的给定函数。
代码然后导出一个名为uriSerialized
的函数。此函数将对象params
作为输入,并返回代表对象为尿道编码的字符串的字符串。
该函数使用Util.funEach
迭代对象并创建一个键值对数组,其中每个值都均为尿道编码。如果值是数组,则通过将“ []”附加到末端来修改键。然后将键值对与“&”作为分离器串联成一个字符串,然后返回。
服务
例如,当我们需要为系统上的角色指定创建数据时。
import axios from "axios"
import { uriSerialized } from "../Utils/uriSerialized"
export const getRoleDatatable = async (queryOptions = null) => {
try {
const query = queryOptions ? "?" + uriSerialized(queryOptions) : ""
const request = await axios({
method: "GET",
url: `/roles/datatable${query}`,
})
const response = request.data
return response
} catch (error) {
console.log(`getRoleDatatable error: ${error}`)
return false
}
}
此函数使使用Axios HTTP客户端库的角色数据登录到API端点。
该函数接受可选参数queryOptions
,可用于将查询参数传递到API端点。如果queryOptions
未零,则将使用从“ ../ utils/uriserializatized导入的uriSerialized
函数”将其转换为序列化的URI字符串。然后将序列化的URI字符串附加到API端点URL。
然后,该函数将HTTP请求发送到AXIO的API端点,并等待响应数据。如果请求成功,则返回响应数据。如果请求失败,则错误消息将记录到控制台,并且函数返回false
。
角色数据表
woooooooooooooooooooooooooooo ....在RoleDatatable
组件中实现
import React, { useState, useCallback, useMemo } from "react"
import { getRoleDatatable } from "../../../services/roles"
import TablePagination from "../../TablePagination"
import { PencilSquareIcon, TrashIcon } from "@heroicons/react/24/solid"
function RoleDatatable() {
const [data, setData] = useState([])
const [loading, setLoading] = useState(false)
const [pageCount, setPageCount] = useState(0)
const [totalRow, setTotalRow] = useState(0)
const fetchData = useCallback(
async (pageSize, pageIndex, search, order) => {
setLoading(true)
const queryOptions = {
page: pageIndex,
limit: pageSize,
search: search,
order: order,
}
const items = await getRoleDatatable(queryOptions)
setData(items.data)
setPageCount(items.pagination.totalPage)
setTotalRow(items.pagination.totalRow)
setLoading(false)
},
[]
)
const columns = useMemo(
() => [
{
Header: "#",
accessor: "roleId",
Cell: ({ row }) => `R#${row.original.roleId}`,
disableSortBy: true,
},
{
Header: "Role Name",
accessor: "roleName",
},
{
Header: "Role Key",
accessor: "roleKey",
Cell: ({ row }) => row.original.roleKey,
},
{
Header: "Action",
accessor: ({ row }) => {
return (
<div className="flex gap-2">
<button className="btn btn-xs btn-info">
<PencilSquareIcon className="w-4 h-4" />
</button>
<button className="btn btn-xs btn-error">
<TrashIcon className="w-4 h-4" />
</button>
</div>
)
},
},
],
[]
)
return (
<section>
<TablePagination
columns={columns}
data={data}
fetchData={fetchData}
loading={loading}
pageCount={pageCount}
totalRow={totalRow}
/>
</section>
)
}
export default RoleDatatable
这是一个功能性的反应组件,可从服务器获取数据,将其显示在打页表中,并为用户提供每个项目的某些操作按钮。
该组件使用useState
钩子维护其内部状态,其中包括data
,loading
,pageCount
和totalRow
。 fetchData
函数是一个useCallback
挂钩,它使用一些查询参数对服务器进行API调用,以获取数据,更新状态变量并设置加载标志。
该组件还使用useMemo
钩记录包含一系列对象的columns
对象,该对象定义了表列的标题,其访问者函数和Cell
函数,该函数返回每行的相应值。该表的最后一列有两个按钮PencilSquareIcon
和TrashIcon
,可允许用户编辑或删除项目。
TablePagination
组件是一种自定义组件,可接收columns
,data
,fetchData
,loading
,pageCount
和totalRow
和totalRow
。该组件负责渲染表,插入它,并在获取数据时显示loading
旋转器。当用户单击“分页链接”时,fetchData
与新页面索引和页面大小一起调用,该页面大小触发了带有更新的查询参数到服务器的新API调用。
最后,将组件导出为默认导出,可以在应用程序的其他部分导入和使用。
那是客户方面的事!!!露台完成了!
服务器端
现在在服务器端移动,我们将在Express API中使用prisma
作为ORM。
依赖项:
- lodash
- Prisma
- 一杯咖啡
角色数据表模型
// Filename: Roles.js
const { PrismaClient } = require('@prisma/client')
const db = new PrismaClient()
const _ = require('lodash')
exports.roleDatatable = async (
page = 0,
limit = 10,
search = '',
order = []
) => {
try {
var paginate = limit * page - 1
var offset = paginate < 0 ? 0 : paginate
const sort = _.isEmpty(order) ? [] : JSON.parse(_.first(order))
const orderKey = _.isEmpty(sort) ? 'roleName' : sort.id
const orderDirection = _.isEmpty(sort)
? 'desc'
: sort.desc
? 'desc'
: 'asc'
const roles = await db.roles.findMany({
where: {
OR: [
{
roleName: {
contains: search,
},
},
],
isDeleted: false,
},
skip: Number(offset),
take: Number(limit),
orderBy: {
[orderKey]: orderDirection,
},
})
const countTotal = await db.roles.count({
where: {
OR: [
{
roleName: {
contains: search,
},
},
],
isDeleted: false,
},
})
return {
data: roles,
totalRow: countTotal,
totalPage: Math.ceil(countTotal / limit),
}
} catch (error) {
console.log(`Error on roleDatatable: ${error}`)
return false
}
}
函数称为roleDatatable
,该函数根据给定的搜索条件,排序和分页询问数据库以检索角色的分页列表。
该功能采用四个可选参数:page
,limit
,search
和order
。 page
和limit
用于确定页面大小和要返回的记录的数量,而搜索用于根据文本字符串过滤记录。订单是指定order
的数组。
在函数内部,使用paginate
和offset
变量来计算要跳过和采取的记录。 sort
,orderKey
和orderDirection
变量用于指定应对记录进行排序的顺序。
然后,功能使用db.roles.findMany()
查询数据库,传递搜索条件,分页和排序选项。它还查询与搜索标准相匹配的角色的总数,该角色用于计算页面总数。
该函数返回包含分页角色,行总数和页面总数的对象。如果发生错误,它将记录错误并返回false。
帮助者
我需要格式化使用formatResponse
助手发送给客户端的服务器响应
// Filename: helpers/formatResponse.js
module.exports = (
type,
message = 'No desription',
data = [],
pagination = null
) => {
const ALLOWED_TYPES = [
'VALID',
'INVALID',
'FOUND',
'NOT_FOUND',
'INTERNAL_ERROR',
'CREATED',
'NOT_MODIFIED',
'NOT_AUTHORIZED',
'FORBIDDEN',
]
if (!ALLOWED_TYPES.includes(type)) {
throw `${type} is not allowed. Available type is ${ALLOWED_TYPES.join(
', '
)}`
}
return pagination === null
? { type, message, data }
: { type, message, data, pagination }
}
该函数包含四个参数:type
(字符串),message
(默认值为“无描述”的字符串),data
(一个具有默认值为空数组的数组)和pagination
(可选的对象)。
该函数返回带有属性type
,message
和data
的对象,如果pagination
不是null
,则还包括pagination
对象。
返回对象之前,该函数通过将其与允许类型的数组进行比较,检查type
参数是否是允许类型之一。如果不允许使用type
,则会丢弃错误,指示允许哪种类型。
路由器
让我们创建角色datatable的API路由
// Filename: routes/roles.js
const express = require('express')
const formatResponse = require('../../helpers/formatResponse')
const { roleDatatable } = require('../../models/Roles')
const router = express.Router()
router.get('/datatable', async (req, res) => {
const { page, limit, search, order } = req.query
const roles = await roleDatatable(page, limit, search, order)
if (roles) {
res.status(200).json(
formatResponse('FOUND', 'Roles datatable', roles.data, {
totalRow: roles.totalRow,
totalPage: roles.totalPage,
})
)
} else {
res.status(404).json(formatResponse('NOT_FOUND', 'No data roles'))
}
})
module.exports = router
他的代码为端点设置了一个路由器,该路由器返回角色的数据表。端点会听取“/datatable”路径的获取请求。收到请求后,将从请求中提取查询参数(页面,限制,搜索和订单)。然后,它使用查询参数从角色模型中调用roleDatatable
函数。如果roleDatatable
返回数据,则端点将发送带有200个状态代码的响应和包含数据表数据和分页信息的JSON对象。如果roleDatatable
返回没有数据,则端点将发送带有404状态代码和包含错误消息的JSON对象的响应。
formatResponse
函数用于将响应格式化为标准结构。它采用四个参数:type(一个指示响应类型的字符串),消息(提供有关响应的其他详细信息的字符串),数据(响应中包含的数据)和分页(包含的可选对象分页信息)。它返回一个包含四个参数的对象。
fiuh !!!!就是这样...没有旁观者...
如果此隐喻可以帮助您,并且想对Buy me coffee进行帮助,或者说谢谢或只是阅读。
来源隐喻:
Add [React Table - Sever Side Pagination, Search, Sort/Order] #10
mentaphore名称
React Table-断开,搜索,排序/顺序
share您的隐喻故事!
tldr
这是我使用react-table
和prisma
Orm在React JS项目中管理DataTable的完整指南。让我们开始!
client side
//组件文件名:tablepagination.js
import {
arrowlongdownicon ,
arrowlongupicon ,
funnelicon ,
} 来自 “@herioicons/react/react/react/react/24/outline”
import { compricicon } 来自 “@herioicons/react/react/24/solid”
import react 来自 ” React
import {
useasyncdebouncous ,
useglobalfilter ,
usepagination ,
useortby ,
usetable ,
} 来自 “ react-table”
在pl-kos“> {
列,
数据,
fetchdata ,
加载,
在
totalrow ,
在
} ) {
const {
getTableProps ,
gettabledobyprops ,
headerGroups ,
Preparerow ,
页,
canpreviouspage ,
canNextPage ,
pageoptions ,
pagecount ,
gotopage ,
nextpage ,
上一个page ,
setPagesize ,
state : { pageIndex , pagesize , globalfilter , sortby } ,
preglobalfilterredrows ,
setGlobalFilter ,
在pl-kos”>(
{
列,
数据,
在
在
在
初始状态: {
在
在
} , //通过我们的吊杆状态
在
在
在
在
} ,
useglobalfilter ,
useortby ,
usepagination
)
在pl-kos”>( {
preglobalfilterredrows ,
GlobalFilter ,
setGlobalFilter ,
在“ PL-KOS”> {
在PL-S1“> preglobalfilterredrows
在pl-kos“>, setValue ] = react 。 usestate 在
在pl-en“> useasyncdebouncous ( ( 值 ) => {
setglobalfilter ( 值 || undefined )
在pl-kos“>)
return ()
< div
className = {
在
? “ flex flex row joanify-weew”
:“ flex flex-col”
}
>
{ action !== 未定义的?在pl-c1“ >> : null }
< input
在pl-s1“>值 || “” }
onChange = { ( e ) => {
setValue ( e 。 target 。 值 )
onChange ( e 。 target 。 值 )
} }
在pl-s“>` $ { count } 记录...` }
在
className = { `输入输入式输入输入sm w-full max-w-xs焦点:outline-0 mb-2 $ $ {
action !== span> 未定义的?态
在-kos“>}
/ >
在PL-C1“ >>
)
}
react 。 useffeft ( ( ) => {
让 search = GlobalFilter === undefined ? “” : globalfilter
fetchData ( pageSize , pageIndex , 搜索 , sortby )
} , [ fetchdata , pageIndex , pagesize , globalfilter 在pl-kos“>)
return ()
< >
< globalfilter
preglobalfilteredrows = { totalrow }
globalfilter = { globalfilter }
setglobalfilter = { setglobalfilter }
/ >
< div className = ” Overflow-x-auto相对“ >
< table
{ ... getTableProps ( ) }
className = “ table table table table compact-zebra w--完整”
>
在
{ headerGroups 。 map ( ( headerGroup ) => (
< tr { ... headerGroup 。 getheadergrouprops ( ) } > << /span>
{ headerGroup 。 标题 。 map ( ( 列 ) => ()
< th
{ ... 列 。 getheaderprops ()
列 。 getsortbyToggleProps ( )
) }
>
< span >
{ 列 。 issorted ? (
列 。 issorteddesc ? (
< arrowlongdownicon className = ” H-4 W-4内联MR-1“ >
):()
< arrowlongupicon className = ” H-4 W-4内联MR-1“ >
)
):()
< funnelicon className = ” H-4 W-4内联MR-1“ >
) }
在PL-C1“ >>
{ 列 。 渲染 ( “ header” ) }
在PL-C1“ >>
) ) }
在PL-C1“ >>
) ) }
在PL-C1“ >>
< tbody { ... gettablebodyprops ( ) } >
{ page 。 长度 > 0 ? (
在pl-kos”>( ( row , i ) => {
在pl-kos“>)
return ()
< tr
{ ... 行 。 getrowprops ( ) }
className = “ hover”
>
{ 行 。 单元格 。 map ( ( cell ) => {
return ()
< td { ... cell 。 getCellProps ( ) } > << /span>
在pl-en“>渲染 ( ) }
在PL-C1“ >>
)
在
在PL-C1“ >>
)
} )
):()
在PL-C1“> = “徘徊” >
在pl-c1“> = { 10000 } className = “ text-center” >
找不到数据!
在PL-C1“ >>
在PL-C1“ >>
) }
在PL-C1“ >>
在PL-C1“ >>
{ 加载? (
< div className = ”绝对top-0底部-0左0左-0右0 bg-bg-bg-opacity-5圆形-MD Z-20 Z-20 Flex项目-center jusify-center“ >
< div className = ”绝对P-3 BG-WHITE W-36 Shadow-MD圆形MD圆形MD文本中心“ >
< div className = “ flex animate-pulse” >
< clackicon className = ” W-6 H-6 MR-1“ /在“ PL-KOS”>}
在span class =“ pl-c1”> < / span >
在PL-C1“ >>
在PL-C1“ >>
在PL-C1“ >>
在
在PL-C1“ >>
< div className = “ flex flex-row jusify-weew” >
< div className = ” MT-2“ >
< span >
Halaman { “” }
< strong >
{ pageIndex + 1 } dari { pageOptions 。 length }
在pl-c1“ >> { “” }
总计 < strong > { preglobalfilteredrows 。 >长度 } < / strong > { “” }
在PL-C1“ >>
< span >
|跳到页面: { “” }
< input
type = “ number”
defaultvalue = { pageIndex + 1 }
onChange = { ( e ) => {
在PL-S1“> e 。 target 。 值
? 数字 ( e 。 target 。 值 ) - 1
: 0
gotopage ( page )
} }
className = “输入输入输入输入输入input-sm w- 20 Max-W-XS焦点:概述-0“
/ >
在pl-c1“ >> { “” }
< select
在pl-s1“> pageize }
onChange = { ( e ) => {
setPagesize ( number ( e 。 目标 。 值 ) )
} }
className = “ select select-bordered select-bordered select-sm w-- 30 Max-W-XS焦点:概述-0“
>
{ [ 10 , 20 , 30 , 40 , 50 ] 。 map ( ( pageSize ) => (
在pl-c1“> = { 值 = {在
tampilkan { pagesize } baris
< / option >
) ) }
在PL-C1“ >>
在PL-C1“ >>
< div className = ” MT-2“ >
< 按钮
className = “ btn btn-xs”
onclick = { ( ) => gotopage ( 0 ) }
禁用 = { ! canpreviouspage } }
>
{ “ <<” }
< / button > { “” }
< 按钮
className = “ btn btn-xs”
onclick = { ( ) => 上一个page ( ) }
禁用 = { ! canpreviouspage } }
>
{ “ <” }
< / button > { “” }
< 按钮
className = “ btn btn-xs”
onclick = { ( ) => nextpage ( ) }
禁用 = { ! cannextpage }
>
{ “>” }
< / button > { “” }
< 按钮
className = “ btn btn-xs”
onclick = { ( ) => gotopage ( pagecount - 1 ) }
禁用 = { ! cannextpage }
>
{ “ >>” }
< / button > { “” }
在PL-C1“ >>
在PL-C1“ >>
< / >
)
}
导出 默认 tablepagination
依赖性 h3>
上面的组件生成一个具有全局过滤,分页,排序和加载功能的表。它使用以下依赖项:图标的@heroicons/react
,基本库的react
,react-table
用于生成表。
table实例
TablePagination
是一个接收用于显示表的道具的函数。这些道具是表列的columns
,要显示的数据的data
,在更改分页时获取数据的fetchData
,loading
以显示加载图标,pageCount
,pageCount
,totalRow
for the Row Count和actions
for actions
for actions
for actions
for actions
for actions
过滤器行上的额外动作按钮。
该函数使用useTable
创建一个表实例,并将表的状态初始化为第一页,每页十行。它还将manualPagination
,manualGlobalFilter
和manualSortBy
设置为true
,以便组件可以控制这些功能。
global滤波 h3>
GlobalFilter
组件显示用于过滤表数据的输入搜索框。它还收到预先过滤的行计数,并使用useAsyncDebounce
延迟搜索过滤器,直到用户停止键入。这有助于减少搜索时对服务器的不必要调用。
然后,使用React-Table库中的 table身体
getTableProps
和getTableBodyProps
方法创建表主体和标头。 headerGroups
和页面用于使用MAP函数分别在标题列和表数据上映射。每行都调用prepareRow
方法,以实现getRowProps
和getCellProps
方法的使用来定型行和单元格。
Sorting
通过将getHeaderProps
方法添加到列标题并使用column.getSortByToggleProps()
方法来启用排序功能。此方法在表状态中更新sortBy
对象,并将适当的类和图标添加到排序的列中。
pagination h3>
使用usePagination
和pageCount
,canPreviousPage
,canNextPage
,pageOptions
,pageOptions
,gotoPage
,nextPage
,nextPage
,previousPage
和setPageSize
方法启用了分页功能。这些方法用于生成分页控件并在用户与它们交互时更新表状态。
loading h3>
最后,通过检查loading
是否为true并在从服务器获取数据时在表中显示加载图标。
Helpers
当我们使用API进行分页时,我们还需要一个助手到发送到服务器之前的序列化端点URL。
//文件名:uriserialized.js
在pl-kos“> {
在“ pl-s1”> val ) {
return object 。 原型 。 toString 。 呼叫 ( val ) ==== “ [对象阵列]跨度>
} ,
在“ pl-s1”> val ) {
在=“ pl-c1”> null || util 。 typeof ( val )
} ,
在“ pl-s1”> val , type ) {
return ( type || “ undefined” ) === typeof val
} ,
在“ PL-S1”> obj , fun ) {
如果 ( util 。 isnil ( obj ) ) return //空值
如果 ( ! util 。 typeof ( obj , “ object” span> ) ) obj = [ obj ] //转换为阵列
如果 ( util 。 isarray ( obj ) ) { {
//在数组上迭代
for ( var i = 0 , l = obj 。 长度 ; i < l ; i ++ )
在pl-kos”>( null , obj [ i ] , i , obj )
} else {
//迭代对象
for ( var key in obj )
在pl-kos“>。 hasownproperty 。 呼叫 ( obj ,在
在pl-kos”>( null , obj [ key ] , key , obj )
}
} ,
}
导出 const uriserialized = ( params ) => {
让 配对 = [ ]
const encodevalue = ( v ) => {
如果 ( util 。 typeof ( v , “ object” ) span> ) v = json 。 stringify ( v )
return encodeuricomponent ( v )
}
在pl-kos”>( params , ( val , 键 ) => {
让 isnil = ( span> $ {span> error span'
return false
}
}
此函数使使用Axios HTTP客户端库的角色数据登录到API端点。
该函数接受可选参数queryOptions
,可用于将查询参数传递到API端点。如果queryOptions
未零,则将使用从“ ../ utils/uriserializatized导入的uriSerialized
函数”将其转换为序列化的URI字符串。然后将序列化的URI字符串附加到API端点URL。
然后,该函数将HTTP请求发送到AXIO的API端点,并等待响应数据。如果请求成功,则返回响应数据。如果请求失败,则错误消息将记录到控制台,并且函数返回false
。
角色datatable
woooooooooooooooooooooooooooo ....在RoleDatatable
组件中实现
import react , { usestate , usecallback , usememo } < /span> 来自 “ react”
import { getRoledatabable } 来自 “ pl-s”>“ ../../ ../ ../ services/wars” < /span>
import tablepagination 来自 ” ../../ tablepagination“
import { pencilssquareicon , trashicon } 来自 “@heroicons/react/react/24/solid”
function roledataitable ( ) {
在pl-kos“>, setData ] = usestate ( [ ] )
在pl-kos“>, setloading ] = usestate ( false )
在pl-kos“>, setPageCount ] = usestate ( 0 )
在pl-kos“>, settotalrow ] = usestate ( 0 )
在pl-en“> usecallback ()
async ( pagesize , pageIndex , 搜索 , 顺序 ) => {
在pl-kos“>)
在pl-kos“> {
在
在
在
在
}
在pl-k“>等待 getRoledatabable ( queryOptions )
setData ( items 。 data )
setPageCount ( items 。 分页 。 TotalPage )
settotalrow ( 项目 。 分页 。 totalrow )
在pl-kos“>)
} ,
[ ]
)
在pl-en“> usememo ()
( ) => [
{
header :“#” ,
在
单元格:( { row } ) => => `r# $ { row 。 原始 。 rooleid } ` ,
在
} ,
{
header :“角色名称” ,
在
} ,
{
header :“角色键” ,
concector :“ colekey” ,
单元格:( { row } ) => => 行 。 原始 。<<<< /span> roulekey ,
} ,
{
header :“ action” ,
concector :( { row } ) => => {
return ()
< div className = ” flex gap-2“ >
< button className = “ btn-btn-xs btn-info” >
< pencilsquareicon className = “ W-4 H-4” / >
< / button >
< button className = “ btn-xs btn-error” >
< trashicon className = “ W-4 H-4” / >
< / button >
在PL-C1“ >>
)
} ,
} ,
] ,
[ ]
)
return ()
< 节 >
< tablepagination
在pl-s1“>列 }
data = { data }
fetchdata = { fetchdata }
加载 = { 加载 }
在pl-s1“> pagecount }
在pl-s1“> totalrow }
/ >
在PL-C1“ >>
)
}
导出 默认 roledatatable
这是一个功能性的反应组件,可从服务器获取数据,将其显示在打页表中,并为用户提供每个项目的某些操作按钮。
该组件使用useState
钩子维护其内部状态,其中包括data
,loading
,pageCount
和totalRow
。 fetchData
函数是一个useCallback
挂钩,它使用一些查询参数对服务器进行API调用,以获取数据,更新状态变量并设置加载标志。
该组件还使用useMemo
钩记录包含一系列对象的columns
对象,该对象定义了表列的标题,其访问者函数和Cell
函数,该函数返回每行的相应值。该表的最后一列有两个按钮PencilSquareIcon
和TrashIcon
,可允许用户编辑或删除项目。
TablePagination
组件是一种自定义组件,可接收columns
,data
,fetchData
,loading
,pageCount
和totalRow
和totalRow
。该组件负责渲染表,插入它,并在获取数据时显示loading
旋转器。当用户单击“分页链接”时,fetchData
与新页面索引和页面大小一起调用,该页面大小触发了带有更新的查询参数到服务器的新API调用。
最后,将组件导出为默认导出,可以在应用程序的其他部分导入和使用。
那是客户方面的事!!!露台完成了!
Server
现在在服务器端移动,我们将在Express API中使用prisma
作为ORM。
依赖项:
- lodash
- Prisma
- 一杯咖啡
role datatable模型
//文件名:roles.js
const { prismaclient } = require ( '@prisma/client' )
在pl-k“> new prismaclient ( )
在pl-en“> require ( 'lodash' )
enforts 。 roledatabable = async ()
在pl-kos“>,
在pl-kos“>,
在“ PL-KOS”>,
在PL-KOS“>]
) => {
尝试 {
在pl-s1“> limit * page - 1
var offset = : sort 。 id
const orderDirection = _ 。 isempty ( sort )
? 'desc'
: sort 。 desc
? 'desc'
:'asc'
在pl-k“>等待 db 。 角色 。 findmany () {
其中: {
或: [
{
rolename : {
在
} ,
} ,
] ,
在
} ,
在“ pl-s1”>偏移 ) ,
在“ pl-s1”> limit ) ,
orderby : {
在“ PL-S1”> OrderDirection ,
} ,
} )
在pl-k“>等待 db 。 角色 。 count () {
其中: {
或: [
{
rolename : {
在
} ,
} ,
] ,
在
} ,
} )
return {
在
在
在“ pl-en”> ceil ( counttotal / limit ) ,
}
在pl-s1“>错误 ) {
console 。 log ( `roledataTable上的错误: $ { 错误 } ` )
return false
}
}
函数称为roleDatatable
,该函数根据给定的搜索条件,排序和分页询问数据库以检索角色的分页列表。
该功能采用四个可选参数:page
,limit
,search
和order
。 page
和limit
用于确定页面大小和要返回的记录的数量,而搜索用于根据文本字符串过滤记录。订单是指定order
的数组。
在函数内部,使用paginate
和offset
变量来计算要跳过和采取的记录。 sort
,orderKey
和orderDirection
变量用于指定应对记录进行排序的顺序。
然后,功能使用db.roles.findMany()
查询数据库,传递搜索条件,分页和排序选项。它还查询与搜索标准相匹配的角色的总数,该角色用于计算页面总数。
该函数返回包含分页角色,行总数和页面总数的对象。如果发生错误,它将记录错误并返回false。
高 h3>
我需要使用formatResponse
助手
//文件名:helpers/formatresponse.js
在PL-C1“> = ()
type ,
消息 = 'no deSription' ,
data = [ ] ,
分页 = null null
) => {
在pl-kos“> [
'有效' ,
'无效' ,
'找到' ,
'not_found' ,
'internal_error' ,
'创建' ,
'not_modified' ,
'not_authorized' ,
'禁止' ,
]
如果 ( ! wasse_types 。 包括 ( type ) ) {
在span> type } 不允许。可用类型IS $ { washe_types 。 join (
态
在`
}
在=“ PL-C1”> null
? { type ,消息, data }
: { type ,消息, data < Span class =“ PL-KOS”>,分页}
}
该函数包含四个参数:type
(字符串),message
(默认值为“无描述”的字符串),data
(一个具有默认值为空数组的数组)和pagination
(可选的对象)。
该函数返回带有属性type
,message
和data
的对象,如果pagination
不是null
,则还包括pagination
对象。
返回对象之前,该函数通过将其与允许类型的数组进行比较,检查type
参数是否是允许类型之一。如果不允许使用type
,则会丢弃错误,指示允许哪种类型。
Router h3>
让我们创建角色datatable的API路由
//文件名:路由/roles.js
在pl-en“> require ( 'express'express' )
在pl-en“> require ( '../ helpers/formatresponse' )
const { roledatabable } = require ( '../../模型/角色' )
在pl-s1“> express 。 路由器 ( )
路由器 。 get ( '/datatable' , async ( req ,< /span> res ) => < SPAN类=“ PL-KOS”> {
在=“ pl-kos”>,搜索, order } = req 。
在pl-k“>等待 roledataTable ( page , limit在pl-kos“>)
如果 ( 角色 ) {
在pl-kos”>( 200 ) 。 json ()
在=“ pl-kos”>, '角色datatable' , 角色 。 data , {
在“ PL-C1”> Totalrow ,
在“ PL-C1”> TotalPage ,
} )
)
} else {
在pl-kos”>( 404 ) 。 json ( formatresponse ( 'not_found' , '无数据角色' ) )
}
} )
在pl-c1“> = 路由器
他的代码为端点设置了一个路由器,该路由器返回角色的数据表。端点会听取“/datatable”路径的获取请求。收到请求后,将从请求中提取查询参数(页面,限制,搜索和订单)。然后,它使用查询参数从角色模型中调用roleDatatable
函数。如果roleDatatable
返回数据,则端点将发送带有200个状态代码的响应和包含数据表数据和分页信息的JSON对象。如果roleDatatable
返回没有数据,则端点将发送带有404状态代码和包含错误消息的JSON对象的响应。
formatResponse
函数用于将响应格式化为标准结构。它采用四个参数:type(一个指示响应类型的字符串),消息(提供有关响应的其他详细信息的字符串),数据(响应中包含的数据)和分页(包含的可选对象分页信息)。它返回一个包含四个参数的对象。
fiuh !!!!就是这样...没有旁观者...
a演示/repos链接
无响应