feat(team): add page
This commit is contained in:
279
app/management/team/sync_server/page.tsx
Normal file
279
app/management/team/sync_server/page.tsx
Normal file
@@ -0,0 +1,279 @@
|
||||
"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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user