背景:此多选框是放在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 })} (
{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>
</>
));
};