merge: 同步 upstream/main 新功能到定制版本

- feat: Add slider verification code (bd67997)
- fix bug: Inventory cannot be zero (1f7a6ee)
- fix: resolve merge conflicts and lint errors
This commit is contained in:
2026-03-23 21:50:10 -07:00
parent 3806264343
commit d6616c5859
63 changed files with 3111 additions and 1130 deletions
+96 -81
View File
@@ -50,28 +50,91 @@ export default function NodeGroups() {
<CardHeader>
<CardTitle>{t("nodeGroups", "Node Groups")}</CardTitle>
<CardDescription>
{t("nodeGroupsDescription", "Manage node groups for user access control")}
{t(
"nodeGroupsDescription",
"Manage node groups for user access control"
)}
</CardDescription>
</CardHeader>
<CardContent>
<ProTable<API.NodeGroup, API.GetNodeGroupListRequest>
action={ref}
request={async (params) => {
const { data } = await getNodeGroupList({
page: params.page || 1,
size: params.size || 10,
});
return {
list: data.data?.list || [],
total: data.data?.total || 0,
};
actions={{
render: (row: any) => [
<NodeGroupForm
allNodeGroups={allNodeGroups}
currentGroupId={row.id}
initialValues={row}
key={`edit-${row.id}`}
loading={loading}
onSubmit={async (values) => {
setLoading(true);
try {
await updateNodeGroup({
id: row.id,
...values,
} as API.UpdateNodeGroupRequest);
toast.success(t("updated", "Updated successfully"));
// 刷新节点组列表
const { data } = await getNodeGroupList({
page: 1,
size: 1000,
});
setAllNodeGroups(data.data?.list || []);
ref.current?.refresh();
setLoading(false);
return true;
} catch {
setLoading(false);
return false;
}
}}
title={t("editNodeGroup", "Edit Node Group")}
trigger={
<Button size="sm" variant="outline">
{t("edit", "Edit")}
</Button>
}
/>,
<ConfirmButton
cancelText={t("cancel", "Cancel")}
confirmText={t("confirm", "Confirm")}
description={t(
"deleteNodeGroupConfirm",
"This will delete the node group. Nodes in this group will be reassigned."
)}
key="delete"
onConfirm={async () => {
await deleteNodeGroup({ id: row.id });
toast.success(t("deleted", "Deleted successfully"));
// 刷新节点组列表
const { data } = await getNodeGroupList({
page: 1,
size: 1000,
});
setAllNodeGroups(data.data?.list || []);
ref.current?.refresh();
setLoading(false);
}}
title={t("confirmDelete", "Confirm Delete")}
trigger={
<Button size="sm" variant="destructive">
{t("delete", "Delete")}
</Button>
}
/>,
],
}}
columns={[
{
id: "id",
accessorKey: "id",
header: t("id", "ID"),
cell: ({ row }: { row: any }) => <span className="text-muted-foreground">#{row.getValue("id")}</span>,
cell: ({ row }: { row: any }) => (
<span className="text-muted-foreground">
#{row.getValue("id")}
</span>
),
},
{
id: "name",
@@ -95,7 +158,8 @@ export default function NodeGroups() {
id: "description",
accessorKey: "description",
header: t("description", "Description"),
cell: ({ row }: { row: any }) => row.getValue("description") || "--",
cell: ({ row }: { row: any }) =>
row.getValue("description") || "--",
},
{
id: "for_calculation",
@@ -134,82 +198,27 @@ export default function NodeGroups() {
header: t("sort", "Sort"),
},
]}
actions={{
render: (row: any) => [
<NodeGroupForm
key={`edit-${row.id}`}
initialValues={row}
allNodeGroups={allNodeGroups}
currentGroupId={row.id}
loading={loading}
onSubmit={async (values) => {
setLoading(true);
try {
await updateNodeGroup({
id: row.id,
...values,
} as API.UpdateNodeGroupRequest);
toast.success(t("updated", "Updated successfully"));
// 刷新节点组列表
const { data } = await getNodeGroupList({ page: 1, size: 1000 });
setAllNodeGroups(data.data?.list || []);
ref.current?.refresh();
setLoading(false);
return true;
} catch {
setLoading(false);
return false;
}
}}
title={t("editNodeGroup", "Edit Node Group")}
trigger={
<Button variant="outline" size="sm">
{t("edit", "Edit")}
</Button>
}
/>,
<ConfirmButton
key="delete"
cancelText={t("cancel", "Cancel")}
confirmText={t("confirm", "Confirm")}
description={t(
"deleteNodeGroupConfirm",
"This will delete the node group. Nodes in this group will be reassigned."
)}
onConfirm={async () => {
await deleteNodeGroup({ id: row.id });
toast.success(t("deleted", "Deleted successfully"));
// 刷新节点组列表
const { data } = await getNodeGroupList({ page: 1, size: 1000 });
setAllNodeGroups(data.data?.list || []);
ref.current?.refresh();
setLoading(false);
}}
title={t("confirmDelete", "Confirm Delete")}
trigger={
<Button variant="destructive" size="sm">
{t("delete", "Delete")}
</Button>
}
/>,
],
}}
header={{
title: t("nodeGroups", "Node Groups"),
toolbar: (
<NodeGroupForm
key="create"
initialValues={undefined}
allNodeGroups={allNodeGroups}
currentGroupId={undefined}
initialValues={undefined}
key="create"
loading={loading}
onSubmit={async (values) => {
setLoading(true);
try {
await createNodeGroup(values as API.CreateNodeGroupRequest);
await createNodeGroup(
values as API.CreateNodeGroupRequest
);
toast.success(t("created", "Created successfully"));
// 刷新节点组列表
const { data } = await getNodeGroupList({ page: 1, size: 1000 });
const { data } = await getNodeGroupList({
page: 1,
size: 1000,
});
setAllNodeGroups(data.data?.list || []);
ref.current?.refresh();
setLoading(false);
@@ -220,14 +229,20 @@ export default function NodeGroups() {
}
}}
title={t("createNodeGroup", "Create Node Group")}
trigger={
<Button>
{t("create", "Create")}
</Button>
}
trigger={<Button>{t("create", "Create")}</Button>}
/>
),
}}
request={async (params) => {
const { data } = await getNodeGroupList({
page: params.page || 1,
size: params.size || 10,
});
return {
list: data.data?.list || [],
total: data.data?.total || 0,
};
}}
/>
</CardContent>
</Card>