30 changed files with 1195 additions and 653 deletions
@ -1,63 +1,26 @@ |
|||||
/** |
import component from "@/locales/bn-BD/component"; |
||||
* @name umi 的路由配置 |
|
||||
* @description 只支持 path,component,routes,redirect,wrappers,name,icon 的配置 |
|
||||
* @param path path 只支持两种占位符配置,第一种是动态参数 :id 的形式,第二种是 * 通配符,通配符只能出现路由字符串的最后。 |
|
||||
* @param component 配置 location 和 path 匹配后用于渲染的 React 组件路径。可以是绝对路径,也可以是相对路径,如果是相对路径,会从 src/pages 开始找起。 |
|
||||
* @param routes 配置子路由,通常在需要为多个路径增加 layout 组件时使用。 |
|
||||
* @param redirect 配置路由跳转 |
|
||||
* @param wrappers 配置路由组件的包装组件,通过包装组件可以为当前的路由组件组合进更多的功能。 比如,可以用于路由级别的权限校验 |
|
||||
* @param name 配置路由的标题,默认读取国际化文件 menu.ts 中 menu.xxxx 的值,如配置 name 为 login,则读取 menu.ts 中 menu.login 的取值作为标题 |
|
||||
* @param icon 配置路由的图标,取值参考 https://ant.design/components/icon-cn, 注意去除风格后缀和大小写,如想要配置图标为 <StepBackwardOutlined /> 则取值应为 stepBackward 或 StepBackward,如想要配置图标为 <UserOutlined /> 则取值应为 user 或者 User
|
|
||||
* @doc https://umijs.org/docs/guides/routes
|
|
||||
*/ |
|
||||
export default [ |
export default [ |
||||
{ |
{ |
||||
path: '/user', |
path: '/user', |
||||
layout: false, |
layout: false, |
||||
routes: [ |
routes: [{ name: '登录', path: '/user/login', component: './User/Login' }], |
||||
{ |
|
||||
name: 'login', |
|
||||
path: '/user/login', |
|
||||
component: './User/Login', |
|
||||
}, |
|
||||
], |
|
||||
}, |
|
||||
{ |
|
||||
path: '/welcome', |
|
||||
name: 'welcome', |
|
||||
icon: 'smile', |
|
||||
component: './Welcome', |
|
||||
}, |
}, |
||||
|
{ path: '/welcome', name: '欢迎', icon: 'smile', component: './Welcome' }, |
||||
|
{ path: '/dashboard/workplace', name: '工作台', icon: 'dashboard', component: './dashboard'}, |
||||
{ |
{ |
||||
path: '/admin', |
path: '/admin', |
||||
name: 'admin', |
name: '管理页', |
||||
icon: 'crown', |
icon: 'crown', |
||||
access: 'canAdmin', |
access: 'canAdmin', |
||||
routes: [ |
routes: [ |
||||
{ |
{ path: '/admin', redirect: '/admin/sub-page' }, |
||||
path: '/admin', |
{ path: '/admin/sub-page', name: '二级管理页', component: './Admin' }, |
||||
redirect: '/admin/sub-page', |
|
||||
}, |
|
||||
{ |
|
||||
path: '/admin/sub-page', |
|
||||
name: 'sub-page', |
|
||||
component: './Admin', |
|
||||
}, |
|
||||
], |
], |
||||
}, |
}, |
||||
{ |
{ name: '设备列表', icon: 'table', path: '/list/device-list', component: './TableList' }, |
||||
name: 'list.table-list', |
{ name: '设备分组', icon: 'table', path: '/list/group-list', component: './TableList/group' }, |
||||
icon: 'table', |
{ name: '记录列表', icon: 'table', path: '/list/basic-list', component: './TableList/list' }, |
||||
path: '/list', |
{ path: '/', redirect: '/welcome' }, |
||||
component: './TableList', |
{ path: '*', layout: false, component: './404' }, |
||||
}, |
|
||||
{ |
|
||||
path: '/', |
|
||||
redirect: '/welcome', |
|
||||
}, |
|
||||
{ |
|
||||
path: '*', |
|
||||
layout: false, |
|
||||
component: './404', |
|
||||
}, |
|
||||
]; |
]; |
||||
|
|||||
@ -0,0 +1,10 @@ |
|||||
|
module app |
||||
|
|
||||
|
go 1.22.2 |
||||
|
|
||||
|
require ( |
||||
|
github.com/gorilla/websocket v1.5.1 |
||||
|
golang.org/x/sys v0.19.0 |
||||
|
) |
||||
|
|
||||
|
require golang.org/x/net v0.17.0 // indirect |
||||
@ -0,0 +1,6 @@ |
|||||
|
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= |
||||
|
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= |
||||
|
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= |
||||
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= |
||||
|
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= |
||||
|
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= |
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 1017 B |
|
After Width: | Height: | Size: 3.8 KiB |
@ -1,18 +1,16 @@ |
|||||
import { history, useIntl } from '@umijs/max'; |
import { history } from '@umijs/max'; |
||||
import { Button, Result } from 'antd'; |
import { Button, Result } from 'antd'; |
||||
import React from 'react'; |
import React from 'react'; |
||||
|
|
||||
const NoFoundPage: React.FC = () => ( |
const NoFoundPage: React.FC = () => ( |
||||
<Result |
<Result |
||||
status="404" |
status="404" |
||||
title="404" |
title="404" |
||||
subTitle={useIntl().formatMessage({ id: 'pages.404.subTitle' })} |
subTitle={'抱歉,您访问的页面不存在。'} |
||||
extra={ |
extra={ |
||||
<Button type="primary" onClick={() => history.push('/')}> |
<Button type="primary" onClick={() => history.push('/')}> |
||||
{useIntl().formatMessage({ id: 'pages.404.buttonText' })} |
{'返回首页'} |
||||
</Button> |
</Button> |
||||
} |
} |
||||
/> |
/> |
||||
); |
); |
||||
|
|
||||
export default NoFoundPage; |
export default NoFoundPage; |
||||
|
|||||
@ -0,0 +1,145 @@ |
|||||
|
import React, { useRef } from 'react'; |
||||
|
import { ActionType, ModalForm, ProColumns, ProFormText, ProTable } from '@ant-design/pro-components'; |
||||
|
import { Button, Popconfirm, message } from 'antd'; |
||||
|
import { request } from '@umijs/max'; |
||||
|
|
||||
|
// 定义一个接口来描述分组的形状
|
||||
|
interface Tag { |
||||
|
id: number; |
||||
|
TagKey: string; |
||||
|
TagValue: string; |
||||
|
} |
||||
|
|
||||
|
const TagsTable: React.FC = () => { |
||||
|
// 创建一个actionRef来控制ProTable
|
||||
|
const actionRef = useRef<ActionType>(); |
||||
|
|
||||
|
const onFinish = async (values: { group_name: string, description: string }) => { |
||||
|
try { |
||||
|
// 使用@umijs/max的request方法发送POST请求
|
||||
|
const { success, message: apiMessage, group_id } = await request('https://867t766n6.zicp.fun/groups', { |
||||
|
method: 'POST', |
||||
|
data: { |
||||
|
group_name: values.group_name, |
||||
|
description: values.description, // 假设对于新分组TagValue是空的
|
||||
|
}, |
||||
|
// 注意:@umijs/max的request默认使用'application/json',并自动转换body为JSON字符串
|
||||
|
}); |
||||
|
|
||||
|
// 根据接口返回的success字段判断操作是否成功
|
||||
|
if (success) { |
||||
|
message.success(apiMessage || '分组创建成功!'); |
||||
|
actionRef.current?.reload(); // 刷新表格以显示新创建的分组
|
||||
|
return true; |
||||
|
} else { |
||||
|
message.error(apiMessage || '分组创建失败。'); |
||||
|
return false; |
||||
|
} |
||||
|
} catch (error) { |
||||
|
// 处理请求过程中出现的错误
|
||||
|
console.error('创建操作失败', error); |
||||
|
message.error('操作异常,请稍后重试。'); |
||||
|
return false; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 使用ProColumns<Tag>来确保columns与Tag接口一致
|
||||
|
const columns: ProColumns<Tag>[] = [ |
||||
|
{ title: 'ID', dataIndex: 'id', key: 'id' }, |
||||
|
{ title: '分组名称', dataIndex: 'group_name', key: 'group_name' }, |
||||
|
{ title: '分组描述', dataIndex: 'description', key: 'description' }, |
||||
|
{ |
||||
|
title: '操作', |
||||
|
key: 'action', |
||||
|
render: (_, record: Tag) => ( |
||||
|
<Popconfirm |
||||
|
placement="topRight" |
||||
|
title="确定要删除此分组吗?" |
||||
|
onConfirm={() => deleteTag(record)} |
||||
|
okText="确定" |
||||
|
cancelText="取消" |
||||
|
> |
||||
|
<Button danger> |
||||
|
删除 |
||||
|
</Button> |
||||
|
</Popconfirm> |
||||
|
), |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
const deleteTag = async (record: Tag) => { |
||||
|
try { |
||||
|
// 使用@umijs/max的request方法发送DELETE请求
|
||||
|
const { success, message: apiMessage } = await request(`https://867t766n6.zicp.fun/groups/${record.id}`, { |
||||
|
method: 'DELETE', |
||||
|
}); |
||||
|
|
||||
|
// 根据返回的success字段判断操作是否成功
|
||||
|
if (success) { |
||||
|
message.success(apiMessage || '分组删除成功。'); |
||||
|
} else { |
||||
|
message.error(apiMessage || '分组删除失败。'); |
||||
|
} |
||||
|
|
||||
|
// 如果你有引用ProTable的actionRef来刷新数据,也可以在这里调用
|
||||
|
actionRef.current?.reload(); |
||||
|
} catch (error) { |
||||
|
// 处理请求过程中出现的错误
|
||||
|
console.error('删除操作失败', error); |
||||
|
message.error('操作异常,请稍后重试。'); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
return <ProTable<Tag> |
||||
|
columns={columns} |
||||
|
actionRef={actionRef} |
||||
|
request={async (params, sorter, filter) => { |
||||
|
// 使用 @umijs/max 的 request 方法请求数据
|
||||
|
const response = await request('https://867t766n6.zicp.fun/groups', { |
||||
|
method: 'GET', // 根据实际请求调整
|
||||
|
// 可以在这里传递查询参数(如分页信息),或者设置请求头部等
|
||||
|
// params: { ...params },
|
||||
|
}); |
||||
|
|
||||
|
// 直接从response中解构出data
|
||||
|
const { data } = response; |
||||
|
|
||||
|
return { |
||||
|
data: data, // 实际的数据数组
|
||||
|
success: true, // 请求是否成功
|
||||
|
total: data.length, // 数据总数,用于分页
|
||||
|
}; |
||||
|
}} |
||||
|
|
||||
|
rowKey="id" |
||||
|
pagination={false} |
||||
|
search={false} |
||||
|
toolBarRender={() => [ |
||||
|
<ModalForm |
||||
|
title="新建分组" |
||||
|
trigger={ |
||||
|
<Button type="primary"> |
||||
|
新建分组 |
||||
|
</Button> |
||||
|
} |
||||
|
onFinish={onFinish} |
||||
|
modalProps={{ |
||||
|
onCancel: () => console.log('取消新建分组'), |
||||
|
}} |
||||
|
> |
||||
|
<ProFormText |
||||
|
name="group_name" |
||||
|
label="名称" |
||||
|
rules={[{ required: true, message: '请输入分组名称' }]} |
||||
|
/> |
||||
|
<ProFormText |
||||
|
name="description" |
||||
|
label="描述" |
||||
|
rules={[{ required: true, message: '请输入分组描述' }]} |
||||
|
/> |
||||
|
</ModalForm>, |
||||
|
]} |
||||
|
/>; |
||||
|
}; |
||||
|
|
||||
|
export default TagsTable; |
||||
@ -0,0 +1,353 @@ |
|||||
|
import { addRule, history, removeRule, updateRule } from '@/services/ant-design-pro/api'; |
||||
|
import { DeleteOutlined, ExportOutlined, PlusOutlined } from '@ant-design/icons'; |
||||
|
import type { ActionType, ProColumns, ProDescriptionsItemProps } from '@ant-design/pro-components'; |
||||
|
import { |
||||
|
FooterToolbar, |
||||
|
ModalForm, |
||||
|
PageContainer, |
||||
|
ProDescriptions, |
||||
|
ProFormText, |
||||
|
ProFormTextArea, |
||||
|
ProTable, |
||||
|
} from '@ant-design/pro-components'; |
||||
|
import '@umijs/max'; |
||||
|
import { Button, Drawer, Image, Modal, Popconfirm, Typography, message } from 'antd'; |
||||
|
import React, { useRef, useState } from 'react'; |
||||
|
import type { FormValueType } from './components/UpdateForm'; |
||||
|
import UpdateForm from './components/UpdateForm'; |
||||
|
import { request } from '@umijs/max'; |
||||
|
|
||||
|
const { Text, Link } = Typography; |
||||
|
|
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* @en-US Add node |
||||
|
* @zh-CN 添加节点 |
||||
|
* @param fields |
||||
|
*/ |
||||
|
const handleAdd = async (fields: API.RuleListItem) => { |
||||
|
const hide = message.loading('正在添加'); |
||||
|
try { |
||||
|
await addRule({ |
||||
|
...fields, |
||||
|
}); |
||||
|
hide(); |
||||
|
message.success('Added successfully'); |
||||
|
return true; |
||||
|
} catch (error) { |
||||
|
hide(); |
||||
|
message.error('Adding failed, please try again!'); |
||||
|
return false; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* @en-US Update node |
||||
|
* @zh-CN 更新节点 |
||||
|
* |
||||
|
* @param fields |
||||
|
*/ |
||||
|
const handleUpdate = async (fields: FormValueType) => { |
||||
|
const hide = message.loading('Configuring'); |
||||
|
try { |
||||
|
await updateRule({ |
||||
|
name: fields.name, |
||||
|
desc: fields.desc, |
||||
|
key: fields.guid, |
||||
|
}); |
||||
|
hide(); |
||||
|
message.success('Configuration is successful'); |
||||
|
return true; |
||||
|
} catch (error) { |
||||
|
hide(); |
||||
|
message.error('Configuration failed, please try again!'); |
||||
|
return false; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Delete node |
||||
|
* @zh-CN 删除节点 |
||||
|
* |
||||
|
* @param selectedRows |
||||
|
*/ |
||||
|
const handleRemove = async (selectedRows: API.RuleListItem[]) => { |
||||
|
const hide = message.loading('正在删除'); |
||||
|
if (!selectedRows) return true; |
||||
|
try { |
||||
|
await removeRule({ |
||||
|
key: selectedRows.map((row) => row.key), |
||||
|
}); |
||||
|
hide(); |
||||
|
message.success('删除成功,即将刷新'); |
||||
|
return true; |
||||
|
} catch (error) { |
||||
|
hide(); |
||||
|
message.error('删除失败,请重试'); |
||||
|
return false; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const handleExport = async () => { |
||||
|
try { |
||||
|
const response = await request('https://867t766n6.zicp.fun/api/exportAccount', { |
||||
|
responseType: 'blob', |
||||
|
}); |
||||
|
const blob = new Blob([response], { type: 'text/csv' }); // 使用 response 而不是 response.data
|
||||
|
const url = window.URL.createObjectURL(blob); |
||||
|
const link = document.createElement('a'); |
||||
|
link.href = url; |
||||
|
link.setAttribute('download', 'export.csv'); |
||||
|
document.body.appendChild(link); |
||||
|
link.click(); |
||||
|
document.body.removeChild(link); // 移除创建的链接
|
||||
|
} catch (error) { |
||||
|
console.error('Error exporting file:', error); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const TableList: React.FC = () => { |
||||
|
/** |
||||
|
* @en-US Pop-up window of new window |
||||
|
* @zh-CN 新建窗口的弹窗 |
||||
|
* */ |
||||
|
const [createModalOpen, handleModalOpen] = useState<boolean>(false); |
||||
|
/** |
||||
|
* @en-US The pop-up window of the distribution update window |
||||
|
* @zh-CN 分布更新窗口的弹窗 |
||||
|
* */ |
||||
|
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false); |
||||
|
const [showDetail, setShowDetail] = useState<boolean>(false); |
||||
|
const actionRef = useRef<ActionType>(); |
||||
|
const [currentRow, setCurrentRow] = useState<API.RuleListItem>(); |
||||
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]); |
||||
|
const [selectedRowsState, setSelectedRows] = useState<API.RuleListItem[]>([]); |
||||
|
const [historyModalVisible, setHistoryModalVisible] = useState(false); |
||||
|
const handleViewClick = (record) => { |
||||
|
setCurrentRow(record); |
||||
|
setHistoryModalVisible(true); |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* @en-US International configuration |
||||
|
* @zh-CN 国际化配置 |
||||
|
* */ |
||||
|
|
||||
|
const columns: ProColumns<API.RuleListItem>[] = [ |
||||
|
{ |
||||
|
title: '名称', |
||||
|
dataIndex: 'user_name', |
||||
|
render: (dom, entity) => { |
||||
|
return ( |
||||
|
<> |
||||
|
{/* <a |
||||
|
onClick={() => { |
||||
|
setCurrentRow(entity); |
||||
|
setShowDetail(true); |
||||
|
}} |
||||
|
> |
||||
|
{dom} |
||||
|
</a> */} |
||||
|
<Text type="success">{dom}</Text> |
||||
|
<div>{entity.name}</div> |
||||
|
</> |
||||
|
); |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
title: '类型', |
||||
|
dataIndex: 'format', |
||||
|
valueType: 'select', |
||||
|
valueEnum: { |
||||
|
CF_TEXT: { |
||||
|
text: '文本', |
||||
|
}, |
||||
|
CF_BITMAP: { |
||||
|
text: '图片', |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
title: '内容', |
||||
|
dataIndex: 'content', |
||||
|
sorter: true, |
||||
|
hideInForm: true, |
||||
|
width: '60%', |
||||
|
}, |
||||
|
{ |
||||
|
title: '地址', |
||||
|
dataIndex: 'detected_address', |
||||
|
hideInSearch: true, |
||||
|
}, |
||||
|
{ |
||||
|
title: '时间', |
||||
|
sorter: true, |
||||
|
dataIndex: 'timestamp', |
||||
|
valueType: 'dateTime', |
||||
|
}, |
||||
|
{ |
||||
|
title: '操作', |
||||
|
dataIndex: 'option', |
||||
|
valueType: 'option', |
||||
|
render: (_, record) => [ |
||||
|
<Popconfirm |
||||
|
key={record.id} |
||||
|
title="确定要删除该行吗?" |
||||
|
onConfirm={async () => { |
||||
|
await handleRemove(record.id); // 直接传递代理ID
|
||||
|
actionRef.current?.reload(); |
||||
|
}} |
||||
|
okText="确定" |
||||
|
cancelText="取消" |
||||
|
> |
||||
|
<Button danger icon={<DeleteOutlined />} /> |
||||
|
</Popconfirm>, |
||||
|
], |
||||
|
}, |
||||
|
]; |
||||
|
return ( |
||||
|
<PageContainer> |
||||
|
<ProTable<API.RuleListItem, API.PageParams> |
||||
|
headerTitle="记录" |
||||
|
actionRef={actionRef} |
||||
|
rowKey="id" |
||||
|
// search={{
|
||||
|
// labelWidth: 120,
|
||||
|
// }}
|
||||
|
search={false} |
||||
|
toolBarRender={() => [ |
||||
|
<Button |
||||
|
type="primary" |
||||
|
key="export" |
||||
|
onClick={() => { |
||||
|
handleExport(); |
||||
|
}} |
||||
|
> |
||||
|
<ExportOutlined /> 导出 |
||||
|
</Button>, |
||||
|
]} |
||||
|
request={history} |
||||
|
columns={columns} |
||||
|
rowSelection={{ |
||||
|
preserveSelectedRowKeys: true, |
||||
|
selectedRowKeys, |
||||
|
onChange: (keys, selectedRows) => { |
||||
|
setSelectedRowKeys(keys); |
||||
|
setSelectedRows(selectedRows); |
||||
|
}, |
||||
|
}} |
||||
|
/> |
||||
|
{selectedRowsState?.length > 0 && ( |
||||
|
<FooterToolbar |
||||
|
extra={ |
||||
|
<div> |
||||
|
已选择{' '} |
||||
|
<a |
||||
|
style={{ |
||||
|
fontWeight: 600, |
||||
|
}} |
||||
|
> |
||||
|
{selectedRowsState.length} |
||||
|
</a>{' '} |
||||
|
项 |
||||
|
{/* <span> |
||||
|
<FormattedMessage |
||||
|
id="pages.searchTable.totalServiceCalls" |
||||
|
defaultMessage="Total number of service calls" |
||||
|
/>{' '} |
||||
|
{selectedRowsState.reduce((pre, item) => pre + item.callNo!, 0)}{' '} |
||||
|
<FormattedMessage id="pages.searchTable.tenThousand" defaultMessage="万" /> |
||||
|
</span> */} |
||||
|
</div> |
||||
|
} |
||||
|
> |
||||
|
<Button |
||||
|
onClick={async () => { |
||||
|
await handleRemove(selectedRowsState); |
||||
|
setSelectedRows([]); |
||||
|
actionRef.current?.reloadAndRest?.(); |
||||
|
}} |
||||
|
> |
||||
|
批量删除 |
||||
|
</Button> |
||||
|
</FooterToolbar> |
||||
|
)} |
||||
|
<ModalForm |
||||
|
title={'新建规则'} |
||||
|
width="400px" |
||||
|
open={createModalOpen} |
||||
|
onOpenChange={handleModalOpen} |
||||
|
onFinish={async (value) => { |
||||
|
const success = await handleAdd(value as API.RuleListItem); |
||||
|
if (success) { |
||||
|
handleModalOpen(false); |
||||
|
if (actionRef.current) { |
||||
|
actionRef.current.reload(); |
||||
|
} |
||||
|
} |
||||
|
}} |
||||
|
> |
||||
|
<ProFormText |
||||
|
rules={[ |
||||
|
{ |
||||
|
required: true, |
||||
|
message: '规则名称为必填项', |
||||
|
}, |
||||
|
]} |
||||
|
width="md" |
||||
|
name="name" |
||||
|
/> |
||||
|
<ProFormTextArea width="md" name="desc" /> |
||||
|
</ModalForm> |
||||
|
<UpdateForm |
||||
|
onSubmit={async (value) => { |
||||
|
const success = await handleUpdate({ |
||||
|
...value, |
||||
|
guid: currentRow?.guid, |
||||
|
}); |
||||
|
if (success) { |
||||
|
handleUpdateModalOpen(false); |
||||
|
setCurrentRow(undefined); |
||||
|
if (actionRef.current) { |
||||
|
actionRef.current.reload(); |
||||
|
} |
||||
|
} |
||||
|
}} |
||||
|
onCancel={() => { |
||||
|
handleUpdateModalOpen(false); |
||||
|
if (!showDetail) { |
||||
|
setCurrentRow(undefined); |
||||
|
} |
||||
|
}} |
||||
|
updateModalOpen={updateModalOpen} |
||||
|
values={currentRow || {}} |
||||
|
/> |
||||
|
|
||||
|
<Drawer |
||||
|
width={600} |
||||
|
open={showDetail} |
||||
|
onClose={() => { |
||||
|
setCurrentRow(undefined); |
||||
|
setShowDetail(false); |
||||
|
}} |
||||
|
closable={false} |
||||
|
> |
||||
|
{currentRow?.name && ( |
||||
|
<ProDescriptions<API.RuleListItem> |
||||
|
column={2} |
||||
|
title={currentRow?.name} |
||||
|
request={async () => ({ |
||||
|
data: currentRow || {}, |
||||
|
})} |
||||
|
params={{ |
||||
|
id: currentRow?.name, |
||||
|
}} |
||||
|
columns={columns as ProDescriptionsItemProps<API.RuleListItem>[]} |
||||
|
/> |
||||
|
)} |
||||
|
</Drawer> |
||||
|
</PageContainer> |
||||
|
); |
||||
|
}; |
||||
|
export default TableList; |
||||
@ -0,0 +1,84 @@ |
|||||
|
import { fetchStats } from '@/services/ant-design-pro/api'; |
||||
|
import React, { useEffect, useState } from 'react'; |
||||
|
import { ProCard, StatisticCard } from '@ant-design/pro-components'; |
||||
|
import { Button, message } from 'antd'; |
||||
|
import axios from 'axios'; |
||||
|
|
||||
|
interface StatsData { |
||||
|
online: number; |
||||
|
offline: number; |
||||
|
total: number; |
||||
|
} |
||||
|
|
||||
|
const StatsPage: React.FC = () => { |
||||
|
const [stats, setStats] = useState<StatsData | null>(null); |
||||
|
const [loading, setLoading] = useState(false); |
||||
|
|
||||
|
// 通过 request 属性模拟远程请求
|
||||
|
// const fetchStats = async () => {
|
||||
|
// try {
|
||||
|
// setLoading(true);
|
||||
|
// const response = await axios.get<StatsData>('https://867t766n6.zicp.fun/computers/stats'); // 更新为实际的 API 地址
|
||||
|
// setStats(response.data);
|
||||
|
// setLoading(false);
|
||||
|
// } catch (error) {
|
||||
|
// message.error('无法获取统计数据,请稍后再试。');
|
||||
|
// setLoading(false);
|
||||
|
// }
|
||||
|
// };
|
||||
|
|
||||
|
// 获取统计数据
|
||||
|
const getStats = async () => { |
||||
|
setLoading(true); |
||||
|
try { |
||||
|
const response = await fetchStats(); |
||||
|
setStats(response); |
||||
|
} catch (error) { |
||||
|
message.error('无法获取统计数据,请稍后再试。'); |
||||
|
} finally { |
||||
|
setLoading(false); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 初始化时获取统计数据
|
||||
|
useEffect(() => { |
||||
|
getStats(); |
||||
|
}, []); |
||||
|
|
||||
|
return ( |
||||
|
<div style={{ padding: 24 }}> |
||||
|
<Button type="primary" onClick={fetchStats} style={{ marginBottom: 16 }}> |
||||
|
刷新统计数据 |
||||
|
</Button> |
||||
|
<ProCard |
||||
|
gutter={[16, 16]} |
||||
|
loading={loading} |
||||
|
title="设备状态统计" |
||||
|
headerBordered |
||||
|
> |
||||
|
<StatisticCard |
||||
|
statistic={{ |
||||
|
title: '在线设备数量', |
||||
|
value: stats ? stats.online : 0, |
||||
|
valueStyle: { color: '#3f8600' }, |
||||
|
}} |
||||
|
/> |
||||
|
<StatisticCard |
||||
|
statistic={{ |
||||
|
title: '离线设备数量', |
||||
|
value: stats ? stats.offline : 0, |
||||
|
valueStyle: { color: '#cf1322' }, |
||||
|
}} |
||||
|
/> |
||||
|
<StatisticCard |
||||
|
statistic={{ |
||||
|
title: '总设备数量', |
||||
|
value: stats ? stats.total : 0, |
||||
|
}} |
||||
|
/> |
||||
|
</ProCard> |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default StatsPage; |
||||
Loading…
Reference in new issue