Files
labelmain/app/management/team/sync_server/page.tsx
2026-02-27 10:21:43 +08:00

281 lines
7.7 KiB
TypeScript

"use client"
import {
getDataSyncServerList,
updateSyncServerLocation,
} from "@/components/label/api/sync"
import { Response } from "@/components/label/api/sync/typing"
import useAuth from "@/components/label/hooks/useAuth"
import {
ActionIcon,
Badge,
Button,
Group,
Modal,
Paper,
Select,
Stack,
TextInput,
} from "@mantine/core"
import { notifications } from "@mantine/notifications"
import { IconEdit, IconRefresh, IconSearch } from "@tabler/icons-react"
import { DataTable, DataTableColumn } from "mantine-datatable"
import { useCallback, useEffect, useMemo, useState } from "react"
export default function TeamSyncServerPage() {
const { isShow } = useAuth()
const [records, setRecords] = useState<Response.SyncServer[]>([])
const [loading, setLoading] = useState(false)
const [totalItems, setTotalItems] = useState(0)
const [page, setPage] = useState(1)
const [pageSize, setPageSize] = useState(20)
const [location, setLocation] = useState("")
const [appliedLocation, setAppliedLocation] = useState("")
const [online, setOnline] = useState<string | null>(null)
const [appliedOnline, setAppliedOnline] = useState<string | null>(null)
const [open, setOpen] = useState(false)
const [editingRecord, setEditingRecord] =
useState<Response.SyncServer | null>(null)
const [editingLocation, setEditingLocation] = useState("")
const [submitting, setSubmitting] = useState(false)
const load = useCallback(async () => {
try {
setLoading(true)
const params: any = {
page_number: page,
page_size: pageSize,
}
if (appliedLocation.trim()) params.location = appliedLocation.trim()
if (appliedOnline === "true") params.is_online = true
if (appliedOnline === "false") params.is_online = false
const res = await getDataSyncServerList(params)
setRecords(res?.sync_servers ?? [])
setTotalItems(res?.total_items ?? 0)
} catch (e) {
setRecords([])
setTotalItems(0)
notifications.show({
color: "red",
title: "加载失败",
message: e instanceof Error ? e.message : "请求失败",
})
} finally {
setLoading(false)
}
}, [appliedLocation, appliedOnline, page, pageSize])
useEffect(() => {
load()
}, [load])
const columns = useMemo(() => {
const cols: DataTableColumn<Response.SyncServer>[] = [
{ accessor: "id", title: "ID", width: 80 },
{ accessor: "ip", title: "IP", width: 180 },
{ accessor: "location", title: "IP所在地", width: 180 },
{
accessor: "is_online",
title: "状态",
width: 100,
render: (record) =>
record.is_online ? (
<Badge color="green" variant="light">
线
</Badge>
) : (
<Badge color="red" variant="light">
线
</Badge>
),
},
{ accessor: "create_at", title: "创建时间", width: 180 },
{ accessor: "update_at", title: "更新时间", width: 180 },
{
accessor: "operation",
title: "操作",
width: 100,
textAlign: "center",
render: (record) =>
isShow("server_edit") ? (
<ActionIcon
variant="subtle"
color="blue"
onClick={() => {
setEditingRecord(record)
setEditingLocation(record.location ?? "")
setOpen(true)
}}>
<IconEdit size={16} />
</ActionIcon>
) : null,
},
]
return cols
}, [isShow])
const saveLocation = async () => {
if (!editingRecord?.id) return
try {
setSubmitting(true)
await updateSyncServerLocation(editingLocation.trim(), editingRecord.id)
notifications.show({
color: "green",
title: "操作成功",
message: "已更新同步服务所在地",
})
setOpen(false)
setEditingRecord(null)
setEditingLocation("")
load()
} catch (e) {
notifications.show({
color: "red",
title: "更新失败",
message: e instanceof Error ? e.message : "请求失败",
})
} finally {
setSubmitting(false)
}
}
return (
<Stack w="100%" h="calc(100vh - 56px)" p="md" gap="md">
<Paper
withBorder
p="md"
radius="md"
style={{
borderColor:
"light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-4))",
}}>
<Group wrap="wrap">
<TextInput
placeholder="请输入IP所在地"
size="xs"
radius="xs"
value={location}
onChange={(e) => setLocation(e.currentTarget.value)}
/>
<Select
placeholder="状态"
size="xs"
radius="xs"
data={[
{ label: "在线", value: "true" },
{ label: "离线", value: "false" },
]}
clearable
value={online}
onChange={setOnline}
/>
<Button
size="xs"
radius="xs"
leftSection={<IconSearch size={16} />}
onClick={() => {
setAppliedLocation(location)
setAppliedOnline(online)
setPage(1)
}}>
</Button>
<Button
variant="default"
size="xs"
radius="xs"
leftSection={<IconRefresh size={16} />}
onClick={() => {
setLocation("")
setOnline(null)
setAppliedLocation("")
setAppliedOnline(null)
setPage(1)
setPageSize(20)
}}>
</Button>
</Group>
</Paper>
<Paper
withBorder
p="md"
radius="md"
style={{
borderColor:
"light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-4))",
flex: 1,
minHeight: 0,
display: "flex",
}}>
<DataTable<Response.SyncServer>
withTableBorder
withRowBorders
fetching={loading}
records={records}
columns={columns}
totalRecords={totalItems}
recordsPerPage={pageSize}
page={page}
onPageChange={setPage}
onRecordsPerPageChange={(v) => {
setPageSize(v)
setPage(1)
}}
recordsPerPageOptions={[10, 20, 50, 100]}
noRecordsText="暂无数据"
scrollAreaProps={{ type: "auto" }}
style={{ width: "100%" }}
/>
</Paper>
<Modal
opened={open}
onClose={() => {
setOpen(false)
setEditingRecord(null)
setEditingLocation("")
}}
title="更新同步服务所在地"
centered
size={520}
closeOnClickOutside={false}>
<Stack gap="sm">
<TextInput
label="所在地"
size="xs"
radius="xs"
value={editingLocation}
onChange={(e) => setEditingLocation(e.currentTarget.value)}
/>
<Group justify="flex-end">
<Button
variant="default"
size="xs"
radius="xs"
onClick={() => {
setOpen(false)
setEditingRecord(null)
setEditingLocation("")
}}>
</Button>
<Button
size="xs"
radius="xs"
onClick={saveLocation}
loading={submitting}>
</Button>
</Group>
</Stack>
</Modal>
</Stack>
)
}