pages_zichan.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. import React, { useState, useEffect } from 'react';
  2. import {
  3. Layout, Button, Table, Space,
  4. Form, Input, Select, message, Modal,
  5. Card, Row, Col, Typography,
  6. Popconfirm, Tag, DatePicker, Radio
  7. } from 'antd';
  8. import {
  9. useQuery,
  10. } from '@tanstack/react-query';
  11. import dayjs from 'dayjs';
  12. import 'dayjs/locale/zh-cn';
  13. import { EnableStatus } from '../share/types.ts'
  14. import type {
  15. ZichanInfo, ZichanTransLog, ZichanCategory, ZichanArea,
  16. } from '../share/monitorTypes.ts';
  17. import {
  18. DeviceCategory, DeviceStatus, AssetTransferType,
  19. AssetTransferTypeNameMap, AssetTransferTypeColorMap,
  20. DeviceStatusNameMap, DeviceStatusColorMap, DeviceCategoryNameMap
  21. } from '../share/monitorTypes.ts';
  22. import { ZichanAPI, ZichanCategoryAPI, ZichanAreaAPI, ZichanTransferAPI } from './api/index.ts';
  23. import { getEnumOptions } from './utils.ts';
  24. const { Title } = Typography;
  25. // 资产管理页面组件
  26. export const ZichanPage = () => {
  27. const [form] = Form.useForm();
  28. const [formMode, setFormMode] = useState<'create' | 'edit'>('create');
  29. const [editingId, setEditingId] = useState<number | null>(null);
  30. const [modalVisible, setModalVisible] = useState(false);
  31. const [isLoading, setIsLoading] = useState(false);
  32. const [searchParams, setSearchParams] = useState({
  33. page: 1,
  34. limit: 10,
  35. asset_name: '',
  36. device_category: undefined as DeviceCategory | undefined,
  37. device_status: undefined as DeviceStatus | undefined
  38. });
  39. // 设备分类选项
  40. const categoryOptions = getEnumOptions(DeviceCategory, DeviceCategoryNameMap);
  41. // 设备状态选项
  42. const statusOptions = getEnumOptions(DeviceStatus, DeviceStatusNameMap);
  43. // 查询资产列表
  44. const {
  45. data: zichanResult = { data: [], pagination: { total: 0, current: 1, pageSize: 10 } },
  46. isLoading: isFetching,
  47. refetch
  48. } = useQuery({
  49. queryKey: ['zichan', searchParams],
  50. queryFn: () => ZichanAPI.getZichanList(searchParams),
  51. // staleTime: 5000, // 数据5秒内不会被认为是过期的
  52. });
  53. // 提取数据和分页信息
  54. const zichanList = zichanResult.data || [];
  55. const pagination = zichanResult.pagination || { total: 0, current: 1, pageSize: 10 };
  56. // 处理表单提交
  57. const handleSubmit = async (values: Partial<ZichanInfo>) => {
  58. try {
  59. setIsLoading(true);
  60. if (formMode === 'create') {
  61. await ZichanAPI.createZichan(values);
  62. message.success('资产创建成功');
  63. } else {
  64. if (editingId) {
  65. await ZichanAPI.updateZichan(editingId, values);
  66. message.success('资产更新成功');
  67. }
  68. }
  69. setModalVisible(false);
  70. refetch();
  71. } catch (error: any) {
  72. message.error(error.response?.data?.error || '操作失败');
  73. } finally {
  74. setIsLoading(false);
  75. }
  76. };
  77. // 处理编辑
  78. const handleEdit = async (id: number) => {
  79. try {
  80. setIsLoading(true);
  81. const data = await ZichanAPI.getZichan(id);
  82. form.setFieldsValue(data);
  83. setEditingId(id);
  84. setFormMode('edit');
  85. setModalVisible(true);
  86. } catch (error: any) {
  87. message.error(error.response?.data?.error || '获取资产详情失败');
  88. } finally {
  89. setIsLoading(false);
  90. }
  91. };
  92. // 处理删除
  93. const handleDelete = async (id: number) => {
  94. try {
  95. await ZichanAPI.deleteZichan(id);
  96. message.success('资产删除成功');
  97. refetch();
  98. } catch (error: any) {
  99. message.error(error.response?.data?.error || '删除资产失败');
  100. }
  101. };
  102. // 处理搜索
  103. const handleSearch = (values: any) => {
  104. setSearchParams({
  105. ...searchParams,
  106. page: 1, // 重置为第一页
  107. asset_name: values.asset_name,
  108. device_category: values.device_category,
  109. device_status: values.device_status
  110. });
  111. };
  112. // 处理页码变化
  113. const handlePageChange = (page: number, pageSize?: number) => {
  114. setSearchParams({
  115. ...searchParams,
  116. page,
  117. limit: pageSize || 10
  118. });
  119. };
  120. // 处理新增
  121. const handleAdd = () => {
  122. form.resetFields();
  123. setFormMode('create');
  124. setEditingId(null);
  125. setModalVisible(true);
  126. };
  127. // 表格列定义
  128. const columns = [
  129. {
  130. title: '资产ID',
  131. dataIndex: 'id',
  132. key: 'id',
  133. width: 80
  134. },
  135. {
  136. title: '资产名称',
  137. dataIndex: 'asset_name',
  138. key: 'asset_name'
  139. },
  140. {
  141. title: '设备分类',
  142. dataIndex: 'device_category',
  143. key: 'device_category',
  144. render: (category: DeviceCategory) => {
  145. return DeviceCategoryNameMap[category] || '未知分类';
  146. }
  147. },
  148. {
  149. title: '品牌',
  150. dataIndex: 'brand',
  151. key: 'brand'
  152. },
  153. {
  154. title: '使用地址',
  155. dataIndex: 'use_address',
  156. key: 'use_address'
  157. },
  158. {
  159. title: '运行情况',
  160. dataIndex: 'operation_status',
  161. key: 'operation_status'
  162. },
  163. {
  164. title: '设备状态',
  165. dataIndex: 'device_status',
  166. key: 'device_status',
  167. render: (status: DeviceStatus) => {
  168. return <Tag color={DeviceStatusColorMap[status] || 'default'}>
  169. {DeviceStatusNameMap[status] || '未知状态'}
  170. </Tag>;
  171. }
  172. },
  173. {
  174. title: 'IP地址',
  175. dataIndex: 'ip_address',
  176. key: 'ip_address'
  177. },
  178. {
  179. title: '创建时间',
  180. dataIndex: 'created_at',
  181. key: 'created_at',
  182. render: (date: string) => date ? dayjs(date).format('YYYY-MM-DD HH:mm:ss') : '-'
  183. },
  184. {
  185. title: '操作',
  186. key: 'action',
  187. width: 200,
  188. render: (_: any, record: ZichanInfo) => (
  189. <Space>
  190. <Button size="small" type="primary" onClick={() => handleEdit(record.id)}>编辑</Button>
  191. <Button size="small" danger onClick={() =>
  192. Modal.confirm({
  193. title: '确认删除',
  194. content: `确定要删除资产"${record.asset_name}"吗?`,
  195. onOk: () => handleDelete(record.id)
  196. })
  197. }>删除</Button>
  198. </Space>
  199. )
  200. }
  201. ];
  202. return (
  203. <div>
  204. <Title level={2}>资产管理</Title>
  205. <Card>
  206. <Form layout="inline" onFinish={handleSearch} style={{ marginBottom: 16 }}>
  207. <Form.Item name="asset_name" label="资产名称">
  208. <Input placeholder="请输入资产名称" allowClear />
  209. </Form.Item>
  210. <Form.Item name="device_category" label="设备分类">
  211. <Select
  212. placeholder="请选择设备分类"
  213. style={{ width: 140 }}
  214. allowClear
  215. options={categoryOptions}
  216. />
  217. </Form.Item>
  218. <Form.Item name="device_status" label="设备状态">
  219. <Select
  220. placeholder="请选择设备状态"
  221. style={{ width: 140 }}
  222. allowClear
  223. options={statusOptions}
  224. />
  225. </Form.Item>
  226. <Form.Item>
  227. <Button type="primary" htmlType="submit">查询</Button>
  228. </Form.Item>
  229. <Form.Item>
  230. <Button type="primary" onClick={handleAdd}>添加资产</Button>
  231. </Form.Item>
  232. </Form>
  233. <Table
  234. columns={columns}
  235. dataSource={zichanList}
  236. rowKey="id"
  237. loading={isFetching}
  238. pagination={{
  239. current: pagination.current,
  240. pageSize: pagination.pageSize,
  241. total: pagination.total,
  242. onChange: handlePageChange,
  243. showSizeChanger: true,
  244. showTotal: (total) => `共 ${total} 条记录`
  245. }}
  246. />
  247. <Modal
  248. title={formMode === 'create' ? '添加资产' : '编辑资产'}
  249. open={modalVisible}
  250. onCancel={() => setModalVisible(false)}
  251. footer={null}
  252. width={800}
  253. >
  254. <Form
  255. form={form}
  256. layout="vertical"
  257. onFinish={handleSubmit}
  258. >
  259. <Row gutter={16}>
  260. <Col span={12}>
  261. <Form.Item
  262. name="asset_name"
  263. label="资产名称"
  264. rules={[{ required: true, message: '请输入资产名称' }]}
  265. >
  266. <Input placeholder="请输入资产名称" />
  267. </Form.Item>
  268. </Col>
  269. <Col span={12}>
  270. <Form.Item
  271. name="device_category"
  272. label="设备分类"
  273. rules={[{ required: true, message: '请选择设备分类' }]}
  274. >
  275. <Select
  276. placeholder="请选择设备分类"
  277. options={categoryOptions}
  278. />
  279. </Form.Item>
  280. </Col>
  281. </Row>
  282. <Row gutter={16}>
  283. <Col span={12}>
  284. <Form.Item
  285. name="brand"
  286. label="品牌"
  287. >
  288. <Input placeholder="请输入品牌" />
  289. </Form.Item>
  290. </Col>
  291. <Col span={12}>
  292. <Form.Item
  293. name="supplier"
  294. label="供应商"
  295. >
  296. <Input placeholder="请输入供应商" />
  297. </Form.Item>
  298. </Col>
  299. </Row>
  300. <Row gutter={16}>
  301. <Col span={12}>
  302. <Form.Item
  303. name="use_address"
  304. label="使用地址"
  305. >
  306. <Input placeholder="请输入使用地址" />
  307. </Form.Item>
  308. </Col>
  309. <Col span={12}>
  310. <Form.Item
  311. name="device_status"
  312. label="设备状态"
  313. >
  314. <Select
  315. placeholder="请选择设备状态"
  316. options={statusOptions}
  317. />
  318. </Form.Item>
  319. </Col>
  320. </Row>
  321. <Row gutter={16}>
  322. <Col span={12}>
  323. <Form.Item
  324. name="ip_address"
  325. label="IP地址"
  326. >
  327. <Input placeholder="请输入IP地址" />
  328. </Form.Item>
  329. </Col>
  330. <Col span={12}>
  331. <Form.Item
  332. name="operation_status"
  333. label="运行情况"
  334. >
  335. <Input placeholder="请输入运行情况" />
  336. </Form.Item>
  337. </Col>
  338. </Row>
  339. <Row gutter={16}>
  340. <Col span={8}>
  341. <Form.Item
  342. name="cpu"
  343. label="CPU信息"
  344. >
  345. <Input placeholder="请输入CPU信息" />
  346. </Form.Item>
  347. </Col>
  348. <Col span={8}>
  349. <Form.Item
  350. name="memory"
  351. label="内存信息"
  352. >
  353. <Input placeholder="请输入内存信息" />
  354. </Form.Item>
  355. </Col>
  356. <Col span={8}>
  357. <Form.Item
  358. name="disk"
  359. label="硬盘信息"
  360. >
  361. <Input placeholder="请输入硬盘信息" />
  362. </Form.Item>
  363. </Col>
  364. </Row>
  365. <Form.Item>
  366. <Space>
  367. <Button type="primary" htmlType="submit" loading={isLoading}>
  368. {formMode === 'create' ? '创建' : '保存'}
  369. </Button>
  370. <Button onClick={() => setModalVisible(false)}>取消</Button>
  371. </Space>
  372. </Form.Item>
  373. </Form>
  374. </Modal>
  375. </Card>
  376. </div>
  377. );
  378. };