我正在使用 MUI 自动完成和 Formik,我想将其分组。如果它没有
sub_accounts
,那么它就不应该有标题标签。像这样的东西:https://mui.com/material-ui/react-autocomplete/#grouped
代码沙箱 ------> 点击这里
UI 上的预期结果类似于:
零用现金
银行现金 - 美国银行
现金
CIB - 越南银行
代码
export const CashAccountAutocomplete = ({
field,
form: { touched, errors, setFieldValue, values },
disabled,
...props
}) => {
const [inputValue, setInputValue] = useState("");
const handleChange = (_, newValue, reason) => {
if (reason === "clear") {
setFieldValue(field.name, { id: "", name: "" });
return;
}
setFieldValue(field.name, newValue);
};
const handleInputChange = (_, newInputValue) => {
setInputValue(newInputValue);
};
const extractSubAccounts = (accounts) => {
if (!Array.isArray(accounts)) {
console.error("Invalid accounts data. Expected an array.");
return [];
}
return accounts.flatMap(
({ id, name, sub_accounts }) =>
sub_accounts && sub_accounts.length > 0
? extractSubAccounts(sub_accounts) // Recursively extract sub-accounts
: [{ id, name }] // Include the account if it has no sub-accounts
);
};
const filteredData = extractSubAccounts(accounts);
return (
<Autocomplete
{...field}
disabled={disabled}
getOptionLabel={(option) =>
typeof option === "string" ? option : option?.name || ""
}
renderOption={(props, option) => {
return (
<li {...props} key={option.id}>
{option?.name}
</li>
);
}}
filterOptions={(x) => x}
options={filteredData || []}
autoComplete
includeInputInList
filterSelectedOptions
noOptionsText={"No data"}
onChange={handleChange}
inputValue={inputValue}
onInputChange={handleInputChange}
renderInput={(params) => (
<TextField
{...params}
{...props}
error={touched[field.name] && errors[field.name] ? true : false}
helperText={
touched[field.name] &&
errors[field.name] &&
String(errors[field.name].id)
}
/>
)}
fullWidth
/>
);
};
您可以修改
extractSubAccounts
函数以包含分组逻辑。
import React, { useState } from "react";
import Autocomplete from "@mui/material/Autocomplete";
import TextField from "@mui/material/TextField";
export const CashAccountAutocomplete = ({
field,
form: { touched, errors, setFieldValue },
disabled,
accounts, // assuming accounts is passed as a prop
...props
}) => {
const [inputValue, setInputValue] = useState("");
const handleChange = (_, newValue, reason) => {
if (reason === "clear") {
setFieldValue(field.name, { id: "", name: "" });
return;
}
setFieldValue(field.name, newValue);
};
const handleInputChange = (_, newInputValue) => {
setInputValue(newInputValue);
};
const extractSubAccounts = (accounts) => {
if (!Array.isArray(accounts)) {
console.error("Invalid accounts data. Expected an array.");
return [];
}
const groupedAccounts = [];
accounts.forEach(({ id, name, sub_accounts }) => {
if (sub_accounts && sub_accounts.length > 0) {
// Include the header for this group
groupedAccounts.push({ id, name, isHeader: true });
// Include sub-accounts
sub_accounts.forEach(({ id: subId, name: subName }) => {
groupedAccounts.push({ id: subId, name: subName, isHeader: false });
});
} else {
// Include the account if it has no sub-accounts
groupedAccounts.push({ id, name, isHeader: false });
}
});
return groupedAccounts;
};
const filteredData = extractSubAccounts(accounts);
return (
<Autocomplete
{...field}
disabled={disabled}
getOptionLabel={(option) =>
typeof option === "string" ? option : option?.name || ""
}
renderOption={(props, option) => {
return option.isHeader ? (
<li>
<strong>{option.name}</strong>
</li>
) : (
<li {...props} key={option.id}>
{option.name}
</li>
);
}}
filterOptions={(x) => x}
options={filteredData || []}
autoComplete
includeInputInList
filterSelectedOptions
noOptionsText={"No data"}
onChange={handleChange}
inputValue={inputValue}
onInputChange={handleInputChange}
renderInput={(params) => (
<TextField
{...params}
{...props}
error={touched[field.name] && errors[field.name] ? true : false}
helperText={
touched[field.name] &&
errors[field.name] &&
String(errors[field.name].id)
}
/>
)}
fullWidth
/>
);
};