Antd搜索组件Filter封装

2020-07-02  本文已影响0人  郁南

早期有封装过js版本Antd搜索栏组件SearchForm的封装/使用

介绍

组件封装的初衷,当然都是为了代码尽可能的复用,从Jquery时代的commonjs就有的模块化思想,在react上应该体现的是最淋漓尽致的。
日常后台页面开发,基本上都会有增删改查,所以把常用功能整合起来并封装成公用组件就显得十分有必要。

封装

由于新项目都使用TS,所以就用在js版本基础上优化并使用ts。下面是实现代码

import React from "react";
import {
  Form,
  Row,
  Col,
  Input,
  Button,
  InputNumber,
  AutoComplete,
  Select, 
  DatePicker,
  TreeSelect,
  Switch,
  Cascader,
  TimePicker,
} from "antd"; // antd@4.x
const { RangePicker } = DatePicker; 

import { setKeystoLocaleLowerCase } from "@/utils"; // 避免引起命名冲突,把oject的key都转化为小写

const { Option } = Select;
const { TreeNode } = TreeSelect;

interface FilterProps {
  filterList: any[];
  autoSearch?: boolean;
  onSearch?: Function;
  onReset?: Function;
  [key: string]: any;
}

export const Filter = ({
  filterList,
  autoSearch = false,
  onSearch,
  onReset,
  ...props
}: FilterProps) => {
  const [form] = Form.useForm();
  filterList = setKeystoLocaleLowerCase(filterList);

  const onFinish = (values: { [key: string]: string | number | undefined }) => {
    onSearch && onSearch(values);
  };

  const lisLen: number = filterList.length;
  const span: number = lisLen >= 5 ? 4 : (24 - 2.5) / lisLen;

  const getFormElement = (tagname: string, oItem: any) => {
    /** onPressEnter会自动执行 */
    let item: any = {};
    for (let key in oItem) {
      if (oItem.hasOwnProperty(key)) {
        if (key !== "defaultvalue") {
          item[key] = oItem[key];
        }
      }
    }
    item = {
      ...item,
      onChange: () => autoSearch && onFinish(form.getFieldsValue()),
      onSelect: () => autoSearch && onFinish(form.getFieldsValue()),
    };
    switch (tagname) {
      case "input":
        return <Input allowClear {...item} />;
      case "inputnumber":
        return <InputNumber min={0} {...item} />;
      case "autocomplete": 
        return (
          <AutoComplete
            allowClear
            options={item.options}
            filterOption={(inputValue: any, option: any) => {
              return (
                option.value.toUpperCase().indexOf(inputValue.toUpperCase()) !==
                -1
              );
            }}
            {...item}
          />
        );
      case "switch": 
        return <Switch checkedChildren="开" unCheckedChildren="关" {...item} />;
      case "select":
        const selectlist = item.list || item.selectlist || []; 
        return (
          <Select allowClear {...item}>
            {item.render && item.render()}
            {!item.render &&
              selectlist.map((cl: any) => {
                const oName = cl.key || cl.text || cl.name;
                return (
                  <Option key={cl.id} value={cl.id}>
                    {oName}
                  </Option>
                );
              })}
          </Select>
        );
      case "treeselect":
        const treeSelectList = item.list || item.selectlist || []; 
        return (
          <TreeSelect allowClear multiple {...item}>
            {item.render && item.render()}
            {!item.render &&
              treeSelectList.map((cl: any) => {
                const nodeName = cl.key || cl.text; 
                return (
                  <TreeNode key={cl.id} value={cl.id} title={nodeName}>
                    {(cl.children || []).map((chil: any) => {
                      const subnodeName = chil.key || chil.text; 
                      return (
                        <TreeNode
                          key={chil.id}
                          value={chil.id}
                          title={subnodeName}
                        />
                      );
                    })}
                  </TreeNode>
                );
              })}
          </TreeSelect>
        );
      case "datepicker":
        return <DatePicker {...item} />;
      case "rangepicker":
        return <RangePicker {...item} />;
      case "timepicker":
        return <TimePicker {...item} />;
      case "rangetimepicker":
        return <TimePicker.RangePicker {...item} />;
      case "cascader":
        return <Cascader {...item} />;
      default:
        return "";
    }
  };

  return (
    <Form
      form={form}
      name="advanced_search"
      className="ant-advanced-search-form"
      {...props}
      onFinish={onFinish}
    >
      <Row gutter={24}>
        {filterList.map((item: any, i: number) => {
          const tagname = (item.tagname || "").toLowerCase();

          if (tagname !== "input" && item.rules) {
            // delete item.rules;
            item.rules = null;
          }

          const itemName = item.key; 
          return (
            <Col
              span={tagname === "switch" ? 2.5 : span}
              key={item.key + i}
              style={{
                minWidth: tagname === "switch" ? "auto" : "200px", 
              }}
            >
              <Form.Item
                name={itemName}
                label={item.label}
                rules={item.rules ? [item.rules] : []}
              >
                {getFormElement(tagname, item)}
              </Form.Item>
            </Col>
          );
        })}

        <Col
          span={2.5}
          style={{ minWidth: "100px", textAlign: "right", flex: "auto" }}
        >
          <Button type="primary" htmlType="submit">
            查询
          </Button>
          <Button
            style={{ margin: "0 8px" }}
            onClick={() => {
              form.resetFields();
              onReset && onReset();
            }}
          >
            重置
          </Button>
        </Col>
      </Row>
    </Form>
  );
};

这样组件就满足了大部分的使用场景,有需要的,就再迭代新增就好了,需要做的兼容也不是很多

使用

在日常开发,应该把模块化思想最大化的落实到项目上,所以页面实现不管是组件还是业务,我都喜欢按功能拆分

  1. 初始数据
// ./initData.ts
export const filterList = [
  {
    tagName: "input",
    key: "name",
    label: "模糊搜索",
    placeholder: "请输入姓名/ID/手机后4位/站点地址",
  },
  {
    tagName: "datepicker",
    key: "datepickerItem",
    label: "创建日期",
  },
  {
    tagName: "select",
    key: "site",
    label: "站点",
    defaultValue: null,
    placeholder: "请选择站点",
    selectList: [
      {
        id: 0,
        name: "站点1",
      },
      {
        id: 1,
        name: "站点2",
      },
    ],
  },
  {
    tagName: "treeselect",
    key: "state",
    label: "状态",
    defaultValue: null,
    placeholder: "请选择状态",
    selectList: [
      {
        id: 0,
        name: "状态1",
        children: [
          {
            id: 10,
            name: "状态11",
          },
          {
            id: 11,
            name: "状态12",
          },
        ],
      },
      {
        id: 1,
        name: "状态2",
        children: [
          {
            id: 20,
            name: "状态21",
          },
          {
            id: 21,
            name: "状态22",
          },
        ],
      },
    ],
  },
];
  1. 页面
import React, { FC } from "react";

import { Filter } from "@/components";

import { filterList} from "./initData";

export const FilterDemo: FC<any> = () => {
  const onSearch = (values: any) => {
    console.log(values);
  };

  return (
    <div> 
      <Filter
        filterList={filterList}
        onSearch={(values: any) => onSearch(values)}
      />

      {/* 其他业务组件 */}
    </div>
  );
}

这样这个Filter组件基本上就可以随处可用了
如果对组件封装以及性能优化等有更好的建议,欢迎在评论区留言

上一篇下一篇

猜你喜欢

热点阅读