mirror of
https://github.com/Buriburizaem0n/admin-frontend-domain.git
synced 2026-02-03 20:20:11 +00:00
feat: 批量转移服务器给其他用户
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
ModelBatchMoveServerForm,
|
||||
ModelServer,
|
||||
ModelServerConfigForm,
|
||||
ModelServerForm,
|
||||
@@ -15,6 +16,10 @@ export const deleteServer = async (id: number[]): Promise<void> => {
|
||||
return fetcher<void>(FetcherMethod.POST, "/api/v1/batch-delete/server", id)
|
||||
}
|
||||
|
||||
export const batchMoveServer = async (data: ModelBatchMoveServerForm): Promise<void> => {
|
||||
return fetcher<void>(FetcherMethod.POST, "/api/v1/batch-move/server", data)
|
||||
}
|
||||
|
||||
export const forceUpdateServer = async (id: number[]): Promise<ModelServerTaskResponse> => {
|
||||
return fetcher<ModelServerTaskResponse>(FetcherMethod.POST, "/api/v1/force-update/server", id)
|
||||
}
|
||||
|
||||
100
src/components/batch-move-server-icon.tsx
Normal file
100
src/components/batch-move-server-icon.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import { batchMoveServer } from "@/api/server"
|
||||
import { Button, ButtonProps } from "@/components/ui/button"
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
import { IconButton } from "@/components/xui/icon-button"
|
||||
import { useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { toast } from "sonner"
|
||||
import { Textarea } from "./ui/textarea"
|
||||
|
||||
interface BatchMoveServerIconProps extends ButtonProps {
|
||||
serverIds: number[]
|
||||
}
|
||||
|
||||
export const BatchMoveServerIcon: React.FC<BatchMoveServerIconProps> = ({ serverIds, ...props }) => {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
const [toUserId, setToUserId] = useState<number | undefined>(undefined)
|
||||
|
||||
const onSubmit = async () => {
|
||||
try {
|
||||
await batchMoveServer({
|
||||
ids: serverIds,
|
||||
to_user: toUserId!
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
toast(t("Error"), {
|
||||
description: t("Results.UnExpectedError"),
|
||||
})
|
||||
return
|
||||
}
|
||||
toast(t("Done"))
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
return serverIds.length < 1 ? (
|
||||
<IconButton
|
||||
{...props}
|
||||
icon="user-pen"
|
||||
onClick={() => {
|
||||
toast(t("Error"), {
|
||||
description: t("Results.NoRowsAreSelected"),
|
||||
})
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<IconButton {...props} icon="user-pen" />
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-xl">
|
||||
<ScrollArea className="max-h-[calc(100dvh-5rem)] p-3">
|
||||
<div className="items-center mx-1">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("BatchMoveServer")}</DialogTitle>
|
||||
<DialogDescription />
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col gap-3 mt-4">
|
||||
<Label>{t("Servers")}</Label>
|
||||
<Textarea disabled>
|
||||
{serverIds.join(", ")}
|
||||
</Textarea>
|
||||
<Label>{t("ToUser")}</Label>
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="User ID"
|
||||
value={toUserId}
|
||||
onChange={(e) => {
|
||||
setToUserId(parseInt(e.target.value, 10))
|
||||
}}
|
||||
/>
|
||||
<DialogFooter className="justify-end">
|
||||
<DialogClose asChild>
|
||||
<Button type="button" className="my-2" variant="secondary">
|
||||
{t("Cancel")}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<Button disabled={!toUserId || toUserId == 0} type="submit" className="my-2" onClick={onSubmit}>
|
||||
{t("Move")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</div>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
Terminal,
|
||||
Trash2,
|
||||
Upload,
|
||||
UserPen,
|
||||
} from "lucide-react"
|
||||
import { forwardRef } from "react"
|
||||
|
||||
@@ -37,6 +38,7 @@ export interface IconButtonProps extends ButtonProps {
|
||||
| "expand"
|
||||
| "cog"
|
||||
| "minus"
|
||||
| "user-pen"
|
||||
}
|
||||
|
||||
export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>((props, ref) => {
|
||||
@@ -97,6 +99,9 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>((props,
|
||||
case "minus": {
|
||||
return <Minus />
|
||||
}
|
||||
case "user-pen": {
|
||||
return <UserPen />
|
||||
}
|
||||
}
|
||||
})()}
|
||||
</Button>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { swrFetcher } from "@/api/api"
|
||||
import { deleteServer, forceUpdateServer } from "@/api/server"
|
||||
import { ActionButtonGroup } from "@/components/action-button-group"
|
||||
import { BatchMoveServerIcon } from "@/components/batch-move-server-icon"
|
||||
import { CopyButton } from "@/components/copy-button"
|
||||
import { HeaderButtonGroup } from "@/components/header-button-group"
|
||||
import { InstallCommandsMenu } from "@/components/install-commands"
|
||||
@@ -213,6 +214,7 @@ export default function ServerPage() {
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<BatchMoveServerIcon serverIds={selectedRows.map((r) => r.original.id)} />
|
||||
<ServerConfigCardBatch
|
||||
sid={selectedRows.map((r) => r.original.id)}
|
||||
className="shadow-[inset_0_1px_0_rgba(255,255,255,0.2)] bg-yellow-600 text-white hover:bg-yellow-500 dark:hover:bg-yellow-700 rounded-lg"
|
||||
|
||||
@@ -74,6 +74,11 @@ export default function UserPage() {
|
||||
return row.role === 1 ? t("User") : t("Admin")
|
||||
},
|
||||
},
|
||||
{
|
||||
header: t("LastLogin"),
|
||||
accessorKey: "updated_at",
|
||||
accessorFn: (row) => row.updated_at ? new Date(row.updated_at).toLocaleString() : t("Never"),
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
header: t("Actions"),
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
|
||||
* ## ##
|
||||
* ## AUTHOR: acacode ##
|
||||
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
|
||||
* ---------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
|
||||
* ## ##
|
||||
* ## AUTHOR: acacode ##
|
||||
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
|
||||
* ---------------------------------------------------------------
|
||||
*/
|
||||
|
||||
export interface GithubComNezhahqNezhaModelCommonResponseAny {
|
||||
data: any
|
||||
error: string
|
||||
@@ -188,6 +188,11 @@ export interface ModelAlertRuleForm {
|
||||
trigger_mode: number
|
||||
}
|
||||
|
||||
export interface ModelBatchMoveServerForm {
|
||||
ids: number[]
|
||||
to_user: number
|
||||
}
|
||||
|
||||
export interface ModelCreateFMResponse {
|
||||
session_id: string
|
||||
}
|
||||
@@ -631,6 +636,8 @@ export interface ModelServiceResponseItem {
|
||||
|
||||
export interface ModelSetting {
|
||||
admin_template: string
|
||||
/** Agent真实IP */
|
||||
agent_real_ip_header: string
|
||||
/** 覆盖范围(0:提醒未被 IgnoredIPNotification 包含的所有服务器; 1:仅提醒被 IgnoredIPNotification 包含的服务器;) */
|
||||
cover: number
|
||||
custom_code: string
|
||||
@@ -648,17 +655,17 @@ export interface ModelSetting {
|
||||
/** 系统语言,默认 zh_CN */
|
||||
language: string
|
||||
oauth2_providers: string[]
|
||||
/** 前端真实IP */
|
||||
web_real_ip_header: string
|
||||
/** Agent真实IP */
|
||||
agent_real_ip_header: string
|
||||
site_name: string
|
||||
/** 用于前端判断生成的安装命令是否启用 TLS */
|
||||
tls: boolean
|
||||
user_template: string
|
||||
/** 前端真实IP */
|
||||
web_real_ip_header: string
|
||||
}
|
||||
|
||||
export interface ModelSettingForm {
|
||||
/** Agent真实IP */
|
||||
agent_real_ip_header?: string
|
||||
cover: number
|
||||
custom_code?: string
|
||||
custom_code_dashboard?: string
|
||||
@@ -671,14 +678,12 @@ export interface ModelSettingForm {
|
||||
ip_change_notification_group_id: number
|
||||
/** @minLength 2 */
|
||||
language: string
|
||||
/** 前端真实IP */
|
||||
web_real_ip_header?: string
|
||||
/** Agent真实IP */
|
||||
agent_real_ip_header?: string
|
||||
/** @minLength 1 */
|
||||
site_name: string
|
||||
tls?: boolean
|
||||
user_template?: string
|
||||
/** 前端真实IP */
|
||||
web_real_ip_header?: string
|
||||
}
|
||||
|
||||
export interface ModelSettingResponse {
|
||||
|
||||
Reference in New Issue
Block a user