antd+react 实现多组复选框

背景:此多选框是放在Antd的一个Form表单里,用了Form表单的自定义控件,更方便地获取值。

注意项:组件刚加载时,若焦点聚焦在其他input框,此时点击多选框,就不能再连续点击此多选框了,可以给多选框设置 onClick={(e) => e.stopPropagation()}

效果图

代码

import React, { useEffect } from "react";
import { Checkbox, Collapse, Input } from "@anyshare/controls";
import { useLocalStore, useObserver } from "mobx-react-lite";
import { SuffixItem } from "@anyshare/common/lib/types";
import { useIntl } from "react-intl";

export const suffixName: {
    [key: string]: { [key: string]: string };
} = {
    "1": { name: "文档类", displayName: "dir.attribute.type.document" },
    "2": { name: "视频/音频", displayName: "dir.attribute.type.video" },
    "3": { name: "图片", displayName: "dir.attribute.type.picture" },
    "4": { name: "压缩包", displayName: "dir.attribute.type.compressed" },
    "5": { name: "可疑文件", displayName: "dir.attribute.type.suspicious" },
    "6": { name: "病毒文件", displayName: "dir.attribute.type.virus" },
    "7": { name: "其它", displayName: "dir.attribute.type.other" },
};

const suffixClassificationInfo = [
    {
        id: 1,
        name: "文档类",
        suffix: [
            ".docx",
            ".dotx",
            ".dot",
            ".doc",
            ".odt",
            ".wps",
            ".docm",
            ".dotm",
            ".xlsx",
            ".et",
            ".xlsm",
            ".xlsb",
            ".xls",
            ".xltx",
            ".xltm",
            ".xlt",
            ".xla",
            ".pptx",
            ".ppt",
            ".pot",
            ".pps",
            ".ppsx",
            ".dps",
            ".ppam",
            ".pptm",
            ".potx",
            ".potm",
            ".ppsm",
            ".ppa",
            ".pdf",
            ".txt",
            ".htm",
            ".rtf",
            ".dic",
            ".log",
            ".xml",
            ".html",
        ],
    },
    {
        id: 2,
        name: "视频/音频",
        suffix: [
            ".flv",
            ".wmv",
            ".mkv",
            ".mov",
            ".mp4",
            ".asf",
            ".mpg",
            ".rm",
            ".3gp",
            ".rmvb",
            ".mpeg",
            ".mts",
            ".m2ts",
            ".avi",
            ".wif",
            ".vob",
            ".mp3",
            ".wav",
            ".ogg",
            ".m4a",
            ".flac",
            ".ape",
            ".aac",
            ".wma",
            ".wv",
            ".mp2",
            ".ac3",
            ".mpc",
            ".mka",
            ".mpa",
            ".dts",
            ".mid",
        ],
    },
    {
        id: 3,
        name: "图片",
        suffix: [".jpeg", ".tif", ".bmp", ".gif", ".png", ".jpg", ".wmf", ".emf", ".raw", ".ioc", ".tga", ".svg"],
    },
    {
        id: 4,
        name: "压缩包",
        suffix: [
            ".zip",
            ".rar",
            ".tgz",
            ".tar",
            ".cab",
            ".uue",
            ".jar",
            ".ace",
            ".lzh",
            ".arj",
            ".gzip",
            ".gz",
            ".gz2",
            ".bz",
            ".bz2",
            ".7z",
            ".iso",
            ".rpm",
        ],
    },
    {
        id: 5,
        name: "可疑文件",
        suffix: [
            ".exe",
            ".dll",
            ".bat",
            ".com",
            ".cmd",
            ".inf",
            ".ocx",
            ".sys",
            ".vn",
            ".vbs",
            ".vbe",
            ".vbx",
            ".vxd",
            ".js",
            ".jse",
            ".wsh",
            ".wsf",
            ".asd",
            ".cpl",
            ".crt",
            ".dbx",
            ".hlp",
            ".hta",
            ".isp",
            ".nws",
            ".ops",
            ".pcd",
            ".pi",
            ".prf",
            ".reg",
            ".scf",
            ".scr",
            ".sct",
            ".uue",
            ".wab",
            ".wsc",
            ".rtf",
            ".pas",
            ".mp8",
            ".tbb",
            ".sht",
            ".mbx",
            ".eml",
            ".pmr",
            ".fpt",
            ".inb",
            ".adb",
            ".cfg",
            ".cgi",
            ".dhtm",
            ".mmf",
            ".mht",
            ".nch",
            ".ods",
            ".oft",
            ".pl",
            ".shtm",
            ".stm",
            ".uin",
        ],
    },
    {
        id: 6,
        name: "病毒文件",
        suffix: [
            ".onion",
            ".wallet",
            ".wmcry",
            ".WNCRY",
            ".b01e",
            ".locky",
            ".Cerber3",
            ".CHAK",
            ".GOTHAM",
            ".YAYA",
            ".GRANNY",
            ".SKUNK",
            ".TRUE",
            ".SEXY",
            ".MAKGR",
            ".BIG1",
            ".LIN",
            ".BIIT",
            ".THIRD",
            ".DREAM",
            ".freeman",
            ".BUNNY",
        ],
    },
    {
        id: 7,
        name: "其它",
        suffix: [],
    },
];

export const SuffixType = (props: any) => {
    const intl = useIntl();
    const { Panel } = Collapse;
    const { TextArea } = Input;
    const store = useLocalStore(() => ({
        checkedLists: {} as { [key: string]: string[] },
        otherValue: props.value?.find((obj: any) => obj.id === 7)?.suffix.join(" "),
    }));
    useEffect(() => {
        props.value.forEach((item: SuffixItem) => {
            store.checkedLists = { ...store.checkedLists, [item.id]: item.suffix };
        });
    }, []);

    const updateValue = () => {
        const updateValue: SuffixItem[] = [];
        Object.entries(store.checkedLists).forEach(([key, value]) => {
            updateValue.push({ id: parseInt(key), name: suffixName[key].name, suffix: value as string[] });
        });
        props.onChange(updateValue);
    };

    const onCheckboxChange = (id: number, value: string, checked: boolean) => {
        const newCheckedList = checked
            ? [...store.checkedLists[id], value]
            : store.checkedLists[id].filter((item: string) => item !== value);
        store.checkedLists = { ...store.checkedLists, [id]: newCheckedList };
        updateValue();
    };

    const onCheckAllChange = (id: number, checked: boolean) => {
        const newCheckedList = checked
            ? suffixClassificationInfo.find((item: SuffixItem) => item.id === id)?.suffix
            : [];
        store.checkedLists = { ...store.checkedLists, [id]: newCheckedList };
        updateValue();
    };

    const getOtherSuffix = (value: string) => {
        store.otherValue = value;
        store.checkedLists = { ...store.checkedLists, [7]: value.split(" ") };
        updateValue();
    };

    return useObserver(() => (
        <div className="as-component-suffixType">
            <Collapse defaultActiveKey={[1, 7]} expandIconPosition="right">
                {suffixClassificationInfo.map((item: SuffixItem) => (
                    <Panel
                        header={
                            item.id !== 7 ? (
                                <Checkbox
                                    onChange={(e) => onCheckAllChange(item.id, e.target.checked)}
                                    checked={store.checkedLists[item.id]?.length === item.suffix.length}
                                    onClick={(e) => e.stopPropagation()}
                                    className="as-components-suffix-title"
                                    indeterminate={
                                        store.checkedLists[item.id]?.length &&
                                        store.checkedLists[item.id]?.length !== item.suffix.length
                                            ? true
                                            : false
                                    }
                                >
                                    {intl.formatMessage({ id: suffixName[item.id].displayName })} &nbsp; (
                                    {store.checkedLists[item.id]?.length ?? 0}/{item.suffix.length})
                                </Checkbox>
                            ) : (
                                <div className="as-components-suffix-title" style={{ marginLeft: "22px" }}>
                                    {intl.formatMessage({ id: "dir.attribute.type.other" })}
                                </div>
                            )
                        }
                        key={item.id}
                    >
                        <div className={item.id !== 7 ? "as-components-suffix-checkbox" : "as-components-suffix-text"}>
                            {item.id !== 7 ? (
                                item.suffix.map((suffix: string) => (
                                    <Checkbox
                                        key={suffix}
                                        onChange={(e) => onCheckboxChange(item.id, suffix, e.target.checked)}
                                        checked={store.checkedLists[item.id]?.includes(suffix)}
                                        onClick={(e) => e.stopPropagation()}
                                    >
                                        {suffix}
                                    </Checkbox>
                                ))
                            ) : (
                                <TextArea
                                    className="as-components-suffix-content-other"
                                    value={store.otherValue}
                                    placeholder={intl.formatMessage({ id: "dir.attribute.type.other.tip" })}
                                    onChange={(e) => getOtherSuffix(e.target.value)}
                                ></TextArea>
                            )}
                        </div>
                    </Panel>
                ))}
            </Collapse>
        </div>
    ));
};

export default SuffixType;
//form表单 删除了部分代码
import React, { useEffect } from "react";
import { Button, Form, Input, Select, message as toast } from "antd";
import { useObserver, useLocalStore } from "mobx-react-lite";
import { useIntl } from "react-intl";
import { SuffixType } from "./SuffixType";
import { toPairs } from "lodash";

export const FolderAttributeEditor: React.FC<IFolderAttributeEditorProps> = ({
    docid,
    name,
    spaceQuota,
    usedSpace,
    folderCsfLevel,
    allowSuffixDoc,
}) => {
    const intl = useIntl();
    const [form] = Form.useForm();

    const store = useLocalStore(() => ({
        libNoAllowSuffixDoc: [] as SuffixItem[],
        parentAllowSuffixDoc: [] as SuffixItem[],
        csfOptions: [] as { level: any; text: string }[],
        suffixClassificationInfo: [] as SuffixItem[],
        suffixErrorTip: "",
        spaceQuotaValue: Number(spaceQuota / Math.pow(1024, 3)).toFixed(2),
        isModified: false,
    }));

    const updateFolderAttribute = async () => {
        try {
            form.validateFields().then(async (data) => {
                const response = await openApi.request({
                    url: "dirs/{object_id}/attributes/{fields}",
                    method: "PUT",
                    params: {
                        object_id: getLastDocId(docid) ?? "",
                        fields: "space_quota,csflevel,allow_suffix_doc",
                    },
                    data: {
                        space_quota: convertGBtoB(data.spacequota),
                        allow_suffix_doc: data.suffixType,
                    },
                });
                if (response.status === 202) {
                    ...
                }
            });
        } catch (error) {
            messageErr(error);
        }
    };

    const isAllowSuffixDoc = (value: SuffixItem[], checkParent?: boolean): boolean => {
        const illegalSuffix: string[] = [];

        const isLibAllow = (suffixString: string) => {
            let isIllegal = false;
            store.libNoAllowSuffixDoc.forEach((item: SuffixItem) => {
                if (item.suffix.includes(suffixString)) isIllegal = true;
            });
            isIllegal ? illegalSuffix.push(suffixString) : null;
        };

        const isParentAllow = (suffixString: string) => {
            let isIllegal = true;
            store.parentAllowSuffixDoc.forEach((item: SuffixItem) => {
                if (item.suffix.includes(suffixString)) isIllegal = false;
            });
            isIllegal ? illegalSuffix.push(suffixString) : null;
        };

        value.forEach((updateItem: SuffixItem) => {
            updateItem.suffix.forEach((suffixString: string) => {
                checkParent ? isParentAllow(suffixString) : isLibAllow(suffixString);
            });
        });

        store.suffixErrorTip = illegalSuffix.length !== 0 ? illegalSuffix.join("、") : "";
        return !store.suffixErrorTip;
    };

    const isEmptySuffix = (value: SuffixItem[]): boolean => {
        let checkedNumber = 0;
        value.forEach((updateItem: SuffixItem) => {
            if (updateItem.id === 7) {
                updateItem.suffix[0] !== "" && checkedNumber++;
            } else {
                checkedNumber += updateItem.suffix.length;
            }
        });
        return !checkedNumber;
    };

    const checkSuffixType = (_: any, value: SuffixItem[]) => {
        if (!isAllowSuffixDoc(value)) {
            return Promise.reject(
                intl.formatMessage({ id: "dir.attribute.type.libnoallowed" }, { noallowed: store.suffixErrorTip })
            );
        } else if (!isLib(parentDocId) && !isAllowSuffixDoc(value, true)) {
            return Promise.reject(
                intl.formatMessage({ id: "dir.attribute.type.noallowed" }, { noallowed: store.suffixErrorTip })
            );
        } else if (isEmptySuffix(value)) {
            return Promise.reject(intl.formatMessage({ id: "dir.attribute.type.require" }));
        } else {
            return Promise.resolve();
        }
    };

    const onBlur = (e: React.FocusEvent<HTMLInputElement>) => {
        if (Number.isNaN(Number(e.target.value))) {
            store.spaceQuotaValue = "";
        } else {
            store.spaceQuotaValue = Number(e.target.value).toFixed(2);
        }
        form.setFieldsValue({ spacequota: store.spaceQuotaValue });
    };

    return useObserver(() => (
        <>
            <Form
                form={form}
                initialValues={{
                    spacequota: store.spaceQuotaValue,
                    suffixType: allowSuffixDoc,
                }}
                onValuesChange={() => (store.isModified = true)}
            >
                <div className="editDirAttr-content">
                    <Form.Item
                        name="spacequota"
                        label={intl.formatMessage({ id: "dir.attribute.storage.size" })}
                        extra={intl.formatMessage({ id: "dir.attribute.quota.used" }, { used: formatSize(usedSpace) })}
                        required
                    >
                        <Input
                            className="editor-input"
                            onBlur={onBlur}
                            suffix={<span style={{ color: "grey" }}>GB</span>}
                        />
                    </Form.Item>
                    <Form.Item
                        name="suffixType"
                        label={intl.formatMessage({ id: "dir.attribute.allowed.type" })}
                        required
                        className="editDirAttr-suffix"
                        rules={[{ validator: checkSuffixType }]}
                    >
                        <SuffixType></SuffixType>
                    </Form.Item>
                </div>
                <div className="editDirAttr-btn">
                    <Button type="primary" onClick={updateFolderAttribute} disabled={!store.isModified}>
                        {intl.formatMessage({ id: "search.button.ok" })}
                    </Button>
                    <Button style={{ marginLeft: 8 }} onClick={() => container.close()} type="normal">
                        {intl.formatMessage({ id: "search.button.cancel" })}
                    </Button>
                </div>
            </Form>
        </>
    ));
};

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当使用 Ant Design 和 TypeScript 来创建带有 key 值的多框组,并实现功能时,你可以按照以下步骤进行操作: 1. 首先,确保你已安装并正确导入了 `antd` 组件库以及相关依赖。 2. 在你的组件中,创建一个状态变量来保存中的框值以及全状态。同时,定义一个项列表数组,每个项都包含一个唯一的 key 值。 ```tsx import React, { useState } from 'react'; import { Checkbox } from 'antd'; const options = [ { key: 'option1', label: 'Option 1' }, { key: 'option2', label: 'Option 2' }, { key: 'option3', label: 'Option 3' }, ]; const CheckboxGroupExample: React.FC = () => { const [checkedList, setCheckedList] = useState<string[]>([]); const [indeterminate, setIndeterminate] = useState<boolean>(false); const [checkAll, setCheckAll] = useState<boolean>(false); // 处理单个框的中 const handleCheckboxChange = (checkedValues: string[]) => { setCheckedList(checkedValues); setIndeterminate(!!checkedValues.length && checkedValues.length < options.length); setCheckAll(checkedValues.length === options.length); }; // 处理全框的中 const handleCheckAllChange = (e: React.ChangeEvent<HTMLInputElement>) => { const checked = e.target.checked; setCheckedList(checked ? options.map(option => option.key) : []); setIndeterminate(false); setCheckAll(checked); }; return ( <div> <div style={{ borderBottom: '1px solid #E9E9E9' }}> <Checkbox indeterminate={indeterminate} onChange={handleCheckAllChange} checked={checkAll} > 全 </Checkbox> </div> <br /> <Checkbox.Group options={options} value={checkedList} onChange={handleCheckboxChange} /> </div> ); }; export default CheckboxGroupExample; ``` 在上面的示例中,我们定义了一个项列表数组 `options`,每个项都包含一个 `key` 值和一个显示的 `label`。然后,我们使用 `useState` 钩子来创建了三个状态变量:`checkedList` 用于保存中的框值,`indeterminate` 用于控制全框的中间状态,`checkAll` 用于控制全框的中状态。 我们还定义了两个处理函数:`handleCheckboxChange` 处理单个框的中,`handleCheckAllChange` 处理全框的中。在 `handleCheckboxChange` 中,我们更新了中的框值,并根据中的数量来更新全框的状态。在 `handleCheckAllChange` 中,我们根据全框的中状态来更新所有框的值,并更新全框的状态。 3. 最后,在你的页面或应用中使用这个组件即可。 ```tsx import React from 'react'; import CheckboxGroupExample from './CheckboxGroupExample'; const App: React.FC = () => { return ( <div> <CheckboxGroupExample /> </div> ); }; export default App; ``` 在上面的示例中,我们在主应用组件中使用 `CheckboxGroupExample` 组件。 这样就创建了一个带有 key 值的多框组,并实现了全功能。你可以根据实际需求调整项列表和样式。 希望这能帮到你!如果还有其他问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值