最近因为使用列表展示的需求有点多,就想着把列表分页筛选的逻辑抽象一下。看了umi的一个useTable的hook,也不能满足业务需要,于是就自己写了一个,支持本地分页筛选和接口分页筛选。
思路就是,筛选的字段都使用form表单控制,然后在hook里面将form和table联合起来。
下面贴出源码
1 import { TableProps, PaginationProps } from '@slardar/antd'; 2 import React, { useEffect } from 'react'; 3 import { 4 PaginationConfig, 5 SorterResult, 6 TableCurrentDataSource 7 } from 'antd/lib/table'; 8 import { useDeepCompareEffect } from '@byted-woody/slardar'; 9 10 export type WrappedFormUtils = any; 11 12 export interface TableControlStateextends TableProps { 13 pagination: PaginationProps; 14 sorter?: SorterResult; 15 loading?: boolean; 16 } 17 18 // 搜索参数描述 19 export interface SearchDescribe { 20 // 字段名 21 fieldName: string; 22 iniValue?: any; 23 // 输入值解析,比如日期输入解析 24 decodeFn?: any; 25 // 自定义搜索函数 26 searchFn?: (record: DataRow, desc: SearchDescribe) => boolean; 27 // 解析后的值 28 searchValue?: any; 29 // 调用接口或者只在本地过滤 30 searchMod?: 'api' | 'local'; 31 // 搜索的字段,默认只搜索当前字段 32 searchFields?: string[]; 33 } 34 35 export interface DataReceive { 36 pageSize?: number; 37 pageIndex?: number; 38 pageTotal?: number; 39 pageData: any[]; 40 } 41 42 export type DataRow = { [key: string]: any }; 43 export type DataApiGet = ( 44 apiParams, 45 pagination?: { pageIndex?: number; pageSize?: number } 46 ) => Promise; 47 48 export interface FormTableReq { 49 form: WrappedFormUtils; 50 getDataApi: DataApiGet; 51 getDataParam?: { [key: string]: any }; 52 // 表单的字段解析 53 includeFormFields?: (SearchDescribe | string)[]; 54 // 本地分页 55 localPagination?: boolean; 56 // 本地搜索 57 localSearch?: boolean; 58 // 本地分页+搜索 59 local?: boolean; 60 afterFetchData?: (v: any) => void; 61 validateParam?: (param: any) => boolean; 62 } 63 64 const defaultTableState: TableControlState= { 65 pagination: { current: 1, total: 0, pageSize: 10 }, 66 dataSource: [], 67 loading: true 68 }; 69 70 export type FormTableRet = [ 71 TableControlState, 72 { fetchData: () => void } 73 ]; 74 75 export function useFormTable(options: FormTableReq): FormTableRet { 76 if (options.local) { 77 options?.includeFormFields?.forEach(d => (d.searchMod = 'local')); 78 return useFormTableLocal(options); 79 } else { 80 return useFormTableDB(options); 81 } 82 } 83 84 // 本地分页筛选版本 85 export function useFormTableLocal(options: FormTableReq): FormTableRet { 86 let { form, getDataApi, includeFormFields } = options; 87 let currentFormValue = form.getFieldsValue(); 88 // 缓存数据 89 let cacheDataListRef = React.useRef([]); 90 let [tableState, setTableState] = React.useState<TableControlState>( 91 defaultTableState 92 ); 93 let searchApiParam = {}; 94 let searchLocalParam: SearchDescribe[] = []; 95 if (Array.isArray(includeFormFields)) { 96 includeFormFields?.forEach(describe => { 97 if (typeof describe === 'string') { 98 let value = currentFormValue[describe]; 99 searchApiParam[describe] = value;100 } else {101 let value = currentFormValue[describe.fieldName];102 if (describe.decodeFn) {103 value = describe.decodeFn(value);104 }105 if (describe.searchMod === 'api') {106 searchApiParam[describe.fieldName] = value;107 } else {108 searchLocalParam.push(109 Object.assign({ searchValue: value }, describe)110 );111 }112 }113 });114 } else {115 searchApiParam = currentFormValue;116 }117 118 function getTableApiData() {119 getDataApi(searchApiParam).then(data => {120 cacheDataListRef.current = data.pageData;121 setTableState(prevState => {122 return Object.assign({}, prevState, { dataSource: [] });123 });124 });125 }126 127 useEffect(getTableApiData, []);128 129 let { data, total } = calculatePageData(130 tableState,131 cacheDataListRef.current,132 searchLocalParam133 );134 135 function onSorterChange(136 _pagination: PaginationConfig,137 _filters: Record,138 _sorter: SorterResult,139 _extra: TableCurrentDataSource140 ) {141 setTableState(prevState => {142 return Object.assign({}, prevState, { sorter: _sorter });143 });144 }145 146 let newPage: PaginationProps = {147 total: total,148 onChange: (page, pageSize) => {149 setTableState(prevState => {150 prevState.pagination.pageSize = pageSize;151 prevState.pagination.current = page;152 return Object.assign({}, prevState);153 });154 }155 };156 157 let finalPagination: PaginationProps = Object.assign(158 {},159 tableState.pagination,160 newPage161 );162 163 return [164 { pagination: finalPagination, dataSource: data, onChange: onSorterChange },165 { fetchData: getTableApiData }166 ];167 }168 169 // 接口分页筛选版本 待完善170 export function useFormTableDB(options: FormTableReq): FormTableRet {171 let { form, getDataApi, includeFormFields } = options;172 let currentFormValue = form.getFieldsValue();173 let [state, setState] = React.useState<TableControlState>(174 defaultTableState175 );176 let searchApiParam: { [key: string]: any } = {};177 let onceRef = React.useRef(false);178 // 计算接口参数179 if (Array.isArray(includeFormFields)) {180 includeFormFields?.forEach(describe => {181 if (typeof describe === 'string') {182 let value = currentFormValue[describe];183 searchApiParam[describe] = value;184 } else {185 let value = currentFormValue[describe.fieldName];186 if (!onceRef.current && describe.iniValue) {187 value = describe.iniValue;188 }189 if (describe.decodeFn) {190 value = describe.decodeFn(value);191 Object.assign(searchApiParam, value);192 } else {193 searchApiParam[describe.fieldName] = value;194 }195 }196 });197 } else {198 searchApiParam = currentFormValue;199 }200 Object.assign(searchApiParam, options.getDataParam);201 const pageParam = {202 pageIndex: state.pagination.current,203 pageSize: state.pagination.pageSize204 };205 206 function getTableApiData() {207 if (options.validateParam && !options.validateParam(searchApiParam)) {208 return;209 }210 setState(prevState => {211 return Object.assign({}, prevState, {212 loading: true213 } as TableControlState);214 });215 getDataApi(searchApiParam, pageParam).then(data => {216 const { pageData, pageTotal } = data;217 onceRef.current = true;218 setState(prevState => {219 return Object.assign({}, prevState, {220 dataSource: pageData,221 pagination: {222 current: pageParam.pageIndex,223 total: pageTotal || 0,224 pageSize: pageParam.pageSize225 },226 loading: false227 } as TableControlState);228 });229 // 将表单数据同步到query230 if (options.afterFetchData) {231 options.afterFetchData(currentFormValue);232 }233 });234 }235 useDeepCompareEffect(getTableApiData, [searchApiParam, pageParam]);236 237 function onSorterChange(238 _pagination: PaginationConfig,239 _filters: Record,240 _sorter: SorterResult,241 _extra: TableCurrentDataSource242 ) {243 setState(prevState => {244 return Object.assign({}, prevState, { sorter: _sorter });245 });246 }247 248 let finalPagination: PaginationProps = Object.assign(249 {250 total: state.pagination.total,251 onChange: (page, pageSize) => {252 setState(prevState => {253 prevState.pagination.pageSize = pageSize;254 prevState.pagination.current = page;255 return Object.assign({}, prevState);256 });257 }258 },259 state.pagination260 );261 let dataSource = state.dataSource;262 if (options.localPagination) {263 let { data, total } = calculatePageData(state, state.dataSource as any, []);264 finalPagination.total = total;265 dataSource = data;266 }267 268 return [269 {270 pagination: finalPagination,271 dataSource: dataSource,272 onChange: onSorterChange,273 loading: state.loading274 },275 { fetchData: getTableApiData },276 state277 ] as any;278 }279 280 // 排序,筛选,计算分页数据281 function calculatePageData(282 state: TableControlState,283 dataList: DataRow[],284 param: SearchDescribe[]285 ) {286 let { pagination, sorter } = state;287 let { current = 1, pageSize = 10 } = pagination;288 let copyDataList = Array.from(dataList);289 // 排序290 if (sorter?.column) {291 let order = sorter.order;292 let sortField = sorter.columnKey;293 copyDataList = copyDataList.sort((a, b) => {294 if (order === 'ascend') {295 return a[sortField] - b[sortField];296 } else {297 return b[sortField] - a[sortField];298 }299 });300 }301 // 筛选302 if (Array.isArray(param) && param.length > 0) {303 copyDataList = copyDataList.filter(function filter(v) {304 return param.every(desc => {305 let { fieldName, searchValue, searchFields, searchFn } = desc;306 let fieldValue = v[fieldName];307 let searchString = searchValue;308 if (!searchString) {309 return true;310 }311 if (searchFn) {312 return searchFn(v, desc);313 }314 if (315 typeof fieldValue !== 'string' ||316 typeof searchString !== 'string'317 ) {318 return true;319 }320 if (searchFields?.length) {321 return searchFields?.some(fieldName => {322 let value = v[fieldName];323 if (typeof value === 'string') {324 value.includes(searchString);325 }326 return false;327 });328 } else {329 return fieldValue.includes(searchString);330 }331 });332 });333 }334 // 分页335 let displayData = copyDataList.slice(336 (current - 1) * pageSize,337 current * pageSize338 );339 // 默认空数据的展示340 displayData.forEach(d => {341 Object.entries(d).forEach(([k, v]) => {342 if (v !== 0 && !v) {343 d[k] = '---';344 }345 });346 return d;347 });348 return { data: displayData, total: copyDataList.length };349 }
下面是业务代码demo
1 import React, { FC } from 'react'; 2 import { Form, FormComponentProps, Input, Table } from '@slardar/antd'; 3 import { useFormTable } from '@slardar/common-modules'; 4 5 const FormItem = Form.Item; 6 7 interface IProps extends FormComponentProps {} 8 const DemoComponent: FC= function(props) { 9 const form = props.form;10 let [tableState] = useFormTable({11 form: props.form,12 getDataApi: () => Promise.resolve([] as any),13 includeFormFields: ['name', 'search']14 });15 16 return (17 18 19 20 {form.getFieldDecorator('name')(21 <Input.Search22 style={{ marginLeft: 16, width: 150 }}23 placeholder="名称"24 />25 )}26 27 28 {form.getFieldDecorator('search')(29 <Input.Search30 style={{ marginLeft: 16, width: 150 }}31 placeholder="评论"32 />33 )}34 35 36 37 38 );39 };40 41 export const Demo = Form.create()(DemoComponent);
1 1 import { useRef, useEffect } from 'react'; 2 2 import _ from 'lodash'; 3 3 export function useDeepCompareEffect(fn, deps: T) { 4 4 // 使用一个数字信号控制是否渲染,简化 react 的计算,也便于调试 5 5 let renderRef = useRef(0); 6 6 let depsRef = useRef(deps); 7 7 if (!_.isEqual(deps, depsRef.current)) { 8 8 renderRef.current++; 9 9 }10 10 depsRef.current = deps;11 11 return useEffect(fn, [renderRef.current]);12 12 }
Copyright © 2004-2024 Ynicp.com 版权所有 法律顾问:建纬(昆明)律师事务所 昆明市网翼通科技有限公司 滇ICP备08002592号-4