Eric-art-coder / my-tools

存储一些js工具,练习技术和日常使用
0 stars 0 forks source link

react 后台管理系统好的组件和hooks记录 #1

Open Eric-art-coder opened 7 months ago

Eric-art-coder commented 7 months ago

背景

很多后台管理系统都需要筛选条件、搜索等功能

核心实现

import React, { FC, useState, useEffect } from 'react'
import { Form, Input, Button, Select, DatePicker } from 'antd'
import './index.less'
import moment from "moment";

const { RangePicker } = DatePicker

interface IProps {
  configs: Array<any>
  onSubmit: (params: any) => void,
  showSort?: Boolean;
  handlerSort?: (params: any) => void
}

interface SearchFormConfigItem {
  name: string
  title: string
  type: string
  isRequired?: boolean
  message?: string
  placeholder?: string
  style?: object
  [key: string]: string
}

const SearchForm: FC<IProps> = ({ configs, onSubmit, handlerSort, showSort }) => {
  const [form] = Form.useForm()
  const [, forceUpdate] = useState({})
  useEffect(() => {
    forceUpdate({})
  }, [])
  return (
    <div className="SearchForm">
      <Form form={form} layout="inline" onFinish={onSubmit}>
        {configs.map((config) => {
          return (
            <Form.Item
              key={config.name}
              label={config.title||""}
              name={config.name}
              rules={[
                {
                  required: config.isRequired,
                  message: config.message
                }
              ]}
            >
              {renderFormItem(config, form)}
            </Form.Item>
          )
        })}
        <Form.Item shouldUpdate>
          {() => (
            <>
              <Button className="borderRadius4" style={{width: 80}} type="primary" htmlType="submit" disabled={!!form.getFieldsError().filter(({ errors }) => errors.length).length}>
                搜索
              </Button>
              <Button
                className="borderRadius4"
                style={{ margin: '0 8px', width: 80 }}
                onClick={() => {
                  form.resetFields()
                  setTimeout(()=> {
                    form.submit();
                  }, 100)
                }}
              >
                重置
              </Button>
              {
                showSort ?<Button
                className="borderRadius4"
                type="primary"
                style={{ margin: '0 8px', width: 80 }}
                onClick={handlerSort}
              >
                排序
              </Button> : null
              }
            </>
          )}
        </Form.Item>
      </Form>
    </div>
  )
}

const renderFormItem = (config: SearchFormConfigItem, form) => {
  switch (config.type) {
    case 'select':
      return (
        <Select className="borderRadius4" showSearch style={config.style || { width: '200px' }} placeholder={config.placeholder}>
          {config.selectConfig.map((item) => (
            <Select.Option key={item.value} value={item.value}>
              {item.name}
            </Select.Option>
          ))}
        </Select>
      )
    case 'date':
      return <RangePicker
          className="borderRadius4"
          showTime={config.showTime||null}
          style={config.style||null}
          onCalendarChange={(val) => {
            config.dates = val
          }}
          disabledDate={(current) => {
            if (current > moment().endOf('day')) {
              return true
            }
            if (!config?.dates || config?.dates?.length === 0 || !config?.limit) {
              return false
            }
            const tooLate = config?.dates[0] && current.diff(config?.dates[0], 'days') > 90
            const tooEarly = config?.dates[1] && config?.dates[1].diff(current, 'days') > 90
            return tooEarly || tooLate;
          }}
        />
    default:
      return <Input className="borderRadius4" placeholder={config.placeholder} style={config.style||null} />
  }
}

export default SearchForm

使用示例

import SearchForm from '@/components/SearchForm'

const searchFormConfig: Array<SearchFormConfigItem> = [
  {
    name: 'tenantName', // 关键字
    title: '', // label name
    type: 'input',
    placeholder: '请输入租户名',
    style: {
      width: 316
    }
  }
]

<SearchForm configs={searchFormConfig} onSubmit={(obj) => {
    let newPagination = {
      ...pagination,
      total: 0,
      current: 1
    }
    setPagination(newPagination)
    clickSearch(obj, newPagination)
  }} />
Eric-art-coder commented 7 months ago

背景

在react+antd项目中,一些后台管理系统的table需求

useTable 的核心代码

import { useState, useEffect } from 'react'
import { message } from 'antd'
import { v4 as uuidv4 } from 'uuid'

export const useTableConfig = (initParams, setInitParams, callback) {
    const [isLoading, setIsLoading] = useState(false)
    const [pagination, setPagination] = useState({
      position: 'bottomRight',
      pageSize: 15,
      total: 0,
      current: 1
    })
    const [dataSource, setDataSource] = useState<Array<any>>([])
    const onSearch = async (searchParams, obj: any) => {
      if (isLoading) return
      setIsLoading(true)
      try {
        const { data, code, msg } = await callback(searchParams, obj.current, obj.pageSize)
        if (code == 200) {
          data.list.forEach((item) => {
            item.key = item.id || uuidv4()
          })
          setPagination({
            ...obj,
            total: data.total
          })
          setInitParams(searchParams)
          setDataSource(data.list)
        } else {
          message.error(msg)
        }
      } catch (e) {
        message.error(e.message)
      } finally {
        setIsLoading(false)
      }
    }
   useEffect(() => {
      onSearch(initParams, pagination)
    }, [])

   return {
      isLoading,
      pagination,
      dataSource,
      setIsLoading,
      setPagination,
      setDataSource,
      onSearch
    }
}

使用案例

import { useTableConfig } from '@/hooks/useTable'
import { Table, message } from 'antd'

const {
    isLoading,
    pagination,
    dataSource,
    setIsLoading,
    setPagination,
    setDataSource,
    onSearch
} = useTableConfig(tenantParams, setTenantParams, selectTenants)

<Table
  columns={columns}
  dataSource={dataSource}
  loading={isLoading}
  pagination={pagination}
  onChange={pageSizeOnChange}
/>

// 其他的一些