/* eslint-disable react-hooks/exhaustive-deps */ import React, { useRef, useEffect, useMemo } from 'react' import { classnames, run, isNumber, delay, isArray, isObject, intersection, isExist, isFunction } from '@fexd/tools' import { useGetState, useMemoizedFn, useLatest, useSafeState } from 'ahooks' import { Result } from 'ahooks/es/useRequest/src/types' import 'ahooks/es/useRequest/src/types' import { catchPromise, useProState, Hook, useRequest, isAhooksUseRequestResult } from '@fexd/pro-utils' import { ProFieldValueTypes, ProForm } from '@fexd/pro-form' import useValueTypePlugin from '../valueType' import { createPlugin } from '../../utils' import { ProTableProps } from '../../types' import useQueryFieldsConfig from './useFieldsConfig' import QueryFieldForm from './QueryField' import usePersistForm from './usePersistForm' import usePaginationParams from './usePaginationParams' import handleAsyncActionResponse from '../actions/handleAsyncActionResponse' import useMockDataSource from './useMockDataSource' export const useQueryFieldPlugin = createPlugin((props: ProTableProps) => { const { types } = useValueTypePlugin(() => []) const { pure, queryWrapperStyle = pure ? { padding: 0 } : {}, queryFieldFilterEmptyParam, queryFieldTriggerOnEnter, onQuery, pagination, defaultPaginationParams, initialPaginationParams, queryFieldLayout, defaultPageSize, queryFieldServiceOptions, manualQuery, normalizeFieldValue, rowSelection, rowKey, dataSource: propDataSource, mockDataSource: needMockDataSource, } = props const [form] = ProForm.useForm() const persistFormProState = usePersistForm(form) const [selectedItems, setSelectedItems, getSelectedItems] = useGetState([]) const { state: paginationParams, setState: setPaginationParams, getState: getPaginationParams, } = usePaginationParams() const extraParamsProState = useProState<{ filters?: any; sorters?: any; [key: string]: any }>({ filters: {}, sorters: {}, }) const { state: extraParams, setState: setExtraParams, getState: getExtraParams } = extraParamsProState const { queryFields } = useQueryFieldsConfig() const hasQueryFields = queryFields?.length > 0 const queryingParamsRef = useRef>({}) const getQueryingParams = useMemoizedFn(() => ({ ...queryingParamsRef.current })) const queryingExtraParamsRef = useRef>({}) const getQueryingExtraParams = useMemoizedFn(() => ({ ...queryingExtraParamsRef.current })) const proFormRef = ProForm.useRef() const normalizeFormValues = useMemoizedFn((formValues: any) => { const fieldsMapRef = proFormRef?.current?.fieldsMapRef return !normalizeFieldValue ? { ...formValues } : Object.fromEntries( Object.entries(formValues).map(([key, value]) => [ key, run(types?.[fieldsMapRef?.current?.[key]?.current?.type as ProFieldValueTypes], 'normalizeValue', value) ?? value, ]), ) }) const getQueryParams = useMemoizedFn((params = {}, extra = {}) => { const currentParams = Object.fromEntries( Object.entries({ ...normalizeFormValues({ ...(form.getFieldsValue() ?? {}), ...params, }), ...(getPaginationParams() ?? paginationParams), }).filter(([, value]) => { if (queryFieldFilterEmptyParam) { return isExist(value) && value !== '' } return true }), ) const currentExtraParams = { ...(getExtraParams() ?? extraParams), ...extra, } return { params: currentParams, extraParams: currentExtraParams, } }) const { mockDataSource, updateMockDataSource, createMockDataSource } = useMockDataSource() const innerService = useRequest( async (params = {}, extra = {}) => { const { params: currentParams, extraParams: currentExtraParams } = getQueryParams(params, extra) if (!isFunction(onQuery) && needMockDataSource !== false) { queryingParamsRef.current = { ...currentParams } queryingExtraParamsRef.current = { ...currentExtraParams } const { page, pageSize } = currentParams as any const mockDataSource = updateMockDataSource() return { success: true, data: [page, pageSize].every(isNumber) ? mockDataSource.slice((page - 1) * pageSize, page * pageSize) : mockDataSource, total: mockDataSource?.length, } } const response = await run(onQuery, undefined, currentParams, currentExtraParams) const success = response?.success ?? true handleAsyncActionResponse(response) if (success) { queryingParamsRef.current = { ...currentParams } queryingExtraParamsRef.current = { ...currentExtraParams } } // if (!success && isString(response?.message)) { // message.error(response?.message) // } return { success, data: isArray(response?.data) ? response?.data : [], total: response?.total, } }, { manual: true, throttleWait: 500, debounceWait: 32, debounceMaxWait: 1000, // loadingDelay: 300, // refreshOnWindowFocus: true, // 离屏重新请求 ...queryFieldServiceOptions, }, ) const onQueryIsService = useMemo(() => isAhooksUseRequestResult(onQuery), [onQuery]) const queryService: Result = onQueryIsService ? onQuery : (innerService as any) const [isSearched, setIsSearched] = useSafeState(false) const search = useMemoizedFn(async (params: any = {}) => { const [error] = await catchPromise(form.validateFields()) setSelectedItems([]) if (error) { setIsSearched(false) queryingParamsRef.current = {} queryingExtraParamsRef.current = {} queryService.mutate({ success: false, data: [], total: 0, }) return } const formValues = form.getFieldsValue() ?? {} const rawParams = { ...formValues, ...(run(getPaginationParams) ?? paginationParams), ...params, } const { params: currentParams, extraParams: currentExtraParams } = getQueryParams(rawParams) persistFormProState.setState(currentParams) const response = await queryService.runAsync(currentParams, currentExtraParams) setIsSearched(true) return response }) const dataSource = propDataSource ?? queryService?.data?.data ?? [] const getDataSource = useMemoizedFn(() => dataSource) const refresh = useMemoizedFn(async (params: any = {}) => { const refreshParams = { ...(form.getFieldsValue() ?? {}), ...paginationParams, ...params, } await search(refreshParams) if (paginationParams?.page === 1) { return } // 如果当前页数大于 1,刷新后无数据,则回到第一页 await delay(100) const dataSource = getDataSource() const hasData = dataSource?.length > 0 if (!hasData) { setPaginationParams({ page: 1 }) await delay(100) await search({ ...refreshParams, page: 1, }) } }) const renderAutoQueryTrigger = useMemoizedFn(() => ( {() => { useEffect(() => { const applyDefaultSelectedRowKeys = () => { const defaultSelectedRowKeys = rowSelection!?.defaultSelectedRowKeys ?? [] if (defaultSelectedRowKeys!?.length > 0) { const currentDataSource = getDataSource() setSelectedItems( currentDataSource.filter((item: any) => { const key = item?.[String(run(rowKey, undefined, item))] return defaultSelectedRowKeys.includes(key) }), ) } } if (!manualQuery) { setTimeout(() => refresh().then(applyDefaultSelectedRowKeys), 128) } else { setTimeout(applyDefaultSelectedRowKeys, 128) } }, [manualQuery]) return null }} )) const render = useMemoizedFn(() => (
{ if (!queryFieldTriggerOnEnter) { return } setPaginationParams({ page: 1 }) await delay(100) refresh({ page: 1, }) }} />
)) const latestServiceRef = useLatest(queryService) const getService = useMemoizedFn(() => latestServiceRef.current) return useMemo( () => ({ hasQueryFields, form, render, service: queryService, loading: queryService.loading, dataSource, mockDataSource, createMockDataSource, updateMockDataSource, paginationParams, setPaginationParams, getPaginationParams, selectedItems, setSelectedItems, getSelectedItems, getQueryingParams, getQueryingExtraParams, getDataSource, refresh, search, setExtraParams, renderAutoQueryTrigger, getService, isSearched, normalizeFormValues, getQueryParams, proFormRef, }), [ hasQueryFields, form, render, queryService, queryService.loading, dataSource, mockDataSource, createMockDataSource, updateMockDataSource, paginationParams, setPaginationParams, getPaginationParams, selectedItems, setSelectedItems, getSelectedItems, getQueryingParams, getQueryingExtraParams, setExtraParams, renderAutoQueryTrigger, getService, isSearched, normalizeFormValues, getQueryParams, ], ) }, 'queryField') export default useQueryFieldPlugin