new branch

This commit is contained in:
rayd1o
2026-03-07 13:06:37 +08:00
parent 3145ff083b
commit 4ada75ca14
64 changed files with 4324 additions and 35 deletions

View File

@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'
import { Table, Tag, Card, Row, Col, Statistic, Button, Modal, Space, Descriptions } from 'antd'
import { AlertOutlined, InfoCircleOutlined, ReloadOutlined } from '@ant-design/icons'
import { useAuthStore } from '../../stores/auth'
import AppLayout from '../../components/AppLayout/AppLayout'
interface Alert {
id: number
@@ -140,7 +141,7 @@ function Alerts() {
)
return (
<div>
<AppLayout>
<Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
<Col span={8}>
<Card>
@@ -173,7 +174,7 @@ function Alerts() {
title="告警列表"
extra={<Button icon={<ReloadOutlined />} onClick={fetchAlerts}></Button>}
>
<Table columns={columns} dataSource={alerts} rowKey="id" loading={loading} pagination={{ pageSize: 10 }} />
<Table columns={columns} dataSource={alerts} rowKey="id" loading={loading} pagination={{ pageSize: 10 }} scroll={{ x: 'max-content' }} tableLayout="fixed" />
</Card>
<Modal
@@ -212,7 +213,7 @@ function Alerts() {
</Descriptions>
)}
</Modal>
</div>
</AppLayout>
)
}

View File

@@ -1,13 +1,15 @@
import { useEffect, useState } from 'react'
import { useEffect, useState, useRef } from 'react'
import {
Table, Tag, Space, Card, Row, Col, Select, Input, Button,
Statistic, Modal, Descriptions, Spin, Empty, Tooltip
} from 'antd'
import type { ColumnsType } from 'antd/es/table'
import {
DatabaseOutlined, GlobalOutlined, CloudServerOutlined,
AppstoreOutlined, EyeOutlined, SearchOutlined
} from '@ant-design/icons'
import axios from 'axios'
import AppLayout from '../../components/AppLayout/AppLayout'
interface CollectedData {
id: number
@@ -153,17 +155,77 @@ function DataList() {
return colors[type] || 'default'
}
const columns = [
const [columnsWidth, setColumnsWidth] = useState<Record<string, number>>({
id: 60,
name: 300,
source: 150,
data_type: 100,
country: 100,
value: 100,
collected_at: 160,
action: 80,
})
const resizeRef = useRef<{ startX: number; startWidth: number; key: string } | null>(null)
const handleResizeStart = (key: string) => (e: React.MouseEvent) => {
e.preventDefault()
e.stopPropagation()
resizeRef.current = {
startX: e.clientX,
startWidth: columnsWidth[key],
key,
}
document.addEventListener('mousemove', handleResizeMove)
document.addEventListener('mouseup', handleResizeEnd)
}
const handleResizeMove = (e: MouseEvent) => {
if (!resizeRef.current) return
const diff = e.clientX - resizeRef.current.startX
const newWidth = Math.max(50, resizeRef.current.startWidth + diff)
setColumnsWidth((prev) => ({
...prev,
[resizeRef.current!.key]: newWidth,
}))
}
const handleResizeEnd = () => {
resizeRef.current = null
document.removeEventListener('mousemove', handleResizeMove)
document.removeEventListener('mouseup', handleResizeEnd)
}
const columns: ColumnsType<CollectedData> = [
{
title: 'ID',
title: () => (
<div style={{ display: 'flex', alignItems: 'center', position: 'relative' }}>
<span>ID</span>
<div
className="resize-handle"
onMouseDown={handleResizeStart('id')}
onClick={(e) => e.stopPropagation()}
/>
</div>
),
dataIndex: 'id',
key: 'id',
width: 80,
width: columnsWidth.id,
},
{
title: '名称',
title: () => (
<div style={{ display: 'flex', alignItems: 'center', position: 'relative' }}>
<span></span>
<div
className="resize-handle"
onMouseDown={handleResizeStart('name')}
onClick={(e) => e.stopPropagation()}
/>
</div>
),
dataIndex: 'name',
key: 'name',
width: columnsWidth.name,
ellipsis: true,
render: (name: string, record: CollectedData) => (
<Tooltip title={name}>
@@ -174,49 +236,95 @@ function DataList() {
),
},
{
title: '数据源',
title: () => (
<div style={{ display: 'flex', alignItems: 'center', position: 'relative' }}>
<span></span>
<div
className="resize-handle"
onMouseDown={handleResizeStart('source')}
onClick={(e) => e.stopPropagation()}
/>
</div>
),
dataIndex: 'source',
key: 'source',
width: 150,
width: columnsWidth.source,
render: (source: string) => (
<Tag icon={getSourceIcon(source)}>{source}</Tag>
),
},
{
title: '类型',
title: () => (
<div style={{ display: 'flex', alignItems: 'center', position: 'relative' }}>
<span></span>
<div
className="resize-handle"
onMouseDown={handleResizeStart('data_type')}
onClick={(e) => e.stopPropagation()}
/>
</div>
),
dataIndex: 'data_type',
key: 'data_type',
width: 120,
width: columnsWidth.data_type,
render: (type: string) => (
<Tag color={getTypeColor(type)}>{type}</Tag>
),
},
{
title: '国家/地区',
title: () => (
<div style={{ display: 'flex', alignItems: 'center', position: 'relative' }}>
<span>/</span>
<div
className="resize-handle"
onMouseDown={handleResizeStart('country')}
onClick={(e) => e.stopPropagation()}
/>
</div>
),
dataIndex: 'country',
key: 'country',
width: 120,
width: columnsWidth.country,
ellipsis: true,
},
{
title: '数值',
title: () => (
<div style={{ display: 'flex', alignItems: 'center', position: 'relative' }}>
<span></span>
<div
className="resize-handle"
onMouseDown={handleResizeStart('value')}
onClick={(e) => e.stopPropagation()}
/>
</div>
),
dataIndex: 'value',
key: 'value',
width: 120,
width: columnsWidth.value,
render: (value: string | null, record: CollectedData) => (
value ? `${value} ${record.unit || ''}` : '-'
),
},
{
title: '采集时间',
title: () => (
<div style={{ display: 'flex', alignItems: 'center', position: 'relative' }}>
<span></span>
<div
className="resize-handle"
onMouseDown={handleResizeStart('collected_at')}
onClick={(e) => e.stopPropagation()}
/>
</div>
),
dataIndex: 'collected_at',
key: 'collected_at',
width: 180,
width: columnsWidth.collected_at,
render: (time: string) => new Date(time).toLocaleString('zh-CN'),
},
{
title: '操作',
key: 'action',
width: 80,
width: columnsWidth.action,
render: (_: unknown, record: CollectedData) => (
<Button
type="link"
@@ -230,7 +338,7 @@ function DataList() {
]
return (
<div>
<AppLayout>
<h2></h2>
{/* Summary Cards */}
@@ -305,6 +413,8 @@ function DataList() {
dataSource={data}
rowKey="id"
loading={loading}
scroll={{ x: 'max-content' }}
tableLayout="fixed"
pagination={{
current: page,
pageSize,
@@ -361,7 +471,7 @@ function DataList() {
<Empty description="暂无数据" />
)}
</Modal>
</div>
</AppLayout>
)
}

View File

@@ -10,6 +10,7 @@ import {
SyncOutlined
} from '@ant-design/icons'
import axios from 'axios'
import AppLayout from '../../components/AppLayout/AppLayout'
interface BuiltInDataSource {
id: number
@@ -326,6 +327,8 @@ function DataSources() {
rowKey="id"
loading={loading}
pagination={false}
scroll={{ x: 'max-content' }}
tableLayout="fixed"
/>
),
},
@@ -352,6 +355,8 @@ function DataSources() {
rowKey="id"
loading={loading}
pagination={false}
scroll={{ x: 'max-content' }}
tableLayout="fixed"
/>
)}
</>
@@ -360,7 +365,7 @@ function DataSources() {
]
return (
<div>
<AppLayout>
<h2></h2>
<Tabs activeKey={activeTab} onChange={setActiveTab} items={tabItems} />
@@ -665,7 +670,7 @@ function DataSources() {
</Form>
)}
</Drawer>
</div>
</AppLayout>
)
}

View File

@@ -0,0 +1,15 @@
function Earth() {
return (
<iframe
src="/earth/3dearthmult.html"
style={{
width: '100vw',
height: '100vh',
border: 'none',
}}
title="3D Earth"
/>
)
}
export default Earth

View File

@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'
import { Table, Tag, Card, Row, Col, Statistic, Button } from 'antd'
import { ReloadOutlined, CheckCircleOutlined, CloseCircleOutlined, SyncOutlined } from '@ant-design/icons'
import { useAuthStore } from '../../stores/auth'
import AppLayout from '../../components/AppLayout/AppLayout'
interface Task {
id: number
@@ -107,7 +108,7 @@ function Tasks() {
const successRate = tasks.length > 0 ? (statusCounts.success / tasks.length) * 100 : 0
return (
<div>
<AppLayout>
<Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
<Col span={6}>
<Card>
@@ -144,9 +145,9 @@ function Tasks() {
</Button>
}
>
<Table columns={columns} dataSource={tasks} rowKey="id" loading={loading} pagination={{ pageSize: 10 }} />
<Table columns={columns} dataSource={tasks} rowKey="id" loading={loading} pagination={{ pageSize: 10 }} scroll={{ x: 'max-content' }} tableLayout="fixed" />
</Card>
</div>
</AppLayout>
)
}

View File

@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'
import { Table, Button, Tag, Space, message, Modal, Form, Input, Select } from 'antd'
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'
import axios from 'axios'
import AppLayout from '../../components/AppLayout/AppLayout'
interface User {
id: number
@@ -113,12 +114,12 @@ function Users() {
]
return (
<div>
<AppLayout>
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between' }}>
<h2></h2>
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}></Button>
</div>
<Table columns={columns} dataSource={users} rowKey="id" loading={loading} />
<Table columns={columns} dataSource={users} rowKey="id" loading={loading} scroll={{ x: 'max-content' }} tableLayout="fixed" />
<Modal
title={editingUser ? '编辑用户' : '添加用户'}
open={modalVisible}
@@ -150,7 +151,7 @@ function Users() {
</Form.Item>
</Form>
</Modal>
</div>
</AppLayout>
)
}