feat: 支持主分组隐藏节点并保留排序
This commit is contained in:
@@ -42,6 +42,7 @@ type ProxyGroupEditorProps = {
|
||||
id: string;
|
||||
name: string;
|
||||
memberSlugs: string[];
|
||||
hiddenSlugs?: string[];
|
||||
};
|
||||
nodes: ProxyGroupNodeOption[];
|
||||
};
|
||||
@@ -90,11 +91,15 @@ function SortableNode({
|
||||
index,
|
||||
removable,
|
||||
onRemove,
|
||||
hidden,
|
||||
onToggleHidden,
|
||||
}: {
|
||||
node: ProxyGroupNodeOption;
|
||||
index: number;
|
||||
removable: boolean;
|
||||
onRemove: () => void;
|
||||
hidden?: boolean;
|
||||
onToggleHidden?: () => void;
|
||||
}) {
|
||||
const {
|
||||
attributes,
|
||||
@@ -135,6 +140,17 @@ function SortableNode({
|
||||
<strong>{node.label}</strong>
|
||||
<span className="proxy-sort-item__sub">{node.meta}</span>
|
||||
</div>
|
||||
{onToggleHidden && (
|
||||
<label className="inline-flex items-center gap-2 text-sm whitespace-nowrap">
|
||||
<input
|
||||
type="checkbox"
|
||||
aria-label={`主分组隐藏 ${node.label}`}
|
||||
checked={hidden}
|
||||
onChange={onToggleHidden}
|
||||
/>
|
||||
主分组隐藏
|
||||
</label>
|
||||
)}
|
||||
{removable && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -170,15 +186,17 @@ export function ProxyGroupEditor({
|
||||
const [selectedSlugs, setSelectedSlugs] = useState(() =>
|
||||
normalizeSelectedSlugs(group?.memberSlugs ?? [], nodes, isMainGroup),
|
||||
);
|
||||
const [hiddenSlugs, setHiddenSlugs] = useState(group?.hiddenSlugs ?? []);
|
||||
const [name, setName] = useState(group?.name ?? "");
|
||||
const [saved, setSaved] = useState(() =>
|
||||
JSON.stringify([
|
||||
group?.name ?? "",
|
||||
normalizeSelectedSlugs(group?.memberSlugs ?? [], nodes, isMainGroup),
|
||||
group?.hiddenSlugs ?? [],
|
||||
]),
|
||||
);
|
||||
const [feedback, setFeedback] = useState("");
|
||||
const dirty = saved !== JSON.stringify([name, selectedSlugs]);
|
||||
const dirty = saved !== JSON.stringify([name, selectedSlugs, hiddenSlugs]);
|
||||
const selectedSlugSet = new Set(selectedSlugs);
|
||||
const nodesBySlug = new Map(nodes.map((node) => [node.slug, node]));
|
||||
const selectedNodes = selectedSlugs
|
||||
@@ -251,7 +269,7 @@ export function ProxyGroupEditor({
|
||||
await (
|
||||
isMainGroup ? saveMainProxyGroupAction : saveProxyGroupAction
|
||||
)(formData);
|
||||
setSaved(JSON.stringify([name, selectedSlugs]));
|
||||
setSaved(JSON.stringify([name, selectedSlugs, hiddenSlugs]));
|
||||
setFeedback("已保存");
|
||||
} catch {
|
||||
setFeedback("保存失败,请重试。当前修改已保留。");
|
||||
@@ -260,6 +278,22 @@ export function ProxyGroupEditor({
|
||||
className="stack-md"
|
||||
>
|
||||
<EditorFields>
|
||||
{isMainGroup && (
|
||||
<>
|
||||
<p className="muted">
|
||||
隐藏仅影响主分组,节点及其他分组仍保留。新节点默认显示;全部隐藏时主分组回退为
|
||||
DIRECT。
|
||||
</p>
|
||||
{hiddenSlugs.map((slug) => (
|
||||
<input
|
||||
key={slug}
|
||||
type="hidden"
|
||||
name="hidden_slugs"
|
||||
value={slug}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{group && !isMainGroup ? (
|
||||
<input type="hidden" name="id" value={group.id} />
|
||||
) : null}
|
||||
@@ -431,6 +465,19 @@ export function ProxyGroupEditor({
|
||||
key={node.slug}
|
||||
node={node}
|
||||
index={index}
|
||||
hidden={hiddenSlugs.includes(node.slug)}
|
||||
onToggleHidden={
|
||||
isMainGroup
|
||||
? () => {
|
||||
setHiddenSlugs((current) =>
|
||||
current.includes(node.slug)
|
||||
? current.filter((s) => s !== node.slug)
|
||||
: [...current, node.slug],
|
||||
);
|
||||
setFeedback("");
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
removable={!isMainGroup}
|
||||
onRemove={() => removeNode(node.slug)}
|
||||
/>
|
||||
@@ -458,12 +505,14 @@ export function ProxyGroupEditor({
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
const [oldName, oldSlugs] = JSON.parse(saved) as [
|
||||
const [oldName, oldSlugs, oldHidden] = JSON.parse(saved) as [
|
||||
string,
|
||||
string[],
|
||||
string[],
|
||||
];
|
||||
setName(oldName);
|
||||
setSelectedSlugs(oldSlugs);
|
||||
setHiddenSlugs(oldHidden);
|
||||
setFeedback("");
|
||||
}}
|
||||
>
|
||||
@@ -471,7 +520,7 @@ export function ProxyGroupEditor({
|
||||
</Button>
|
||||
)}
|
||||
<SubmitButton disabled={!dirty && (isEditing || isMainGroup)}>
|
||||
{isMainGroup ? "保存顺序" : isEditing ? "保存分组" : "新建分组"}
|
||||
{isMainGroup ? "保存主分组" : isEditing ? "保存分组" : "新建分组"}
|
||||
</SubmitButton>
|
||||
</div>
|
||||
</EditorFields>
|
||||
|
||||
Reference in New Issue
Block a user