Device.razor 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192
  1. @page "/siteConfiguration/device"
  2. @attribute [ReuseTabsPage(Title = "设备管理")]
  3. <div class="equipment-management-container">
  4. <!-- 筛选条件 -->
  5. <div class="ant-card filter-section">
  6. <div class="ant-card-body">
  7. <div class="ant-form ant-form-horizontal">
  8. <div class="ant-row ant-row-top" style="row-gap: 16px;">
  9. <!-- 站点BUID -->
  10. <div class="ant-col ant-col-6">
  11. <div class="ant-form-item">
  12. <label class="ant-form-item-label">
  13. <span>车牌号</span>
  14. </label>
  15. <div class="ant-form-item-control">
  16. <div class="ant-form-item-control-input">
  17. <div class="ant-form-item-control-input-content">
  18. <input type="text"
  19. class="ant-input"
  20. placeholder="车牌号"
  21. @bind="License"
  22. @bind:event="oninput" />
  23. </div>
  24. </div>
  25. </div>
  26. </div>
  27. </div>
  28. <!-- 站点名称 -->
  29. <div class="ant-col ant-col-6">
  30. <div class="ant-form-item">
  31. <label class="ant-form-item-label">
  32. <span>设备编号</span>
  33. </label>
  34. <div class="ant-form-item-control">
  35. <div class="ant-form-item-control-input">
  36. <div class="ant-form-item-control-input-content">
  37. <input type="text"
  38. class="ant-input"
  39. placeholder="设备编号"
  40. @bind="deviceNo"
  41. @bind:event="oninput" />
  42. </div>
  43. </div>
  44. </div>
  45. </div>
  46. </div>
  47. <!-- 操作按钮 -->
  48. <div class="ant-col ant-col-12">
  49. <div style="padding-top: 8px; text-align: right;">
  50. <span class="ant-space" style="gap: 8px;">
  51. <button type="button" class="ant-btn ant-btn-primary" @onclick="OnQuery">
  52. <span role="img" class="anticon anticon-search"></span>
  53. <span>查询</span>
  54. </button>
  55. <button type="button" class="ant-btn ant-btn-text" @onclick="OnNewDevice">
  56. <span role="img" class="anticon anticon-plus"></span>
  57. <span>新建设备</span>
  58. </button>
  59. </span>
  60. </div>
  61. </div>
  62. </div>
  63. </div>
  64. </div>
  65. </div>
  66. <!-- 设备列表表格 -->
  67. <div class="ant-card table-section">
  68. <div class="ant-card-body">
  69. <div class="ant-spin-nested-loading">
  70. <div class="ant-spin-container">
  71. <div class="ant-table-wrapper">
  72. <div class="ant-table ant-table-bordered ant-table-middle">
  73. <div class="ant-table-container">
  74. <!-- 统一表头和数据表格的列宽 -->
  75. <div class="ant-table-header">
  76. <table class="table-header-fixed">
  77. <colgroup>
  78. <col style="width: 120px;" />
  79. <col style="width: 120px;" />
  80. <col style="width: 100px;" />
  81. <col style="width: 100px;" />
  82. <col style="width: 140px;" />
  83. <col style="width: 100px;" />
  84. <col style="width: 100px;" />
  85. <col style="width: 100px;" />
  86. <col style="width: 100px;" />
  87. <col style="width: 150px;" />
  88. <col style="width: 180px;" />
  89. </colgroup>
  90. <thead class="ant-table-thead">
  91. <tr>
  92. <th class="ant-table-cell">设备编号</th>
  93. <th class="ant-table-cell">车牌号</th>
  94. <th class="ant-table-cell">油品名称</th>
  95. <th class="ant-table-cell">罐容量</th>
  96. <th class="ant-table-cell">当前容量</th>
  97. <th class="ant-table-cell">泵码</th>
  98. <th class="ant-table-cell">油高</th>
  99. <th class="ant-table-cell">水高</th>
  100. <th class="ant-table-cell">温度</th>
  101. <th class="ant-table-cell">最后更新时间</th>
  102. <th class="ant-table-cell">操作</th>
  103. </tr>
  104. </thead>
  105. </table>
  106. </div>
  107. <!-- 表格内容 -->
  108. <div class="ant-table-body">
  109. <table class="table-body-fixed">
  110. <colgroup>
  111. <col style="width: 120px;" />
  112. <col style="width: 120px;" />
  113. <col style="width: 100px;" />
  114. <col style="width: 100px;" />
  115. <col style="width: 140px;" />
  116. <col style="width: 100px;" />
  117. <col style="width: 100px;" />
  118. <col style="width: 100px;" />
  119. <col style="width: 100px;" />
  120. <col style="width: 150px;" />
  121. <col style="width: 180px;" />
  122. </colgroup>
  123. <tbody class="ant-table-tbody">
  124. @if (_loading)
  125. {
  126. <tr>
  127. <td colspan="7" class="ant-table-cell" style="text-align: center; padding: 40px;">
  128. <div class="ant-spin ant-spin-spinning">
  129. <span class="ant-spin-dot ant-spin-dot-spin">
  130. <i class="ant-spin-dot-item"></i>
  131. <i class="ant-spin-dot-item"></i>
  132. <i class="ant-spin-dot-item"></i>
  133. <i class="ant-spin-dot-item"></i>
  134. </span>
  135. </div>
  136. </td>
  137. </tr>
  138. }
  139. else if (_deviceList.Count == 0)
  140. {
  141. <tr>
  142. <td colspan="6" class="ant-table-cell" style="text-align: center; padding: 40px; color: #999;">
  143. 暂无数据
  144. </td>
  145. </tr>
  146. }
  147. else
  148. {
  149. @foreach (var device in _deviceList)
  150. {
  151. <tr class="ant-table-row">
  152. <td class="ant-table-cell">
  153. <span style="font-weight: 500;">@device.DeviceId</span>
  154. </td>
  155. <td class="ant-table-cell">@device.LicencePlate</td>
  156. <td class="ant-table-cell">@device.OilProductName</td>
  157. <td class="ant-table-cell">@FormatNumber(device.TankCapacity)</td>
  158. <td class="ant-table-cell">
  159. <div class="capacity-cell">
  160. <span>@FormatNumber(device.CurrentCapacity)</span>
  161. <div class="capacity-progress">
  162. <div class="progress-bar" style="width: @(GetCapacityPercent(device))%; background: @GetProgressColor(GetCapacityPercent(device));"></div>
  163. </div>
  164. </div>
  165. </td>
  166. <td class="ant-table-cell"></td>
  167. <td class="ant-table-cell"></td>
  168. <td class="ant-table-cell"></td>
  169. <td class="ant-table-cell"></td>
  170. <td class="ant-table-cell">@FormatDateTime(device.LastUpdatedTime)</td>
  171. <td class="ant-table-cell">
  172. <div class="action-buttons">
  173. <button type="button" class="ant-btn ant-btn-link ant-btn-sm" @onclick="@(() => OnEditDevice(device))">
  174. <span role="img" class="anticon anticon-edit"></span>
  175. <span>编辑</span>
  176. </button>
  177. <button type="button" class="ant-btn ant-btn-link ant-btn-sm ant-btn-dangerous" @onclick="@(() => OnDeleteDevice(device))">
  178. <span role="img" class="anticon anticon-delete"></span>
  179. <span>删除</span>
  180. </button>
  181. <button type="button" class="ant-btn ant-btn-link ant-btn-sm ant-btn-dangerous" @onclick="@(() => OnDeleteDevice(device))">
  182. <span role="img" class="anticon anticon-delete"></span>
  183. <span>下发单价</span>
  184. </button>
  185. </div>
  186. </td>
  187. </tr>
  188. }
  189. }
  190. </tbody>
  191. </table>
  192. </div>
  193. </div>
  194. </div>
  195. </div>
  196. <!-- 分页 -->
  197. @if (_totalCount > 0)
  198. {
  199. <div style="margin-top: 16px; text-align: right;">
  200. <div class="ant-pagination ant-pagination-total-text">
  201. 共 @_totalCount 条记录
  202. </div>
  203. <div class="ant-pagination ant-pagination-simple">
  204. <div class="ant-pagination-prev">
  205. <button type="button" class="ant-pagination-item-link" @onclick="@(() => OnPageChange(_currentPage - 1))" disabled="@(_currentPage <= 1)">
  206. <span role="img" class="anticon anticon-left"></span>
  207. </button>
  208. </div>
  209. <div class="ant-pagination-simple-pager">
  210. <input type="text"
  211. class="ant-pagination-simple-pager-input"
  212. value="@_currentPage"
  213. @onchange="@((ChangeEventArgs e) => OnPageInputChange(e))" />
  214. <span class="ant-pagination-slash">/</span>
  215. <span class="ant-pagination-simple-pager-text">@GetTotalPages()</span>
  216. </div>
  217. <div class="ant-pagination-next">
  218. <button type="button" class="ant-pagination-item-link" @onclick="@(() => OnPageChange(_currentPage + 1))" disabled="@(_currentPage >= GetTotalPages())">
  219. <span role="img" class="anticon anticon-right"></span>
  220. </button>
  221. </div>
  222. <div class="ant-pagination-options" style="margin-left: 8px;">
  223. <select class="ant-select ant-select-sm" @onchange="@((ChangeEventArgs e) => OnPageSizeChange(e))">
  224. <option value="10">10 条/页</option>
  225. <option value="20" selected>20 条/页</option>
  226. <option value="50">50 条/页</option>
  227. <option value="100">100 条/页</option>
  228. </select>
  229. </div>
  230. </div>
  231. </div>
  232. }
  233. </div>
  234. </div>
  235. </div>
  236. </div>
  237. <!-- 新建设备对话框 - 简化版 -->
  238. @if (_showNewDeviceDialog)
  239. {
  240. <div class="ant-modal-root">
  241. <div class="ant-modal-mask" style="z-index: 1000;" @onclick="@(() => _showNewDeviceDialog = false)"></div>
  242. <div class="ant-modal-wrap ant-modal-centered" style="z-index: 1000;">
  243. <div class="ant-modal modal-simple-size">
  244. <div class="ant-modal-content">
  245. <div class="ant-modal-header">
  246. <div class="ant-modal-title">新建设备</div>
  247. <button type="button" class="ant-modal-close" @onclick="@(() => _showNewDeviceDialog = false)">
  248. <span role="img" class="anticon anticon-close"></span>
  249. </button>
  250. </div>
  251. <div class="ant-modal-body">
  252. <!-- 简化版编辑设备表单 -->
  253. <div class="ant-form ant-form-vertical">
  254. <!-- 设备编号 -->
  255. <div class="ant-form-item">
  256. <label class="ant-form-item-label">
  257. <span class="ant-form-item-required">设备编号</span>
  258. </label>
  259. <div class="ant-form-item-control">
  260. <div class="ant-form-item-control-input">
  261. <div class="ant-form-item-control-input-content">
  262. <input type="text"
  263. class="ant-input"
  264. placeholder="请输入设备编号"
  265. @bind="_deviceModel.DeviceId"
  266. @bind:event="oninput" />
  267. </div>
  268. </div>
  269. <div class="ant-form-item-explain">设备的唯一标识符</div>
  270. </div>
  271. </div>
  272. <!-- 油品名称 -->
  273. <div class="ant-form-item">
  274. <label class="ant-form-item-label">
  275. <span class="ant-form-item-required">油品名称</span>
  276. </label>
  277. <div class="ant-form-item-control">
  278. <div class="ant-form-item-control-input">
  279. <div class="ant-form-item-control-input-content">
  280. <select class="ant-select" @onchange="@((ChangeEventArgs e) => OnOilProductChange(e))">
  281. <option value="">请选择油品</option>
  282. @foreach (var option in _oilProductOptions)
  283. {
  284. <option value="@option.Value" selected="@(_deviceModel.OilProductName == option.Value)">@option.Label</option>
  285. }
  286. </select>
  287. </div>
  288. </div>
  289. <div class="ant-form-item-explain">选择设备监控的油品类型</div>
  290. </div>
  291. </div>
  292. <!-- 罐容量 -->
  293. <div class="ant-form-item">
  294. <label class="ant-form-item-label">
  295. <span class="ant-form-item-required">罐容量(L)</span>
  296. </label>
  297. <div class="ant-form-item-control">
  298. <div class="ant-form-item-control-input">
  299. <div class="ant-form-item-control-input-content">
  300. <div class="ant-input-number">
  301. <input type="number"
  302. class="ant-input-number-input"
  303. placeholder="请输入罐容量"
  304. @bind="_deviceModel.TankCapacity"
  305. @bind:event="oninput"
  306. min="0"
  307. step="100" />
  308. <div class="ant-input-number-suffix">L</div>
  309. </div>
  310. </div>
  311. </div>
  312. <div class="ant-form-item-explain">油罐的最大容量</div>
  313. </div>
  314. </div>
  315. <!-- 车牌号 -->
  316. <div class="ant-form-item">
  317. <label class="ant-form-item-label">
  318. <span class="ant-form-item-required">车牌号</span>
  319. </label>
  320. <div class="ant-form-item-control">
  321. <div class="ant-form-item-control-input">
  322. <div class="ant-form-item-control-input-content">
  323. <input type="text"
  324. class="ant-input"
  325. placeholder="请输入车牌号"
  326. @bind="_deviceModel.LicencePlate" />
  327. </div>
  328. </div>
  329. <div class="ant-form-item-explain">设备关联的车牌号码</div>
  330. </div>
  331. </div>
  332. </div>
  333. </div>
  334. <div class="ant-modal-footer">
  335. <button type="button" class="ant-btn ant-btn-default" @onclick="@(() => _showNewDeviceDialog = false)">
  336. <span>取消</span>
  337. </button>
  338. <button type="button" class="ant-btn ant-btn-primary" @onclick="OnSaveDevice">
  339. <span>保存设备</span>
  340. </button>
  341. </div>
  342. </div>
  343. </div>
  344. </div>
  345. </div>
  346. }
  347. </div>
  348. <style>
  349. /* 容器样式 */
  350. .equipment-management-container {
  351. padding: 16px;
  352. background-color: #f5f7fa;
  353. min-height: 100vh;
  354. box-sizing: border-box;
  355. }
  356. /* Ant Design样式模拟 */
  357. .ant-card {
  358. background: #fff;
  359. border-radius: 8px;
  360. box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02), 0 2px 4px 0 rgba(0, 0, 0, 0.02);
  361. transition: box-shadow 0.3s ease;
  362. }
  363. .ant-card:hover {
  364. box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
  365. }
  366. .filter-section {
  367. margin-bottom: 16px;
  368. }
  369. .table-section {
  370. margin-top: 16px;
  371. }
  372. .ant-card-body {
  373. padding: 20px;
  374. }
  375. /* 表格样式优化 */
  376. .ant-table-container {
  377. border: 1px solid #f0f0f0;
  378. border-radius: 8px;
  379. overflow: hidden;
  380. }
  381. .table-header-fixed,
  382. .table-body-fixed {
  383. table-layout: fixed;
  384. width: 100%;
  385. border-collapse: collapse;
  386. }
  387. .ant-table-header table,
  388. .ant-table-body table {
  389. width: 100%;
  390. }
  391. .ant-table-thead > tr > th {
  392. color: rgba(0, 0, 0, 0.85);
  393. font-weight: 600;
  394. text-align: left;
  395. background: #fafafa;
  396. border-bottom: 1px solid #f0f0f0;
  397. transition: background 0.3s ease;
  398. padding: 12px 8px;
  399. white-space: nowrap;
  400. overflow: hidden;
  401. text-overflow: ellipsis;
  402. }
  403. .ant-table-tbody > tr > td {
  404. border-bottom: 1px solid #f0f0f0;
  405. padding: 12px 8px;
  406. transition: all 0.3s ease;
  407. white-space: nowrap;
  408. overflow: hidden;
  409. text-overflow: ellipsis;
  410. }
  411. /* 容量单元格样式 */
  412. .capacity-cell {
  413. display: flex;
  414. align-items: center;
  415. gap: 8px;
  416. width: 100%;
  417. }
  418. .capacity-progress {
  419. flex-shrink: 0;
  420. width: 60px;
  421. background: #f5f5f5;
  422. border-radius: 100px;
  423. height: 8px;
  424. overflow: hidden;
  425. }
  426. .progress-bar {
  427. height: 100%;
  428. border-radius: 100px;
  429. transition: width 0.3s ease, background 0.3s ease;
  430. }
  431. /* 操作按钮样式 */
  432. .action-buttons {
  433. display: flex;
  434. gap: 8px;
  435. flex-wrap: nowrap;
  436. }
  437. .ant-table-tbody > tr:hover {
  438. background: #fafafa;
  439. }
  440. .ant-table-tbody > tr:hover > td {
  441. background: #fafafa;
  442. }
  443. /* 加载样式 */
  444. .ant-spin-nested-loading {
  445. position: relative;
  446. }
  447. .ant-spin-container {
  448. position: relative;
  449. transition: opacity 0.3s;
  450. }
  451. .ant-spin {
  452. position: absolute;
  453. display: none;
  454. color: #1890ff;
  455. text-align: center;
  456. vertical-align: middle;
  457. opacity: 0;
  458. transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
  459. }
  460. .ant-spin-spinning {
  461. position: static;
  462. display: inline-block;
  463. opacity: 1;
  464. }
  465. .ant-spin-dot {
  466. position: relative;
  467. display: inline-block;
  468. font-size: 20px;
  469. width: 1em;
  470. height: 1em;
  471. }
  472. .ant-spin-dot-item {
  473. position: absolute;
  474. display: block;
  475. width: 9px;
  476. height: 9px;
  477. background-color: #1890ff;
  478. border-radius: 100%;
  479. transform: scale(0.75);
  480. transform-origin: 50% 50%;
  481. opacity: 0.3;
  482. animation: antSpinMove 1s infinite linear alternate;
  483. }
  484. .ant-spin-dot-item:nth-child(1) {
  485. top: 0;
  486. left: 0;
  487. }
  488. .ant-spin-dot-item:nth-child(2) {
  489. top: 0;
  490. right: 0;
  491. animation-delay: 0.4s;
  492. }
  493. .ant-spin-dot-item:nth-child(3) {
  494. right: 0;
  495. bottom: 0;
  496. animation-delay: 0.8s;
  497. }
  498. .ant-spin-dot-item:nth-child(4) {
  499. bottom: 0;
  500. left: 0;
  501. animation-delay: 1.2s;
  502. }
  503. @@keyframes antSpinMove {
  504. to {
  505. opacity: 1;
  506. }
  507. }
  508. /* 分页样式 */
  509. .ant-pagination {
  510. box-sizing: border-box;
  511. margin: 0;
  512. padding: 0;
  513. color: rgba(0, 0, 0, 0.85);
  514. font-size: 14px;
  515. line-height: 1.5715;
  516. list-style: none;
  517. }
  518. .ant-pagination-total-text {
  519. display: inline-block;
  520. height: 32px;
  521. margin-right: 8px;
  522. line-height: 30px;
  523. vertical-align: middle;
  524. }
  525. .ant-pagination-simple {
  526. display: inline-block;
  527. vertical-align: middle;
  528. }
  529. .ant-pagination-prev,
  530. .ant-pagination-next {
  531. display: inline-block;
  532. vertical-align: middle;
  533. }
  534. .ant-pagination-item-link {
  535. display: block;
  536. width: 36px;
  537. height: 36px;
  538. padding: 0;
  539. color: rgba(0, 0, 0, 0.85);
  540. text-align: center;
  541. background-color: #fff;
  542. border: 1px solid #d9d9d9;
  543. border-radius: 6px;
  544. outline: none;
  545. cursor: pointer;
  546. user-select: none;
  547. transition: all 0.3s;
  548. }
  549. .ant-pagination-item-link:hover {
  550. color: #1677ff;
  551. border-color: #1677ff;
  552. transform: translateY(-1px);
  553. }
  554. .ant-pagination-item-link:disabled {
  555. color: rgba(0, 0, 0, 0.25);
  556. background-color: #f5f5f5;
  557. border-color: #d9d9d9;
  558. cursor: not-allowed;
  559. }
  560. .ant-pagination-simple-pager {
  561. display: inline-block;
  562. height: 36px;
  563. margin: 0 12px;
  564. vertical-align: middle;
  565. }
  566. .ant-pagination-simple-pager-input {
  567. width: 60px;
  568. height: 36px;
  569. margin-right: 8px;
  570. text-align: center;
  571. background-color: #fff;
  572. border: 1px solid #d9d9d9;
  573. border-radius: 6px;
  574. outline: none;
  575. transition: all 0.3s;
  576. }
  577. .ant-pagination-simple-pager-input:focus {
  578. border-color: #1677ff;
  579. box-shadow: 0 0 0 2px rgba(5, 145, 255, 0.1);
  580. }
  581. .ant-pagination-slash {
  582. margin: 0 8px;
  583. }
  584. .ant-pagination-options {
  585. display: inline-block;
  586. vertical-align: middle;
  587. margin-left: 12px;
  588. }
  589. /* ====================== 模态框样式优化 ====================== */
  590. .ant-modal-root {
  591. position: fixed;
  592. top: 0;
  593. right: 0;
  594. bottom: 0;
  595. left: 0;
  596. z-index: 1000;
  597. overflow: auto;
  598. outline: 0;
  599. }
  600. .ant-modal-mask {
  601. position: fixed;
  602. top: 0;
  603. right: 0;
  604. bottom: 0;
  605. left: 0;
  606. z-index: 1000;
  607. height: 100%;
  608. background-color: rgba(0, 0, 0, 0.45);
  609. backdrop-filter: blur(2px);
  610. animation: fadeIn 0.2s ease-out;
  611. }
  612. @@keyframes fadeIn {
  613. from {
  614. opacity: 0;
  615. }
  616. to {
  617. opacity: 1;
  618. }
  619. }
  620. .ant-modal-wrap {
  621. position: fixed;
  622. top: 0;
  623. right: 0;
  624. bottom: 0;
  625. left: 0;
  626. overflow: auto;
  627. outline: 0;
  628. display: flex;
  629. align-items: center;
  630. justify-content: center;
  631. animation: modalSlideIn 0.3s ease-out;
  632. }
  633. @@keyframes modalSlideIn {
  634. from {
  635. opacity: 0;
  636. transform: translateY(-30px);
  637. }
  638. to {
  639. opacity: 1;
  640. transform: translateY(0);
  641. }
  642. }
  643. .ant-modal-centered {
  644. display: flex;
  645. align-items: center;
  646. justify-content: center;
  647. }
  648. .ant-modal {
  649. box-sizing: border-box;
  650. margin: 0;
  651. padding: 0;
  652. color: rgba(0, 0, 0, 0.85);
  653. font-size: 14px;
  654. line-height: 1.5715;
  655. list-style: none;
  656. position: relative;
  657. width: auto;
  658. max-width: calc(100vw - 32px);
  659. }
  660. .modal-simple-size {
  661. width: 500px;
  662. max-width: 90vw;
  663. }
  664. .ant-modal-content {
  665. position: relative;
  666. background-color: #fff;
  667. background-clip: padding-box;
  668. border: 0;
  669. border-radius: 12px;
  670. box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
  671. pointer-events: auto;
  672. overflow: hidden;
  673. }
  674. .ant-modal-header {
  675. padding: 20px 24px;
  676. color: rgba(0, 0, 0, 0.85);
  677. background: #fff;
  678. border-bottom: 1px solid #f0f0f0;
  679. border-radius: 12px 12px 0 0;
  680. position: relative;
  681. }
  682. .ant-modal-title {
  683. margin: 0;
  684. color: rgba(0, 0, 0, 0.85);
  685. font-weight: 600;
  686. font-size: 18px;
  687. line-height: 1.5;
  688. }
  689. .ant-modal-close {
  690. position: absolute;
  691. top: 20px;
  692. right: 20px;
  693. z-index: 10;
  694. padding: 0;
  695. color: rgba(0, 0, 0, 0.45);
  696. font-weight: 600;
  697. line-height: 1;
  698. text-decoration: none;
  699. background: transparent;
  700. border: 0;
  701. outline: 0;
  702. cursor: pointer;
  703. transition: all 0.3s;
  704. width: 32px;
  705. height: 32px;
  706. border-radius: 50%;
  707. display: flex;
  708. align-items: center;
  709. justify-content: center;
  710. }
  711. .ant-modal-close:hover {
  712. color: rgba(0, 0, 0, 0.75);
  713. background-color: rgba(0, 0, 0, 0.06);
  714. }
  715. .ant-modal-body {
  716. padding: 24px;
  717. font-size: 14px;
  718. line-height: 1.5715;
  719. word-wrap: break-word;
  720. }
  721. .ant-modal-footer {
  722. padding: 16px 24px;
  723. text-align: right;
  724. background: transparent;
  725. border-top: 1px solid #f0f0f0;
  726. border-radius: 0 0 12px 12px;
  727. display: flex;
  728. align-items: center;
  729. justify-content: flex-end;
  730. gap: 12px;
  731. }
  732. /* 表单样式 */
  733. .ant-form {
  734. box-sizing: border-box;
  735. margin: 0;
  736. padding: 0;
  737. font-size: 14px;
  738. font-variant: tabular-nums;
  739. line-height: 1.5715;
  740. list-style: none;
  741. }
  742. .ant-form-vertical .ant-form-item {
  743. margin-bottom: 24px;
  744. }
  745. .ant-form-item {
  746. margin-bottom: 0;
  747. transition: all 0.3s ease;
  748. }
  749. .ant-form-item:hover {
  750. transform: translateY(-1px);
  751. }
  752. .ant-form-item-label {
  753. line-height: 1.5;
  754. margin-bottom: 8px;
  755. }
  756. .ant-form-item-label > label {
  757. color: rgba(0, 0, 0, 0.85);
  758. font-size: 14px;
  759. font-weight: 500;
  760. }
  761. .ant-form-item-required::before {
  762. display: inline-block;
  763. margin-right: 4px;
  764. color: #ff4d4f;
  765. font-size: 14px;
  766. line-height: 1;
  767. content: '*';
  768. }
  769. .ant-form-item-control-input {
  770. min-height: 36px;
  771. }
  772. .ant-form-item-explain {
  773. color: rgba(0, 0, 0, 0.45);
  774. font-size: 12px;
  775. line-height: 1.5;
  776. margin-top: 4px;
  777. min-height: 18px;
  778. }
  779. .ant-input {
  780. box-sizing: border-box;
  781. margin: 0;
  782. font-variant: tabular-nums;
  783. list-style: none;
  784. font-feature-settings: 'tnum';
  785. position: relative;
  786. display: inline-block;
  787. width: 100%;
  788. min-width: 0;
  789. padding: 8px 12px;
  790. color: rgba(0, 0, 0, 0.85);
  791. font-size: 14px;
  792. line-height: 1.5715;
  793. background-color: #fff;
  794. background-image: none;
  795. border: 1px solid #d9d9d9;
  796. border-radius: 6px;
  797. transition: all 0.3s;
  798. }
  799. .ant-input:focus {
  800. border-color: #4096ff;
  801. box-shadow: 0 0 0 2px rgba(5, 145, 255, 0.1);
  802. outline: 0;
  803. }
  804. .ant-input:hover {
  805. border-color: #4096ff;
  806. }
  807. .ant-select {
  808. box-sizing: border-box;
  809. margin: 0;
  810. padding: 0;
  811. color: rgba(0, 0, 0, 0.85);
  812. font-size: 14px;
  813. line-height: 1.5715;
  814. list-style: none;
  815. position: relative;
  816. display: inline-block;
  817. cursor: pointer;
  818. width: 100%;
  819. }
  820. .ant-select select {
  821. width: 100%;
  822. height: 36px;
  823. padding: 0 11px;
  824. border: 1px solid #d9d9d9;
  825. border-radius: 6px;
  826. background-color: #fff;
  827. transition: all 0.3s;
  828. }
  829. .ant-select select:focus {
  830. border-color: #4096ff;
  831. box-shadow: 0 0 0 2px rgba(5, 145, 255, 0.1);
  832. outline: 0;
  833. }
  834. .ant-select select:hover {
  835. border-color: #4096ff;
  836. }
  837. /* 数字输入框样式 */
  838. .ant-input-number {
  839. position: relative;
  840. width: 100%;
  841. }
  842. .ant-input-number-input {
  843. width: 100%;
  844. padding-right: 30px;
  845. box-sizing: border-box;
  846. }
  847. .ant-input-number-suffix {
  848. position: absolute;
  849. right: 12px;
  850. top: 50%;
  851. transform: translateY(-50%);
  852. color: rgba(0, 0, 0, 0.45);
  853. font-size: 14px;
  854. }
  855. /* 按钮样式 */
  856. .ant-btn {
  857. line-height: 1.5715;
  858. position: relative;
  859. display: inline-flex;
  860. align-items: center;
  861. justify-content: center;
  862. gap: 8px;
  863. font-weight: 400;
  864. white-space: nowrap;
  865. text-align: center;
  866. background-image: none;
  867. border: 1px solid transparent;
  868. box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
  869. cursor: pointer;
  870. transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
  871. user-select: none;
  872. touch-action: manipulation;
  873. height: 36px;
  874. padding: 6px 15px;
  875. font-size: 14px;
  876. border-radius: 6px;
  877. }
  878. .ant-btn-primary {
  879. color: #fff;
  880. background-color: #1677ff;
  881. border-color: #1677ff;
  882. text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
  883. box-shadow: 0 2px 0 rgba(5, 145, 255, 0.1);
  884. }
  885. .ant-btn-primary:hover {
  886. color: #fff;
  887. background-color: #4096ff;
  888. border-color: #4096ff;
  889. transform: translateY(-1px);
  890. box-shadow: 0 4px 12px rgba(5, 145, 255, 0.2);
  891. }
  892. .ant-btn-default {
  893. color: rgba(0, 0, 0, 0.85);
  894. background-color: #fff;
  895. border-color: #d9d9d9;
  896. }
  897. .ant-btn-default:hover {
  898. color: #1677ff;
  899. border-color: #1677ff;
  900. background-color: #fff;
  901. }
  902. .ant-btn-text {
  903. color: #1677ff;
  904. background-color: transparent;
  905. border-color: transparent;
  906. box-shadow: none;
  907. }
  908. .ant-btn-text:hover {
  909. color: #4096ff;
  910. background-color: rgba(0, 0, 0, 0.06);
  911. border-color: transparent;
  912. }
  913. .ant-btn-link {
  914. color: #1677ff;
  915. background-color: transparent;
  916. border-color: transparent;
  917. box-shadow: none;
  918. padding: 0;
  919. height: auto;
  920. }
  921. .ant-btn-link:hover {
  922. color: #4096ff;
  923. background-color: transparent;
  924. border-color: transparent;
  925. }
  926. .ant-btn-sm {
  927. height: 28px;
  928. font-size: 13px;
  929. border-radius: 4px;
  930. }
  931. .ant-btn-dangerous {
  932. color: #ff4d4f;
  933. background-color: transparent;
  934. border-color: transparent;
  935. box-shadow: none;
  936. }
  937. .ant-btn-dangerous:hover {
  938. color: #ff7875;
  939. background-color: rgba(255, 77, 79, 0.06);
  940. border-color: transparent;
  941. }
  942. /* 图标样式 */
  943. .anticon {
  944. display: inline-flex;
  945. align-items: center;
  946. justify-content: center;
  947. color: inherit;
  948. font-style: normal;
  949. line-height: 0;
  950. text-align: center;
  951. text-transform: none;
  952. vertical-align: -0.125em;
  953. text-rendering: optimizeLegibility;
  954. -webkit-font-smoothing: antialiased;
  955. -moz-osx-font-smoothing: grayscale;
  956. }
  957. .anticon-search::before {
  958. content: "🔍";
  959. }
  960. .anticon-plus::before {
  961. content: "+";
  962. font-weight: bold;
  963. }
  964. .anticon-edit::before {
  965. content: "✏️";
  966. }
  967. .anticon-delete::before {
  968. content: "🗑️";
  969. }
  970. .anticon-close::before {
  971. content: "✕";
  972. font-size: 16px;
  973. }
  974. .anticon-left::before {
  975. content: "←";
  976. }
  977. .anticon-right::before {
  978. content: "→";
  979. }
  980. /* 间距样式 */
  981. .ant-space {
  982. display: inline-flex;
  983. gap: 8px;
  984. }
  985. /* 响应式设计 */
  986. @@media (max-width: 768px) {
  987. .equipment-management-container {
  988. padding: 12px;
  989. }
  990. .ant-card-body {
  991. padding: 16px;
  992. }
  993. .ant-col {
  994. width: 100% !important;
  995. }
  996. .modal-simple-size {
  997. width: 95vw !important;
  998. margin: 0 auto;
  999. }
  1000. .ant-modal-header {
  1001. padding: 16px;
  1002. }
  1003. .ant-modal-body {
  1004. padding: 16px;
  1005. }
  1006. .ant-modal-footer {
  1007. padding: 12px 16px;
  1008. flex-direction: column;
  1009. gap: 8px;
  1010. }
  1011. .ant-modal-footer button {
  1012. width: 100%;
  1013. }
  1014. .ant-form-vertical .ant-form-item {
  1015. margin-bottom: 20px;
  1016. }
  1017. .ant-table-thead > tr > th,
  1018. .ant-table-tbody > tr > td {
  1019. padding: 10px 6px;
  1020. font-size: 13px;
  1021. }
  1022. .capacity-cell {
  1023. flex-direction: column;
  1024. align-items: flex-start;
  1025. gap: 4px;
  1026. }
  1027. .capacity-progress {
  1028. width: 100%;
  1029. }
  1030. .action-buttons {
  1031. flex-direction: column;
  1032. gap: 4px;
  1033. }
  1034. }
  1035. @@media (max-width: 480px) {
  1036. .ant-btn {
  1037. height: 40px;
  1038. font-size: 15px;
  1039. }
  1040. .ant-table-thead > tr > th,
  1041. .ant-table-tbody > tr > td {
  1042. font-size: 12px;
  1043. }
  1044. }
  1045. /* 小屏幕适配 */
  1046. @@media (max-width: 576px) {
  1047. .ant-table-wrapper {
  1048. overflow-x: auto;
  1049. }
  1050. .ant-table {
  1051. min-width: 600px;
  1052. }
  1053. .filter-section .ant-row {
  1054. flex-direction: column;
  1055. }
  1056. .filter-section .ant-col {
  1057. width: 100%;
  1058. }
  1059. .filter-section .ant-col-12 {
  1060. text-align: left;
  1061. margin-top: 8px;
  1062. }
  1063. }
  1064. </style>