HardwareManagement.tsx 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  1. import React, { useState } from 'react';
  2. import { Plus, Search, Edit2, Trash2, MoreVertical, Package, Key, Lock, Briefcase, QrCode, MapPin } from 'lucide-react';
  3. import { Button } from 'antd';
  4. import { Button as UIButton } from './ui/button';
  5. import PadLockManagement from './PadLockManagement';
  6. import HardwareLockCabinetManagement from './lockCabinet/HardwareLockCabinetManagement';
  7. interface TableRow {
  8. id: number;
  9. [key: string]: any;
  10. }
  11. interface HardwareManagementProps {
  12. subMenu: string;
  13. }
  14. export default function HardwareManagement({ subMenu }: HardwareManagementProps) {
  15. // 如果是挂锁,使用专门的挂锁管理组件
  16. // subMenu 可能是 '挂锁'、'padlock' 或 'lock'
  17. if (subMenu === '挂锁' || subMenu === 'padlock' || subMenu === 'lock') {
  18. return <PadLockManagement subMenu={subMenu} />;
  19. }
  20. const [searchTerm, setSearchTerm] = useState('');
  21. const [showAddModal, setShowAddModal] = useState(false);
  22. const [editingItem, setEditingItem] = useState<TableRow | null>(null);
  23. // 机柜数据
  24. const cabinetData: TableRow[] = [
  25. {
  26. id: 1,
  27. code: 'CAB-001',
  28. name: '1号配电柜',
  29. location: 'A区-1层',
  30. type: '配电柜',
  31. voltage: '380V',
  32. capacity: '200A',
  33. status: '运行中',
  34. lockCount: 8,
  35. manufacturer: '施耐德',
  36. installDate: '2024-01-15',
  37. lastCheck: '2025-12-01'
  38. },
  39. {
  40. id: 2,
  41. code: 'CAB-002',
  42. name: '2号配电柜',
  43. location: 'A区-2层',
  44. type: '配电柜',
  45. voltage: '380V',
  46. capacity: '150A',
  47. status: '维护中',
  48. lockCount: 6,
  49. manufacturer: 'ABB',
  50. installDate: '2024-02-20',
  51. lastCheck: '2025-11-28'
  52. },
  53. {
  54. id: 3,
  55. code: 'CAB-003',
  56. name: '变压器柜A',
  57. location: 'B区-地下室',
  58. type: '变压器柜',
  59. voltage: '10kV',
  60. capacity: '1000kVA',
  61. status: '运行中',
  62. lockCount: 12,
  63. manufacturer: '西门子',
  64. installDate: '2024-01-10',
  65. lastCheck: '2025-12-03'
  66. },
  67. {
  68. id: 4,
  69. code: 'CAB-004',
  70. name: '控制柜C1',
  71. location: 'C区-3层',
  72. type: '控制柜',
  73. voltage: '220V',
  74. capacity: '50A',
  75. status: '运行中',
  76. lockCount: 4,
  77. manufacturer: '正泰',
  78. installDate: '2024-03-05',
  79. lastCheck: '2025-11-25'
  80. },
  81. ];
  82. // 钥匙数据
  83. const keyData: TableRow[] = [
  84. {
  85. id: 1,
  86. code: 'KEY-001',
  87. name: '1号配电柜主钥匙',
  88. type: '主钥匙',
  89. cabinet: '1号配电柜',
  90. holder: '张三',
  91. department: '运维部',
  92. status: '在用',
  93. borrowDate: '2025-12-01',
  94. returnDate: '-',
  95. remark: '日常巡检使用'
  96. },
  97. {
  98. id: 2,
  99. code: 'KEY-002',
  100. name: '1号配电柜备用钥匙',
  101. type: '备用钥匙',
  102. cabinet: '1号配电柜',
  103. holder: '钥匙柜',
  104. department: '安全部',
  105. status: '库存',
  106. borrowDate: '-',
  107. returnDate: '-',
  108. remark: '应急备用'
  109. },
  110. {
  111. id: 3,
  112. code: 'KEY-003',
  113. name: '变压器柜A钥匙',
  114. type: '主钥匙',
  115. cabinet: '变压器柜A',
  116. holder: '李四',
  117. department: '技术部',
  118. status: '在用',
  119. borrowDate: '2025-12-03',
  120. returnDate: '-',
  121. remark: '设备检修'
  122. },
  123. {
  124. id: 4,
  125. code: 'KEY-004',
  126. name: '控制柜C1钥匙',
  127. type: '主钥匙',
  128. cabinet: '控制柜C1',
  129. holder: '钥匙柜',
  130. department: '安全部',
  131. status: '库存',
  132. borrowDate: '-',
  133. returnDate: '-',
  134. remark: ''
  135. },
  136. ];
  137. // 挂锁数据
  138. const padlockData: TableRow[] = [
  139. {
  140. id: 1,
  141. code: 'LOCK-001',
  142. name: '红色安全挂锁-001',
  143. type: '安全挂锁',
  144. color: '红色',
  145. keyCode: 'K001',
  146. owner: '张三',
  147. department: '运维部',
  148. status: '使用中',
  149. location: '1号配电柜',
  150. lockDate: '2025-12-01 09:00',
  151. remark: '隔离作业中'
  152. },
  153. {
  154. id: 2,
  155. code: 'LOCK-002',
  156. name: '蓝色安全挂锁-002',
  157. type: '安全挂锁',
  158. color: '蓝色',
  159. keyCode: 'K002',
  160. owner: '李四',
  161. department: '技术部',
  162. status: '使用中',
  163. location: '变压器柜A',
  164. lockDate: '2025-12-03 08:30',
  165. remark: '维修作业'
  166. },
  167. {
  168. id: 3,
  169. code: 'LOCK-003',
  170. name: '黄色安全挂锁-003',
  171. type: '安全挂锁',
  172. color: '黄色',
  173. keyCode: 'K003',
  174. owner: '王五',
  175. department: '安全部',
  176. status: '空闲',
  177. location: '工具柜',
  178. lockDate: '-',
  179. remark: ''
  180. },
  181. {
  182. id: 4,
  183. code: 'LOCK-004',
  184. name: '绿色安全挂锁-004',
  185. type: '安全挂锁',
  186. color: '绿色',
  187. keyCode: 'K004',
  188. owner: '赵六',
  189. department: '运维部',
  190. status: '空闲',
  191. location: '工具柜',
  192. lockDate: '-',
  193. remark: ''
  194. },
  195. ];
  196. // 便携式数据
  197. const portableData: TableRow[] = [
  198. {
  199. id: 1,
  200. code: 'PORT-001',
  201. name: '便携式接地线-A组',
  202. type: '接地线',
  203. model: 'JDX-10',
  204. specification: '10kV/50mm²',
  205. quantity: 3,
  206. holder: '张三',
  207. department: '运维部',
  208. status: '使用中',
  209. borrowDate: '2025-12-01',
  210. returnDate: '-',
  211. location: 'B区变压器',
  212. remark: '定期检修使用'
  213. },
  214. {
  215. id: 2,
  216. code: 'PORT-002',
  217. name: '验电器-B',
  218. type: '验电器',
  219. model: 'YDQ-10',
  220. specification: '10kV',
  221. quantity: 2,
  222. holder: '李四',
  223. department: '技术部',
  224. status: '使用中',
  225. borrowDate: '2025-12-03',
  226. returnDate: '-',
  227. location: 'B区变压器',
  228. remark: '配合接地线使用'
  229. },
  230. {
  231. id: 3,
  232. code: 'PORT-003',
  233. name: '绝缘手套-C组',
  234. type: '绝缘防护',
  235. model: 'JYT-500',
  236. specification: '500V',
  237. quantity: 5,
  238. holder: '工具库',
  239. department: '安全部',
  240. status: '库存',
  241. borrowDate: '-',
  242. returnDate: '-',
  243. location: '安全工具柜',
  244. remark: '每月检测'
  245. },
  246. {
  247. id: 4,
  248. code: 'PORT-004',
  249. name: '便携式接地线-D组',
  250. type: '接地线',
  251. model: 'JDX-35',
  252. specification: '35kV/70mm²',
  253. quantity: 3,
  254. holder: '工具库',
  255. department: '安全部',
  256. status: '库存',
  257. borrowDate: '-',
  258. returnDate: '-',
  259. location: '安全工具柜',
  260. remark: ''
  261. },
  262. ];
  263. // 机柜管理使用专用组件
  264. // subMenu 可能是 '机柜'、'cabinet' 或 'hwcabinet'
  265. if (subMenu === '机柜' || subMenu === 'cabinet' || subMenu === 'hwcabinet') {
  266. console.log('HardwareManagement: 渲染机柜管理组件,subMenu:', subMenu);
  267. return <HardwareLockCabinetManagement key="hardware-lock-cabinet-management" />;
  268. }
  269. // 根据当前子菜单获取数据和列配置
  270. const getTableConfig = () => {
  271. if (subMenu === '钥匙') {
  272. return {
  273. data: keyData,
  274. columns: [
  275. { key: 'code', label: '编号', width: '10%' },
  276. { key: 'name', label: '名称', width: '15%' },
  277. { key: 'type', label: '类型', width: '10%' },
  278. { key: 'cabinet', label: '对应机柜', width: '12%' },
  279. { key: 'holder', label: '持有人', width: '10%' },
  280. { key: 'department', label: '部门', width: '10%' },
  281. { key: 'status', label: '状态', width: '8%' },
  282. { key: 'borrowDate', label: '借出日期', width: '12%' },
  283. { key: 'remark', label: '备注', width: '13%' },
  284. ],
  285. };
  286. } else if (subMenu === '挂锁') {
  287. return {
  288. data: padlockData,
  289. columns: [
  290. { key: 'code', label: '编号', width: '10%' },
  291. { key: 'name', label: '名称', width: '15%' },
  292. { key: 'color', label: '颜色', width: '8%' },
  293. { key: 'owner', label: '所有人', width: '10%' },
  294. { key: 'department', label: '部门', width: '10%' },
  295. { key: 'status', label: '状态', width: '8%' },
  296. { key: 'location', label: '当前位置', width: '12%' },
  297. { key: 'lockDate', label: '上锁时间', width: '15%' },
  298. { key: 'remark', label: '备注', width: '12%' },
  299. ],
  300. };
  301. } else if (subMenu === '便携式') {
  302. return {
  303. data: portableData,
  304. columns: [
  305. { key: 'code', label: '编号', width: '10%' },
  306. { key: 'name', label: '名称', width: '15%' },
  307. { key: 'type', label: '类型', width: '10%' },
  308. { key: 'specification', label: '规格', width: '10%' },
  309. { key: 'quantity', label: '数量', width: '6%' },
  310. { key: 'holder', label: '持有人', width: '10%' },
  311. { key: 'status', label: '状态', width: '8%' },
  312. { key: 'location', label: '位置', width: '12%' },
  313. { key: 'borrowDate', label: '借出日期', width: '10%' },
  314. { key: 'remark', label: '备注', width: '9%' },
  315. ],
  316. };
  317. }
  318. return { data: [], columns: [] };
  319. };
  320. const { data, columns } = getTableConfig();
  321. // 过滤数据
  322. const filteredData = data.filter((item) =>
  323. Object.values(item).some((value) =>
  324. String(value).toLowerCase().includes(searchTerm.toLowerCase())
  325. )
  326. );
  327. const handleDelete = (id: number) => {
  328. if (confirm('确定要删除这条数据吗?')) {
  329. console.log('删除:', id);
  330. }
  331. };
  332. const handleEdit = (item: TableRow) => {
  333. setEditingItem(item);
  334. setShowAddModal(true);
  335. };
  336. // 获取表单字段
  337. const getFormFields = () => {
  338. if (subMenu === '机柜') {
  339. return (
  340. <div className="grid grid-cols-2 gap-4">
  341. <div>
  342. <label className="block text-sm text-gray-700 mb-2">编号 *</label>
  343. <input
  344. type="text"
  345. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  346. placeholder="请输入编号"
  347. defaultValue={editingItem?.code || ''}
  348. />
  349. </div>
  350. <div>
  351. <label className="block text-sm text-gray-700 mb-2">名称 *</label>
  352. <input
  353. type="text"
  354. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  355. placeholder="请输入名称"
  356. defaultValue={editingItem?.name || ''}
  357. />
  358. </div>
  359. <div>
  360. <label className="block text-sm text-gray-700 mb-2">位置 *</label>
  361. <input
  362. type="text"
  363. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  364. placeholder="请输入位置"
  365. defaultValue={editingItem?.location || ''}
  366. />
  367. </div>
  368. <div>
  369. <label className="block text-sm text-gray-700 mb-2">类型 *</label>
  370. <select className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm">
  371. <option value="">请选择</option>
  372. <option value="配电柜">配电柜</option>
  373. <option value="变压器柜">变压器柜</option>
  374. <option value="控制柜">控制柜</option>
  375. </select>
  376. </div>
  377. <div>
  378. <label className="block text-sm text-gray-700 mb-2">电压</label>
  379. <input
  380. type="text"
  381. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  382. placeholder="请输入电压"
  383. defaultValue={editingItem?.voltage || ''}
  384. />
  385. </div>
  386. <div>
  387. <label className="block text-sm text-gray-700 mb-2">容量</label>
  388. <input
  389. type="text"
  390. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  391. placeholder="请输入容量"
  392. defaultValue={editingItem?.capacity || ''}
  393. />
  394. </div>
  395. <div>
  396. <label className="block text-sm text-gray-700 mb-2">锁点数</label>
  397. <input
  398. type="number"
  399. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  400. placeholder="请输入锁点数"
  401. defaultValue={editingItem?.lockCount || ''}
  402. />
  403. </div>
  404. <div>
  405. <label className="block text-sm text-gray-700 mb-2">厂商</label>
  406. <input
  407. type="text"
  408. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  409. placeholder="请输入厂商"
  410. defaultValue={editingItem?.manufacturer || ''}
  411. />
  412. </div>
  413. <div className="col-span-2">
  414. <label className="block text-sm text-gray-700 mb-2">备注</label>
  415. <textarea
  416. rows={3}
  417. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm resize-none"
  418. placeholder="请输入备注"
  419. ></textarea>
  420. </div>
  421. </div>
  422. );
  423. } else if (subMenu === '钥匙') {
  424. return (
  425. <div className="grid grid-cols-2 gap-4">
  426. <div>
  427. <label className="block text-sm text-gray-700 mb-2">编号 *</label>
  428. <input
  429. type="text"
  430. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  431. placeholder="请输入编号"
  432. defaultValue={editingItem?.code || ''}
  433. />
  434. </div>
  435. <div>
  436. <label className="block text-sm text-gray-700 mb-2">名称 *</label>
  437. <input
  438. type="text"
  439. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  440. placeholder="请输入名称"
  441. defaultValue={editingItem?.name || ''}
  442. />
  443. </div>
  444. <div>
  445. <label className="block text-sm text-gray-700 mb-2">类型 *</label>
  446. <select className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm">
  447. <option value="">请选择</option>
  448. <option value="主钥匙">主钥匙</option>
  449. <option value="备用钥匙">备用钥匙</option>
  450. </select>
  451. </div>
  452. <div>
  453. <label className="block text-sm text-gray-700 mb-2">对应机柜 *</label>
  454. <select className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm">
  455. <option value="">请选择</option>
  456. <option value="1号配电柜">1号配电柜</option>
  457. <option value="2号配电柜">2号配电柜</option>
  458. <option value="变压器柜A">变压器柜A</option>
  459. </select>
  460. </div>
  461. <div>
  462. <label className="block text-sm text-gray-700 mb-2">持有人</label>
  463. <input
  464. type="text"
  465. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  466. placeholder="请输入持有人"
  467. defaultValue={editingItem?.holder || ''}
  468. />
  469. </div>
  470. <div>
  471. <label className="block text-sm text-gray-700 mb-2">部门</label>
  472. <select className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm">
  473. <option value="">请选择</option>
  474. <option value="运维部">运维部</option>
  475. <option value="技术部">技术部</option>
  476. <option value="安全部">安全部</option>
  477. </select>
  478. </div>
  479. <div className="col-span-2">
  480. <label className="block text-sm text-gray-700 mb-2">备注</label>
  481. <textarea
  482. rows={3}
  483. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm resize-none"
  484. placeholder="请输入备注"
  485. defaultValue={editingItem?.remark || ''}
  486. ></textarea>
  487. </div>
  488. </div>
  489. );
  490. } else if (subMenu === '挂锁') {
  491. return (
  492. <div className="grid grid-cols-2 gap-4">
  493. <div>
  494. <label className="block text-sm text-gray-700 mb-2">编号 *</label>
  495. <input
  496. type="text"
  497. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  498. placeholder="请输入编号"
  499. defaultValue={editingItem?.code || ''}
  500. />
  501. </div>
  502. <div>
  503. <label className="block text-sm text-gray-700 mb-2">名称 *</label>
  504. <input
  505. type="text"
  506. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  507. placeholder="请输入名称"
  508. defaultValue={editingItem?.name || ''}
  509. />
  510. </div>
  511. <div>
  512. <label className="block text-sm text-gray-700 mb-2">颜色 *</label>
  513. <select className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm">
  514. <option value="">请选择</option>
  515. <option value="红色">红色</option>
  516. <option value="蓝色">蓝色</option>
  517. <option value="黄色">黄色</option>
  518. <option value="绿色">绿色</option>
  519. </select>
  520. </div>
  521. <div>
  522. <label className="block text-sm text-gray-700 mb-2">钥匙编号</label>
  523. <input
  524. type="text"
  525. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  526. placeholder="请输入钥匙编号"
  527. defaultValue={editingItem?.keyCode || ''}
  528. />
  529. </div>
  530. <div>
  531. <label className="block text-sm text-gray-700 mb-2">所有人 *</label>
  532. <input
  533. type="text"
  534. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  535. placeholder="请输入所有人"
  536. defaultValue={editingItem?.owner || ''}
  537. />
  538. </div>
  539. <div>
  540. <label className="block text-sm text-gray-700 mb-2">部门 *</label>
  541. <select className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm">
  542. <option value="">请选择</option>
  543. <option value="运维部">运维部</option>
  544. <option value="技术部">技术部</option>
  545. <option value="安全部">安全部</option>
  546. </select>
  547. </div>
  548. <div className="col-span-2">
  549. <label className="block text-sm text-gray-700 mb-2">备注</label>
  550. <textarea
  551. rows={3}
  552. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm resize-none"
  553. placeholder="请输入备注"
  554. defaultValue={editingItem?.remark || ''}
  555. ></textarea>
  556. </div>
  557. </div>
  558. );
  559. } else if (subMenu === '便携式') {
  560. return (
  561. <div className="grid grid-cols-2 gap-4">
  562. <div>
  563. <label className="block text-sm text-gray-700 mb-2">编号 *</label>
  564. <input
  565. type="text"
  566. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  567. placeholder="请输入编号"
  568. defaultValue={editingItem?.code || ''}
  569. />
  570. </div>
  571. <div>
  572. <label className="block text-sm text-gray-700 mb-2">名称 *</label>
  573. <input
  574. type="text"
  575. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  576. placeholder="请输入名称"
  577. defaultValue={editingItem?.name || ''}
  578. />
  579. </div>
  580. <div>
  581. <label className="block text-sm text-gray-700 mb-2">类型 *</label>
  582. <select className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm">
  583. <option value="">请选择</option>
  584. <option value="接地线">接地线</option>
  585. <option value="验电器">验电器</option>
  586. <option value="绝缘防护">绝缘防护</option>
  587. </select>
  588. </div>
  589. <div>
  590. <label className="block text-sm text-gray-700 mb-2">型号</label>
  591. <input
  592. type="text"
  593. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  594. placeholder="请输入型号"
  595. defaultValue={editingItem?.model || ''}
  596. />
  597. </div>
  598. <div>
  599. <label className="block text-sm text-gray-700 mb-2">规格</label>
  600. <input
  601. type="text"
  602. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  603. placeholder="请输入规格"
  604. defaultValue={editingItem?.specification || ''}
  605. />
  606. </div>
  607. <div>
  608. <label className="block text-sm text-gray-700 mb-2">数量</label>
  609. <input
  610. type="number"
  611. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  612. placeholder="请输入数量"
  613. defaultValue={editingItem?.quantity || ''}
  614. />
  615. </div>
  616. <div className="col-span-2">
  617. <label className="block text-sm text-gray-700 mb-2">备注</label>
  618. <textarea
  619. rows={3}
  620. className="w-full px-4 py-2.5 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm resize-none"
  621. placeholder="请输入备注"
  622. defaultValue={editingItem?.remark || ''}
  623. ></textarea>
  624. </div>
  625. </div>
  626. );
  627. }
  628. return null;
  629. };
  630. return (
  631. <div className="space-y-6">
  632. {/* 表格容器 - 合并工具栏和列表 */}
  633. <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm overflow-hidden">
  634. {/* 工具栏 */}
  635. <div className="p-4 border-b border-gray-200/50">
  636. <div className="flex items-center justify-between">
  637. <div className="relative w-80">
  638. <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
  639. <input
  640. type="text"
  641. placeholder={`搜索${subMenu}...`}
  642. value={searchTerm}
  643. onChange={(e) => setSearchTerm(e.target.value)}
  644. className="w-full h-10 pl-10 pr-4 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
  645. />
  646. </div>
  647. <button
  648. onClick={() => {
  649. setEditingItem(null);
  650. setShowAddModal(true);
  651. }}
  652. className="flex items-center gap-2 px-4 py-2.5 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl hover:shadow-lg hover:shadow-blue-400/40 transition-all duration-300"
  653. >
  654. <Plus className="w-4 h-4" strokeWidth={2.5} />
  655. <span className="text-sm">新增{subMenu}</span>
  656. </button>
  657. </div>
  658. </div>
  659. {/* 表格 */}
  660. <div className="overflow-x-auto">
  661. <table className="w-full">
  662. <thead>
  663. <tr className="bg-gradient-to-r from-gray-50 to-gray-100/50 border-b border-gray-200">
  664. <th className="px-6 py-4 text-left text-xs text-gray-600 uppercase tracking-wider" style={{ width: '5%' }}>
  665. 序号
  666. </th>
  667. {columns.map((column) => (
  668. <th
  669. key={column.key}
  670. className="px-6 py-4 text-left text-xs text-gray-600 uppercase tracking-wider"
  671. style={{ width: column.width }}
  672. >
  673. {column.label}
  674. </th>
  675. ))}
  676. <th className="px-6 py-4 text-center text-xs text-gray-600 uppercase tracking-wider" style={{ width: '10%' }}>
  677. 操作
  678. </th>
  679. </tr>
  680. </thead>
  681. <tbody className="divide-y divide-gray-100">
  682. {filteredData.map((row, index) => (
  683. <tr
  684. key={row.id}
  685. className="hover:bg-blue-50/30 transition-colors"
  686. >
  687. <td className="px-6 py-4 text-sm text-gray-900">
  688. {index + 1}
  689. </td>
  690. {columns.map((column) => (
  691. <td key={column.key} className="px-6 py-4 text-sm text-gray-900">
  692. {column.key === 'status' ? (
  693. <span
  694. className={`inline-flex px-3 py-1 rounded-lg text-xs ${
  695. row[column.key] === '运行中' || row[column.key] === '在用' || row[column.key] === '使用中'
  696. ? 'bg-green-100 text-green-700'
  697. : row[column.key] === '维护中'
  698. ? 'bg-orange-100 text-orange-700'
  699. : row[column.key] === '库存' || row[column.key] === '空闲'
  700. ? 'bg-blue-100 text-blue-700'
  701. : 'bg-gray-100 text-gray-700'
  702. }`}
  703. >
  704. {row[column.key]}
  705. </span>
  706. ) : column.key === 'color' ? (
  707. <div className="flex items-center gap-2">
  708. <div
  709. className={`w-4 h-4 rounded-full border border-gray-300 ${
  710. row[column.key] === '红色' ? 'bg-red-500' :
  711. row[column.key] === '蓝色' ? 'bg-blue-500' :
  712. row[column.key] === '黄色' ? 'bg-yellow-500' :
  713. row[column.key] === '绿色' ? 'bg-green-500' :
  714. 'bg-gray-500'
  715. }`}
  716. ></div>
  717. <span>{row[column.key]}</span>
  718. </div>
  719. ) : (
  720. row[column.key]
  721. )}
  722. </td>
  723. ))}
  724. <td className="px-6 py-4">
  725. <div className="flex items-center justify-center gap-2">
  726. <UIButton
  727. variant="ghost"
  728. size="sm"
  729. onClick={() => handleEdit(row)}
  730. className="h-8 px-2"
  731. >
  732. <Edit2 className="w-4 h-4" />
  733. <span className="ml-1">编辑</span>
  734. </UIButton>
  735. <UIButton
  736. variant="ghost"
  737. size="sm"
  738. onClick={() => handleDelete(row.id)}
  739. className="h-8 px-2 text-red-600 hover:text-red-700"
  740. >
  741. <Trash2 className="w-4 h-4" />
  742. <span className="ml-1">删除</span>
  743. </UIButton>
  744. <UIButton
  745. variant="ghost"
  746. size="sm"
  747. className="h-8 px-2"
  748. >
  749. <MoreVertical className="w-4 h-4" />
  750. <span className="ml-1">更多</span>
  751. </UIButton>
  752. </div>
  753. </td>
  754. </tr>
  755. ))}
  756. </tbody>
  757. </table>
  758. </div>
  759. </div>
  760. {/* 分页 */}
  761. {filteredData.length > 0 && (
  762. <div className="bg-white rounded-lg border border-gray-200 px-6 py-4">
  763. <div className="flex items-center justify-between">
  764. <div className="text-sm text-gray-600">
  765. 共 <span className="text-blue-600 font-medium">{filteredData.length}</span> 条记录
  766. </div>
  767. <div className="flex gap-2">
  768. <Button
  769. disabled={true}
  770. >
  771. 上一页
  772. </Button>
  773. <span className="px-4 py-2 text-sm text-gray-600 flex items-center">
  774. 1 / 1
  775. </span>
  776. <Button
  777. disabled={true}
  778. >
  779. 下一页
  780. </Button>
  781. </div>
  782. </div>
  783. </div>
  784. )}
  785. {/* 新增/编辑弹窗 */}
  786. {showAddModal && (
  787. <div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50 animate-in fade-in duration-200">
  788. <div className="bg-white rounded-2xl shadow-2xl w-full max-w-3xl mx-4 max-h-[90vh] overflow-y-auto animate-in zoom-in duration-200">
  789. {/* 弹窗标题 */}
  790. <div className="px-6 py-4 border-b border-gray-200 flex items-center justify-between sticky top-0 bg-white z-10">
  791. <h3 className="text-lg text-gray-900">
  792. {editingItem ? `编辑${subMenu}` : `新增${subMenu}`}
  793. </h3>
  794. <button
  795. onClick={() => setShowAddModal(false)}
  796. className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
  797. >
  798. <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
  799. <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
  800. </svg>
  801. </button>
  802. </div>
  803. {/* 弹窗内容 */}
  804. <div className="px-6 py-6">
  805. {getFormFields()}
  806. </div>
  807. {/* 弹窗底部 */}
  808. <div className="px-6 py-4 bg-gray-50/50 border-t border-gray-200 flex justify-end gap-3 rounded-b-2xl">
  809. <button
  810. onClick={() => setShowAddModal(false)}
  811. className="px-5 py-2.5 text-sm text-gray-600 bg-white border border-gray-200 rounded-xl hover:bg-gray-50 transition-colors"
  812. >
  813. 取消
  814. </button>
  815. <button
  816. onClick={() => {
  817. console.log('保存:', editingItem);
  818. setShowAddModal(false);
  819. }}
  820. className="px-5 py-2.5 text-sm text-white bg-gradient-to-r from-blue-500 to-blue-600 rounded-xl hover:shadow-lg hover:shadow-blue-400/40 transition-all"
  821. >
  822. {editingItem ? '保存修改' : '确定'}
  823. </button>
  824. </div>
  825. </div>
  826. </div>
  827. )}
  828. </div>
  829. );
  830. }