对select进行组件封装

import { useDebounceFn, useRequest } from 'ahooks';
import { Select } from 'antd';
import { useEffect, useState } from 'react';

type selectValues = string | string[] | undefined;

interface OptionStructure {
  optionKey: string;
  optionValue: string;
  optionImgKey?: string;
}

interface IProps {
  value?: selectValues;
  onChange?: (value: selectValues) => void;
  multiple: boolean;
  showSearch: boolean;
  pageSize: number;
  resStructure: Record<'dataKey' | 'tokenKey', string>;
  optionStructure: OptionStructure;
  labelInValue: boolean;
  placeholder: string;
  fetch: any;
  fetchFilter: (val: string) => string;
  fetchBase: Record<string, any>;
  reloadingControl?: {
    reloading: boolean;
    setReloading: (loading: boolean) => void;
  };
}

const { Option } = Select;

function SelectTest(props: IProps) {
  const {
    placeholder,
    multiple,
    showSearch,
    fetch,
    pageSize,
    resStructure,
    optionStructure,
    labelInValue,
    fetchFilter,
    fetchBase,
    onChange,
    reloadingControl,
    value,
  } = props;

  const { reloading, setReloading } = reloadingControl!;
  const { dataKey, tokenKey } = resStructure;
  const { optionKey, optionValue, optionImgKey } = optionStructure;
  const [options, setOptions] = useState<Record<string, any>[]>([]);
  const [selectValue, setSelectValue] = useState(value);
  const [pageToken, setPageToken] = useState('');
  const [searchVal, setSearchVal] = useState('');
  const [prevSearchVal, setPrevSearchVal] = useState('');
  const [noMore, setNoMore] = useState(false);
  
  const { run } = useRequest(fetch, {
    manual: true,
    onSuccess: (result: any) => {
      const fetchData = result[dataKey];
      const nextToken = result[tokenKey];
      setOptions((originOptin) => {
        return [...originOptin, ...fetchData];
      });
      if (!nextToken) {
        setNoMore(true);
      } else {
        setPageToken(nextToken);
      }
    },
    onError: () => {
      //...
    },
  });

  useEffect(() => {
    setSelectValue(value);
  }, [value]);

  const handleScroll = (e: any) => {};
  const handleClear = () => {};
  const fetchOption = (val: string) => {
    const filter = !!val
      ? {
          filter: fetchFilter(val),
        }
      : {};
    if (val !== prevSearchVal) {
      //...
    } else {
      //...
    }
  };
  
  useEffect(() => {
    run({ pageSize, pageToken, ...fetchBase });
  }, []);

  useEffect(() => {
    if (reloading) {
      handleClear();
      setReloading(false);
    }
  }, [reloading]);

  const { run: handleSearch } = useDebounceFn(
    (val) => {
      if (!showSearch) return;
      setSearchVal(val);
      fetchOption(val);
    },
    {
      wait: 800,
    },
  );

  const Options = options.map((option) => {
    return (
      <Option value={option[optionValue]} key={option[optionValue]}>
        {optionImgKey ? (
          <img
            src={option[optionImgKey]}
            alt=''
            style={{
              width: '15px',
              height: '15px',
              borderRadius: '50%',
              marginRight: '5px',
            }}
          />
        ) : null}
        {option[optionKey]}
      </Option>
    );
  });

  return (
    <div>
      <Select
        labelInValue={labelInValue}
        mode={multiple ? 'multiple' : undefined}
        allowClear
        showSearch={showSearch}
        placeholder={placeholder}
        filterOption={false}
        onSearch={handleSearch}
        onPopupScroll={handleScroll}
        onChange={onChange}
        onClear={handleClear}
        value={selectValue}>
        {Options}
      </Select>
    </div>
  );
}

SelectTest.defaultProps = {
  multiple: false,
  showSearch: false,
  pageSize: 10,
  fetchBase: {},
  fetchFilter: (val: any) => val,
  reloadingControl: {
    reloading: false,
    setReloading: () => false,
  },
  labelInValue: false,
};

export default SelectTest;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue3 中组件封装相比 Vue2 有了很大的改进,可以使用 Composition API 来进行组件的编写,下面是一个使用 Vue3 Composition API 封装的带有搜索功能的 el-select 组件的示例: ```vue <template> <el-select v-model="selectedValue" filterable remote :remote-method="search" :loading="isLoading" :loading-text="loadingText" :no-match-text="noMatchText" :popper-append-to-body="false" :popover-class="popoverClass" :debounce="debounce" :placeholder="placeholder" :clearable="clearable" :disabled="disabled" > <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </template> <script> import { reactive, toRefs } from 'vue' export default { name: 'ElSelectSearch', props: { // 下拉选项列表,格式为 [{ label: '选项1', value: 'value1' }, ...] options: { type: Array, default: () => [] }, // 是否可清空 clearable: { type: Boolean, default: false }, // 是否禁用 disabled: { type: Boolean, default: false }, // 是否显示搜索框 filterable: { type: Boolean, default: true }, // 下拉框的 class popoverClass: { type: String, default: '' }, // 搜索框 placeholder placeholder: { type: String, default: '请输入搜索内容' }, // 输入搜索内容后,触发搜索的延迟时间 debounce: { type: Number, default: 300 }, // 搜索中的 loading 文本 loadingText: { type: String, default: '加载中...' }, // 没有匹配项的文本 noMatchText: { type: String, default: '没有匹配的数据' }, // 是否正在加载中 isLoading: { type: Boolean, default: false } }, setup(props, { emit }) { const state = reactive({ selectedValue: '', searchValue: '', searchTimer: null }) // 监听选中的值变化 const handleSelectChange = (value) => { state.selectedValue = value emit('change', value) } // 监听搜索框输入的值变化 const handleSearchValueChange = (value) => { state.searchValue = value if (state.searchTimer) { clearTimeout(state.searchTimer) } state.searchTimer = setTimeout(() => { emit('search', value) }, props.debounce) } // 执行搜索的方法 const search = (query) => { emit('search', query) } // 将响应式数据转化为 ref const refs = toRefs(state) return { ...refs, handleSelectChange, handleSearchValueChange, search } } } </script> ``` 这里我们使用了 `reactive` 和 `toRefs` 来创建响应式数据,并将其转化为 ref,方便在模板中使用。同时,我们也使用了 Vue3 的两个新的钩子函数 `setup` 和 `emit`。其中,`setup` 钩子函数用来替代 Vue2 中的 `beforeCreate` 和 `created` 钩子函数,用于组件的初始化和响应式数据的创建。`emit` 函数用于向父组件派发事件。在 `setup` 函数中,我们将响应式数据和方法都返回给模板使用。 该组件支持以下 props: - `options`:下拉选项列表,格式为 `[{ label: '选项1', value: 'value1' }, ...]`。 - `clearable`:是否可清空。 - `disabled`:是否禁用。 - `filterable`:是否显示搜索框。 - `popoverClass`:下拉框的 class。 - `placeholder`:搜索框 placeholder。 - `debounce`:输入搜索内容后,触发搜索的延迟时间。 - `loadingText`:搜索中的 loading 文本。 - `noMatchText`:没有匹配项的文本。 - `isLoading`:是否正在加载中。 该组件支持以下事件: - `change`:选中的值变化时触发。 - `search`:搜索框输入内容变化或者点击下拉框时触发。 该组件中的搜索功能使用了 ElementUI 的远程搜索,可以在 `search` 方法中进行异步搜索。需要注意的是,在使用远程搜索时,需要设置 `remote` 和 `:remote-method` 属性。`remote` 表示是否使用远程搜索,`:remote-method` 表示远程搜索的方法。 同时,我们也使用了 ElementUI 的 `el-option` 组件来显示下拉选项列表,使用 `v-for` 来遍历 `options` 数组中的每一个元素,并将其显示在下拉选项列表中。`el-select` 组件的 `v-model` 双向绑定 `selectedValue` 变量,当选中的值发生变化时,会触发 `handleSelectChange` 方法。 最后,我们将响应式数据和方法都返回给模板使用,模板中可以直接使用它们来进行渲染和交互。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值