基于Antd远程搜索Select 受控组件,接受所有Antd Select API,参数灵活可配置

24 篇文章 4 订阅
12 篇文章 0 订阅

已封装包组件 react-antd-search-select - npm

基于React Antd远程 SearchSelect 组件,参数灵活可配置,持续迭代中~开源_五虎战画戟-CSDN博客 

// index.jsx 受控组件

import React from 'react'
import AntdSelect from './AntdSelect'

/**
 * Select组件
 * @param value                   Antd Select value
 * @param onChange                Antd Select onChange
 * @param handleList              外部处理并返回list数据
 * @param url                     String 请求接口
 * @param ajaxData                Object 接口入参
 * @param debounceMs              Number 防抖延时时间
 * @param max                     Number 多选时可选最大数量
 * @param remote                  Boolean 是否为远程搜索 默认true
 * @param mode                    Antd Select mode, 设置 Select 的模式为多选或标签  multiple | tags
 * @param echoOptions             回显的list数据
 * @param pageSize                每页搜索数量 默认15
 * @param curPageKey              分页请求页码键值 'curPage'
 * @param queryKey                String 搜索键值 默认 'queryKeyword'
 * @param disabledKey             String 指定要使用的 disabled 字段 默认 'disabled'
 * @param labelKey                String 指定要显示的 label 字段 默认 'label'
 * @param valueKey                String 指定要显示的 value 字段 默认 'value'
 * @param rich                    Boolean Option是否显示更多信息 默认 false
 * @param rightLabelKey           String 指定要显示的 地区 字段 默认 ''
 * @param bottomLabelKey          String 指定要显示的 公司/组 字段 默认 ''
 * @param rest                    Antd Select API
 * @returns {JSX.Element}
 */
export default ({
  value,
  onChange,
  handleList,
  url,
  ajaxData,
  debounceMs,
  remote,
  mode,
  max,
  echoOptions,
  pageSize,
  curPageKey,
  queryKey,
  labelKey,
  disabledKey,
  valueKey,
  rich,
  rightLabelKey,
  bottomLabelKey,
  ...rest
}) => {
  return (
    <AntdSelect
      url={url}
      ajaxData={ajaxData}
      debounceMs={debounceMs}
      remote={remote}
      mode={mode}
      echoOptions={echoOptions}
      propsPageSize={pageSize}
      curPageKey={curPageKey}
      queryKey={queryKey}
      labelKey={labelKey}
      disabledKey={disabledKey}
      valueKey={valueKey}
      rich={rich}
      rightLabelKey={rightLabelKey}
      bottomLabelKey={bottomLabelKey}
      handleList={handleList}
      {...rest}
      value={value}
      onChange={(val) => {
        // 下面是处理 max 逻辑
        if (mode !== 'multiple' && mode !== 'tags') {
          // 单选
          onChange(val)
        } else {
          // 多选
          if (max && val.length > max) {
            onChange(val.slice(0, max))
          } else {
            onChange(val)
          }
        }
      }}
    />
  )
}
// AntdSelect.jsx

import React, { useEffect, useState } from 'react'
import { useDebounce } from 'react-use'
import { Select, message } from 'antd'
import { axios } from '@common'
import './index.less'
import qs from 'qs'
const { Option } = Select

/**
 * Select组件
 * @param value                   Antd Select value
 * @param onChange                Antd Select onChange
 * @param handleList              外部处理并返回list数据
 * @param url                     String 请求接口
 * @param ajaxData                Object 接口入参
 * @param debounceMs              Number 防抖延时时间
 * @param remote                  Boolean 是否为远程搜索 默认true
 * @param mode                    Antd Select mode, 设置 Select 的模式为多选或标签  multiple | tags
 * @param echoOptions             回显的list数据
 * @param propsPageSize           每页搜索数量 默认15
 * @param curPageKey              String 分页请求页码键值 'curPage'
 * @param queryKey                String 搜索键值 默认 'queryKeyword'
 * @param disabledKey             String 指定要使用的 disabled 字段 默认 'disabled'
 * @param labelKey                String 指定要显示的 label 字段 默认 'label'
 * @param valueKey                String 指定要显示的 value 字段 默认 'value'
 * @param rich                    Boolean Option是否显示更多信息 默认 false
 * @param rightLabelKey           String 指定要显示的 地区 字段 默认 ''
 * @param bottomLabelKey          String 指定要显示的 公司/组 字段 默认 ''
 * @param rest                    Antd Select API
 * @returns {JSX.Element}
 */
export default ({
  value,
  onChange,
  handleList,
  url,
  ajaxData,
  debounceMs,
  remote = true,
  mode,
  echoOptions,
  propsPageSize,
  curPageKey,
  queryKey,
  labelKey,
  disabledKey,
  valueKey,
  rich,
  rightLabelKey,
  bottomLabelKey,
  ...rest
}) => {
  const [loading, setLoading] = useState(false) // loding
  const [list, setList] = useState([]) // 下列选项
  const [searchText, setSearchText] = useState('') // 搜索文本,防抖用
  const [pageSize, setPageSize] = useState(propsPageSize || 15) // 每页搜索数量
  const [curPage, setCurPage] = useState(0) // 搜索页码
  const [pageSt, setPageSt] = useState(0) // 滚动的距离
  const [lastPage, setLastPage] = useState(false) // 是否是最后一页
  const [_curPageKey, setCurPageKey] = useState(curPageKey ? curPageKey : 'curPage') // 每页搜索数量
  const [_queryKeyword, setQueryKeyword] = useState(queryKey ? queryKey : 'queryKeyword') // 搜索键值
  const [_disabledKey, setDisabledKey] = useState(disabledKey ? disabledKey : 'disabled') // 指定要使用的 disabled 字段
  const [_labelKey, setLabelKey] = useState(labelKey ? labelKey : 'label') // 指定要显示的 label 字段
  const [_valueKey, setValueKey] = useState(valueKey ? valueKey : 'value') // 指定要显示的 value 字段
  const [_rightLabelKey, setCity] = useState(rightLabelKey ? rightLabelKey : '') // 指定要显示的 右侧 字段
  const [_bottomLabelKey, setCompanyName] = useState(bottomLabelKey ? bottomLabelKey : '') // 指定要显示的 底部 字段

  useEffect(() => {
    if (echoOptions?.length) {
      setList(echoOptions)
    }
  }, [])

  useEffect(() => {
    getList()
  }, [curPage])

  // 防抖
  useDebounce(
    () => {
      getList(searchText)
    },
    debounceMs || 300,
    [searchText]
  )

  // 获取列表
  const getList = (val) => {
    setLoading(true)
    const params = {
      pageSize,
      [_curPageKey]: curPage,
      [_queryKeyword]: val || '',
      ...ajaxData
    }
    axios.post(url, qs.stringify(params))
      .then(({ flag, data, msg }) => {
        setLoading(false)
        if (flag) {
          setLastPage(() => {
            if (data.totalRows) {
              // 如果有分页
              return Math.ceil(data.totalRows / pageSize) <= curPage + 1
            } else {
              // 没有分页则不再滚动加载
              return true
            }
          })
          if (curPage === 0) {
            if (Array.isArray(data)) {
              handleList ? handleList2(data) : setList(data)
            } else {
              handleList ? handleList2(data.dataList) : setList(data.dataList)
            }
          } else {
            if (Array.isArray(data)) {
              handleList ? handleList2([...list, ...data]) : setList((value) => [...value, ...data])
            } else {
              handleList ? handleList2([...list, ...data.dataList]) : setList((value) => [...value, ...data.dataList])
            }
          }
        } else {
          message.error(msg || '请求失败')
        }
      })
      .catch((error) => {
        setLoading(false)
        message.error(error.toString().includes('timeout') ? '服务超时' : error.toString())
      })
  }

  // 外部函数处理数据
  const handleList2 = (val) => {
    const list = handleList(val)
    setList(list)
  }

  // 当清空时
  const onClear = (val) => {
    setSearchText(val)
  }

  //  搜索
  const handleSearch = (val) => {
    if (remote) {
      setSearchText(val)
    }
  }

  //  滚动,分页搜索
  const handlePopupScroll = (e) => {
    // scrollTop // 滚动条距元素(不一定是屏幕最顶端)顶部滚动的距离
    // offsetHeight // 元素CSS高度
    // scrollHeight // 文档内容实际高度,包括超出视窗的溢出部分
    e.persist()
    const { target } = e
    const st = target.scrollTop
    if (st === 0 && pageSt) {
      target.scrollTop = pageSt
    }
    if (st + target.offsetHeight + 3 >= target.scrollHeight && !lastPage) {
      setPageSt(st)
      setCurPage(curPage + 1)
    } else {
      setPageSt(0)
    }
  }

  return (
    <Select
      className="custom-select"
      optionLabelProp="label"
      getPopupContainer={(triggerNode) => triggerNode.parentNode}
      filterOption={false}
      allowClear
      mode={mode}
      showSearch
      onSearch={handleSearch}
      onPopupScroll={handlePopupScroll}
      loading={loading}
      value={value}
      dropdownMatchSelectWidth={false}
      onChange={onChange}
      onClear={onClear}
      {...rest}
    >
      {list?.map((item) => {
        return (
          <Option disabled={item[_disabledKey]} value={item[_valueKey]} key={item[_valueKey]} label={item[_labelKey]}>
            {rich ? (
              <>
                <div className="select-top-title">
                  <span className="top-label-key" title={item[_labelKey]}>{item[_labelKey]}</span>
                  {item?.ext[_rightLabelKey] ? (
                    <span className="right-label-key" title={item?.ext[_rightLabelKey]}>{item?.ext[_rightLabelKey]}</span>
                  ) : null}
                </div>
                {item?.ext[_bottomLabelKey] ? <div className="bottom-label-key" title={item?.ext[_bottomLabelKey]}>{item?.ext[_bottomLabelKey]}</div> : null}
              </>
            ) : (
              <p className="label-key" title={item[_labelKey]}>{item[_labelKey]}</p>
            )}
          </Option>
        )
      })}
    </Select>
  )
}

// index.less

.custom-select {
  .ant-select-selection-item {
    height: auto;
  }
  .ant-select-item-option-selected:not(.ant-select-item-option-disabled) .ant-select-item-option-content {
    padding-right: 0;
  }

  .ant-select-dropdown {
    width: auto !important;

    .label-key {
      max-width: 200px;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .select-top-title {
      display: flex;
      justify-content: space-between;

      .top-label-key {
        float: left;
        max-width: 200px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }
      .right-label-key {
        float: right;
        color: #8593a5;
        font-size: 12px;
        margin-left: 10px;
        max-width: 100px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }
    }
    .bottom-label-key {
      color: #8593a5;
      font-size: 12px;
      max-width: 240px;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
  }
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__畫戟__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值