pages_rack.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. import React, { useState } from 'react';
  2. import {
  3. Button, Table, Space,
  4. Form, Input, Select, message, Modal,
  5. Card, Row, Col, Typography,
  6. Switch, Badge, Image,
  7. Popconfirm, Tag,
  8. } from 'antd';
  9. import {
  10. useQuery
  11. } from '@tanstack/react-query';
  12. import dayjs from 'dayjs';
  13. import 'dayjs/locale/zh-cn';
  14. import type {
  15. ZichanInfo, RackServerType,
  16. RackInfo, RackServer
  17. } from '../share/monitorTypes.ts';
  18. import {
  19. ServerType, ServerTypeNameMap
  20. } from '../share/monitorTypes.ts';
  21. import { EnableStatus, EnableStatusNameMap } from '../share/types.ts'
  22. import { RackAPI, RackServerAPI , RackServerTypeAPI, ZichanAPI, getOssUrl} from './api/index.ts';
  23. const { Title } = Typography;
  24. // 机柜管理页面
  25. export const RackManagePage = () => {
  26. const [form] = Form.useForm();
  27. const [formMode, setFormMode] = useState<'create' | 'edit'>('create');
  28. const [editingId, setEditingId] = useState<number | null>(null);
  29. const [modalVisible, setModalVisible] = useState(false);
  30. const [isLoading, setIsLoading] = useState(false);
  31. const [searchParams, setSearchParams] = useState({
  32. page: 1,
  33. limit: 10,
  34. rack_name: '',
  35. rack_code: '',
  36. area: ''
  37. });
  38. // 查询机柜列表
  39. const {
  40. data: rackResult = { data: [], pagination: { total: 0, current: 1, pageSize: 10 } },
  41. isLoading: isFetching,
  42. refetch
  43. } = useQuery({
  44. queryKey: ['racks', searchParams],
  45. queryFn: () => RackAPI.getRackList(searchParams),
  46. });
  47. // 提取数据和分页信息
  48. const rackList = rackResult.data || [];
  49. const pagination = rackResult.pagination || { total: 0, current: 1, pageSize: 10 };
  50. // 处理表单提交
  51. const handleSubmit = async (values: Partial<RackInfo>) => {
  52. try {
  53. setIsLoading(true);
  54. if (formMode === 'create') {
  55. await RackAPI.createRack(values);
  56. message.success('机柜创建成功');
  57. } else {
  58. if (editingId) {
  59. await RackAPI.updateRack(editingId, values);
  60. message.success('机柜更新成功');
  61. }
  62. }
  63. setModalVisible(false);
  64. refetch();
  65. } catch (error: any) {
  66. message.error(error.response?.data?.error || '操作失败');
  67. } finally {
  68. setIsLoading(false);
  69. }
  70. };
  71. // 处理编辑
  72. const handleEdit = async (id: number) => {
  73. try {
  74. setIsLoading(true);
  75. const data = await RackAPI.getRack(id);
  76. form.setFieldsValue(data);
  77. setEditingId(id);
  78. setFormMode('edit');
  79. setModalVisible(true);
  80. } catch (error: any) {
  81. message.error(error.response?.data?.error || '获取机柜详情失败');
  82. } finally {
  83. setIsLoading(false);
  84. }
  85. };
  86. // 处理删除
  87. const handleDelete = async (id: number) => {
  88. try {
  89. await RackAPI.deleteRack(id);
  90. message.success('机柜删除成功');
  91. refetch();
  92. } catch (error: any) {
  93. message.error(error.response?.data?.error || '删除机柜失败');
  94. }
  95. };
  96. // 处理搜索
  97. const handleSearch = (values: any) => {
  98. setSearchParams({
  99. ...searchParams,
  100. page: 1, // 重置为第一页
  101. rack_name: values.rack_name,
  102. rack_code: values.rack_code,
  103. area: values.area
  104. });
  105. };
  106. // 处理页码变化
  107. const handlePageChange = (page: number, pageSize?: number) => {
  108. setSearchParams({
  109. ...searchParams,
  110. page,
  111. limit: pageSize || 10
  112. });
  113. };
  114. // 处理新增
  115. const handleAdd = () => {
  116. form.resetFields();
  117. setFormMode('create');
  118. setEditingId(null);
  119. setModalVisible(true);
  120. };
  121. // 表格列定义
  122. const columns = [
  123. {
  124. title: '机柜ID',
  125. dataIndex: 'id',
  126. key: 'id',
  127. width: 80
  128. },
  129. {
  130. title: '机柜名称',
  131. dataIndex: 'rack_name',
  132. key: 'rack_name'
  133. },
  134. {
  135. title: '机柜编号',
  136. dataIndex: 'rack_code',
  137. key: 'rack_code'
  138. },
  139. {
  140. title: '区域',
  141. dataIndex: 'area',
  142. key: 'area'
  143. },
  144. {
  145. title: '机房',
  146. dataIndex: 'room',
  147. key: 'room'
  148. },
  149. {
  150. title: '容量(U)',
  151. dataIndex: 'capacity',
  152. key: 'capacity'
  153. },
  154. {
  155. title: '状态',
  156. dataIndex: 'is_disabled',
  157. key: 'is_disabled',
  158. render: (status: EnableStatus) => (
  159. <Badge
  160. status={status === EnableStatus.ENABLED ? 'success' : 'error'}
  161. text={status === EnableStatus.ENABLED ? '正常' : '禁用'}
  162. />
  163. )
  164. },
  165. {
  166. title: '创建时间',
  167. dataIndex: 'created_at',
  168. key: 'created_at',
  169. render: (date: string) => date ? dayjs(date).format('YYYY-MM-DD HH:mm:ss') : '-'
  170. },
  171. {
  172. title: '操作',
  173. key: 'action',
  174. width: 200,
  175. render: (_: any, record: RackInfo) => (
  176. <Space>
  177. <Button size="small" type="primary" onClick={() => handleEdit(record.id)}>编辑</Button>
  178. <Button size="small" danger onClick={() =>
  179. Modal.confirm({
  180. title: '确认删除',
  181. content: `确定要删除机柜"${record.rack_name}"吗?`,
  182. onOk: () => handleDelete(record.id)
  183. })
  184. }>删除</Button>
  185. </Space>
  186. )
  187. }
  188. ];
  189. return (
  190. <div>
  191. <Title level={2}>机柜管理</Title>
  192. <Card>
  193. <Form layout="inline" onFinish={handleSearch} style={{ marginBottom: 16 }}>
  194. <Form.Item name="rack_name" label="机柜名称">
  195. <Input placeholder="请输入机柜名称" allowClear />
  196. </Form.Item>
  197. <Form.Item name="rack_code" label="机柜编号">
  198. <Input placeholder="请输入机柜编号" allowClear />
  199. </Form.Item>
  200. <Form.Item name="area" label="区域">
  201. <Input placeholder="请输入区域" allowClear />
  202. </Form.Item>
  203. <Form.Item>
  204. <Button type="primary" htmlType="submit">查询</Button>
  205. </Form.Item>
  206. <Form.Item>
  207. <Button type="primary" onClick={handleAdd}>添加机柜</Button>
  208. </Form.Item>
  209. </Form>
  210. <Table
  211. columns={columns}
  212. dataSource={rackList}
  213. rowKey="id"
  214. loading={isFetching}
  215. pagination={{
  216. current: pagination.current,
  217. pageSize: pagination.pageSize,
  218. total: pagination.total,
  219. onChange: handlePageChange,
  220. showSizeChanger: true,
  221. showTotal: (total) => `共 ${total} 条记录`
  222. }}
  223. />
  224. <Modal
  225. title={formMode === 'create' ? '添加机柜' : '编辑机柜'}
  226. open={modalVisible}
  227. onCancel={() => setModalVisible(false)}
  228. footer={null}
  229. width={700}
  230. >
  231. <Form
  232. form={form}
  233. layout="vertical"
  234. onFinish={handleSubmit}
  235. >
  236. <Row gutter={16}>
  237. <Col span={12}>
  238. <Form.Item
  239. name="rack_name"
  240. label="机柜名称"
  241. rules={[{ required: true, message: '请输入机柜名称' }]}
  242. >
  243. <Input placeholder="请输入机柜名称" />
  244. </Form.Item>
  245. </Col>
  246. <Col span={12}>
  247. <Form.Item
  248. name="rack_code"
  249. label="机柜编号"
  250. rules={[{ required: true, message: '请输入机柜编号' }]}
  251. >
  252. <Input placeholder="请输入机柜编号" />
  253. </Form.Item>
  254. </Col>
  255. </Row>
  256. <Row gutter={16}>
  257. <Col span={12}>
  258. <Form.Item
  259. name="area"
  260. label="区域"
  261. >
  262. <Input placeholder="请输入区域" />
  263. </Form.Item>
  264. </Col>
  265. <Col span={12}>
  266. <Form.Item
  267. name="room"
  268. label="机房"
  269. >
  270. <Input placeholder="请输入机房" />
  271. </Form.Item>
  272. </Col>
  273. </Row>
  274. <Row gutter={16}>
  275. <Col span={12}>
  276. <Form.Item
  277. name="capacity"
  278. label="容量(U)"
  279. initialValue={42}
  280. rules={[{ required: true, message: '请输入容量' }]}
  281. >
  282. <Input type="number" placeholder="请输入容量" />
  283. </Form.Item>
  284. </Col>
  285. <Col span={12}>
  286. <Form.Item
  287. name="is_disabled"
  288. label="状态"
  289. initialValue={EnableStatus.ENABLED}
  290. >
  291. <Select>
  292. <Select.Option value={EnableStatus.ENABLED}>正常</Select.Option>
  293. <Select.Option value={EnableStatus.DISABLED}>禁用</Select.Option>
  294. </Select>
  295. </Form.Item>
  296. </Col>
  297. </Row>
  298. <Row gutter={16}>
  299. <Col span={8}>
  300. <Form.Item
  301. name="position_x"
  302. label="X轴位置"
  303. >
  304. <Input type="number" placeholder="请输入X轴位置" />
  305. </Form.Item>
  306. </Col>
  307. <Col span={8}>
  308. <Form.Item
  309. name="position_y"
  310. label="Y轴位置"
  311. >
  312. <Input type="number" placeholder="请输入Y轴位置" />
  313. </Form.Item>
  314. </Col>
  315. <Col span={8}>
  316. <Form.Item
  317. name="position_z"
  318. label="Z轴位置"
  319. >
  320. <Input type="number" placeholder="请输入Z轴位置" />
  321. </Form.Item>
  322. </Col>
  323. </Row>
  324. <Form.Item
  325. name="remark"
  326. label="备注信息"
  327. >
  328. <Input.TextArea rows={3} placeholder="请输入备注信息" />
  329. </Form.Item>
  330. <Form.Item>
  331. <Space>
  332. <Button type="primary" htmlType="submit" loading={isLoading}>
  333. {formMode === 'create' ? '创建' : '保存'}
  334. </Button>
  335. <Button onClick={() => setModalVisible(false)}>取消</Button>
  336. </Space>
  337. </Form.Item>
  338. </Form>
  339. </Modal>
  340. </Card>
  341. </div>
  342. );
  343. };