增加视频管理和日志管理

This commit is contained in:
mmc
2026-08-10 14:51:11 +08:00
parent 3cc738195f
commit 7d81362a88
14 changed files with 533 additions and 9 deletions

223
build_log.txt Normal file
View File

@@ -0,0 +1,223 @@

> react-example@0.0.0 build
> vite build
vite v6.4.2 building for production...
transforming...
鉁?[39m 9318 modules transformed.
rendering chunks...
npm : [plugin vite:reporter]
所在位置 行:1 字符: 25
+ cd "D:\code\机器人智慧服务系统"; npm run build 2>&1 | Out-String -Width 300 | ...
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: ([plugin vite:reporter] :String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
(!) D:/code/鏈哄櫒浜烘櫤鎱ф湇鍔$郴缁?src/store/userSlice.ts is dynamically imported by D:/code/鏈哄櫒浜烘櫤鎱ф湇鍔$郴缁?src/api/request.ts but also statically imported by D:/code/鏈哄櫒浜烘櫤鎱ф湇鍔$郴缁?src/components/Layout.tsx, D:/code/鏈哄櫒浜烘櫤鎱ф湇鍔$郴缁?src/pages/Login.tsx, D:/code/鏈哄櫒浜烘櫤鎱ф湇鍔$郴缁?src/pages/Profile.tsx, D:/code/鏈哄櫒浜烘櫤
鎱ф湇鍔$郴缁?src/store/index.ts, dynamic import will not move module into another chunk.

[plugin vite:reporter]
(!) D:/code/鏈哄櫒浜烘櫤鎱ф湇鍔$郴缁?src/store/index.ts is dynamically imported by D:/code/鏈哄櫒浜烘櫤鎱ф湇鍔$郴缁?src/api/request.ts but also statically imported by D:/code/鏈哄櫒浜烘櫤鎱ф湇鍔$郴缁?src/components/Layout.tsx, D:/code/鏈哄櫒浜烘櫤鎱ф湇鍔$郴缁?src/main.tsx, D:/code/鏈哄櫒浜烘櫤鎱ф湇鍔$郴缁?src/pages/Login.tsx, D:/code/鏈哄櫒浜烘櫤鎱ф湇鍔$郴缁?src/p
ages/Profile.tsx, dynamic import will not move module into another chunk.

computing gzip size...
dist/index.html  2.52 kB 鈹?gzip: 1.13 kB
dist/assets/gecaocar-DMp9NnzT.png  16.23 kB
dist/assets/bgmain-DtapCCMy.jpg  71.91 kB
dist/assets/xunjiancar-CAfmDzzl.png  213.39 kB
dist/assets/carplane-f-ISorrw.png  259.24 kB
dist/assets/logincard-B8hQtFRG.png  355.36 kB
dist/assets/logo-BZW_A_MC.png  1,814.58 kB
dist/assets/bgc2-CfINnBvq.png  3,954.80 kB
dist/assets/inspectCar-C-BgLHu6.png 14,801.70 kB
dist/assets/Home-C67sUCdb.css  1.21 kB 鈹?gzip: 0.55 kB
dist/assets/index-bPDl6cOh.css  70.30 kB 鈹?gzip: 12.91 kB
dist/assets/useForceUpdate-DtM2zGu_.js  0.11 kB 鈹?gzip: 0.13 kB
dist/assets/EyeOutlined-BpIrDvhk.js  0.20 kB 鈹?gzip: 0.18 kB
dist/assets/index-BZ2XYiLY.js  0.29 kB 鈹?gzip: 0.17 kB
dist/assets/constants-DwgWOJkA.js  0.31 kB 鈹?gzip: 0.28 kB
dist/assets/chevron-right-C-2t-OOD.js  0.31 kB 鈹?gzip: 0.25 kB
dist/assets/SwapRightOutlined-CaVf5wQU.js  0.32 kB 鈹?gzip: 0.26 kB
dist/assets/index-CPl-Ppnu.js  0.35 kB 鈹?gzip: 0.19 kB
dist/assets/useBreakpoint-B_LmyIb1.js  0.37 kB 鈹?gzip: 0.27 kB
dist/assets/Empty-Ctr5z1y_.js  0.40 kB 鈹?gzip: 0.32 kB
dist/assets/CloseOutlined-DNEu5lOW.js  0.41 kB 鈹?gzip: 0.31 kB
dist/assets/wind-BGkejR8u.js  0.43 kB 鈹?gzip: 0.31 kB
dist/assets/CheckOutlined-DWAq-EXD.js  0.44 kB 鈹?gzip: 0.31 kB
dist/assets/PlusOutlined-9xQFYjfY.js  0.44 kB 鈹?gzip: 0.31 kB
dist/assets/index-Bw21YrWE.js  0.44 kB 鈹?gzip: 0.22 kB
dist/assets/zap-T1mbixAe.js  0.45 kB 鈹?gzip: 0.32 kB
dist/assets/collapse-BbEVqHco.js  0.45 kB 鈹?gzip: 0.20 kB
dist/assets/EyeOutlined-ChMUbuX5.js  0.45 kB 鈹?gzip: 0.32 kB
dist/assets/EditOutlined-D_m5HT9j.js  0.45 kB 鈹?gzip: 0.33 kB
dist/assets/DeleteOutlined-DJWa8o0s.js  0.45 kB 鈹?gzip: 0.32 kB
dist/assets/SearchOutlined-D93E4Oj1.js  0.45 kB 鈹?gzip: 0.33 kB
dist/assets/CalendarOutlined-DZsP2fe7.js  0.45 kB 鈹?gzip: 0.33 kB
dist/assets/DownloadOutlined-RqkGX7Gr.js  0.45 kB 鈹?gzip: 0.33 kB
dist/assets/ClockCircleOutlined-BPcFT2WX.js  0.46 kB 鈹?gzip: 0.33 kB
dist/assets/DownloadOutlined-DTidZHln.js  0.46 kB 鈹?gzip: 0.30 kB
dist/assets/SwapOutlined-MHggqN5a.js  0.47 kB 鈹?gzip: 0.34 kB
dist/assets/DeleteOutlined-DFutYCzo.js  0.50 kB 鈹?gzip: 0.34 kB
dist/assets/shield-check-D8S1FByg.js  0.50 kB 鈹?gzip: 0.36 kB
dist/assets/CalendarOutlined-DKq2sctu.js  0.51 kB 鈹?gzip: 0.31 kB
dist/assets/ClockCircleOutlined-CELcQ7Uv.js  0.53 kB 鈹?gzip: 0.34 kB
dist/assets/EditOutlined-DdmGLkPL.js  0.56 kB 鈹?gzip: 0.38 kB
dist/assets/EyeOutlined-Bmd706kk.js  0.63 kB 鈹?gzip: 0.39 kB
dist/assets/PauseOutlined-BqDFOIsB.js  0.65 kB 鈹?gzip: 0.47 kB
dist/assets/play-B5DtZ9KN.js  0.66 kB 鈹?gzip: 0.36 kB
dist/assets/MoreOutlined-Bojj3j6_.js  0.67 kB 鈹?gzip: 0.43 kB
dist/assets/index-EuwyWURW.js  0.75 kB 鈹?gzip: 0.30 kB
dist/assets/ThunderboltOutlined-Dpsk7I4X.js  0.81 kB 鈹?gzip: 0.57 kB
dist/assets/SearchOutlined-DBDe8SEH.js  0.82 kB 鈹?gzip: 0.52 kB
dist/assets/RiseOutlined-CBId0fo2.js  0.83 kB 鈹?gzip: 0.57 kB
dist/assets/cpu-Sb-QLlDK.js  0.83 kB 鈹?gzip: 0.41 kB
dist/assets/trending-up-CfL4zUOM.js  0.83 kB 鈹?gzip: 0.39 kB
dist/assets/WarningOutlined-iECt8d0C.js  0.84 kB 鈹?gzip: 0.59 kB
dist/assets/DatabaseOutlined-BwouSYc2.js  0.84 kB 鈹?gzip: 0.53 kB
dist/assets/EnterOutlined-DahysLq8.js  0.85 kB 鈹?gzip: 0.46 kB
dist/assets/InfoCircleOutlined-3BD64K2v.js  0.88 kB 鈹?gzip: 0.56 kB
dist/assets/MailOutlined-CqThN_M3.js  0.88 kB 鈹?gzip: 0.59 kB
dist/assets/LockOutlined-Cj1K_c-k.js  0.88 kB 鈹?gzip: 0.60 kB
dist/assets/ExclamationCircleOutlined-DwOXB-Gw.js  0.88 kB 鈹?gzip: 0.57 kB
dist/assets/index-DRJqI9JD.js  0.89 kB 鈹?gzip: 0.28 kB
dist/assets/VideoCameraOutlined-B27jqIy_.js  0.89 kB 鈹?gzip: 0.62 kB
dist/assets/PlayCircleOutlined-BYW1xJTi.js  0.90 kB 鈹?gzip: 0.59 kB
dist/assets/LeftOutlined-D6OBLFDp.js  0.90 kB 鈹?gzip: 0.49 kB
dist/assets/ProtectedRoute-IM0Sy2Xs.js  0.93 kB 鈹?gzip: 0.55 kB
dist/assets/index-DVB2C8Ff.js  0.93 kB 鈹?gzip: 0.56 kB
dist/assets/CheckCircleOutlined-Bxmsq8Kw.js  0.94 kB 鈹?gzip: 0.61 kB
dist/assets/droplets-NAdX3BmJ.js  0.94 kB 鈹?gzip: 0.50 kB
dist/assets/FileTextOutlined-CNX0YM61.js  0.96 kB 鈹?gzip: 0.62 kB
dist/assets/RobotOutlined-BORS_9JP.js  0.97 kB 鈹?gzip: 0.63 kB
dist/assets/BellOutlined-D5GkP0tr.js  0.97 kB 鈹?gzip: 0.65 kB
dist/assets/FormOutlined-BQt8zVxi.js  1.00 kB 鈹?gzip: 0.66 kB
dist/assets/BarChartOutlined-CuaUHpnJ.js  1.01 kB 鈹?gzip: 0.53 kB
dist/assets/BgColorsOutlined-DChci6LA.js  1.01 kB 鈹?gzip: 0.66 kB
dist/assets/CameraOutlined-BVs6tmVA.js  1.02 kB 鈹?gzip: 0.66 kB
dist/assets/AppstoreOutlined-VqZrPBsZ.js  1.06 kB 鈹?gzip: 0.52 kB
dist/assets/ExperimentOutlined-BgHosctB.js  1.06 kB 鈹?gzip: 0.72 kB
dist/assets/BranchesOutlined-DoqceiH7.js  1.07 kB 鈹?gzip: 0.65 kB
dist/assets/index-7tIGlO1L.js  1.10 kB 鈹?gzip: 0.59 kB
dist/assets/SystemSetting-C5nTGJmA.js  1.11 kB 鈹?gzip: 0.69 kB
dist/assets/ExpandOutlined-DmxD2QPe.js  1.12 kB 鈹?gzip: 0.59 kB
dist/assets/CloudOutlined-7Kp-N517.js  1.13 kB 鈹?gzip: 0.74 kB
dist/assets/UserOutlined-C4ebdDKF.js  1.22 kB 鈹?gzip: 0.76 kB
dist/assets/ZoomOutOutlined-CdPjzMfa.js  1.22 kB 鈹?gzip: 0.42 kB
dist/assets/ReloadOutlined-DdNrEJCC.js  1.23 kB 鈹?gzip: 0.77 kB
dist/assets/AlertOutlined-BEmiVn_J.js  1.24 kB 鈹?gzip: 0.73 kB
dist/assets/AimOutlined-DjiWwPPF.js  1.25 kB 鈹?gzip: 0.71 kB
dist/assets/alarmApi-BZ-m506o.js  1.25 kB 鈹?gzip: 0.35 kB
dist/assets/SyncOutlined-CLMibV2z.js  1.27 kB 鈹?gzip: 0.81 kB
dist/assets/MoneyCollectOutlined-DPX31kZt.js  1.30 kB 鈹?gzip: 0.79 kB
dist/assets/FullscreenOutlined-CKE8zdPY.js  1.34 kB 鈹?gzip: 0.77 kB
dist/assets/HistoryOutlined-Cm2yhZBV.js  1.41 kB 鈹?gzip: 0.87 kB
dist/assets/SafetyCertificateOutlined-Cv9Ug_8-.js  1.41 kB 鈹?gzip: 0.77 kB
dist/assets/SwapOutlined-C3QhvSoJ.js  1.42 kB 鈹?gzip: 0.65 kB
dist/assets/ZoomOutOutlined-BsCJgCwM.js  1.47 kB 鈹?gzip: 0.67 kB
dist/assets/satellite-DN23gGtM.js  1.47 kB 鈹?gzip: 0.54 kB
dist/assets/RocketOutlined-D32l95Ej.js  1.49 kB 鈹?gzip: 0.93 kB
dist/assets/EnvironmentOutlined-DqDsf3JY.js  1.49 kB 鈹?gzip: 0.91 kB
dist/assets/useBubbleLock-CnKa9QU1.js  1.54 kB 鈹?gzip: 0.75 kB
dist/assets/index-CVpDycq2.js  1.61 kB 鈹?gzip: 0.85 kB
dist/assets/ApiOutlined-B-23p3zJ.js  1.61 kB 鈹?gzip: 0.87 kB
dist/assets/SunOutlined-DulLtM_U.js  1.75 kB 鈹?gzip: 0.90 kB
dist/assets/DashboardOutlined-BcbkGzZH.js  1.83 kB 鈹?gzip: 0.93 kB
dist/assets/createLucideIcon-DQHekqUj.js  1.86 kB 鈹?gzip: 0.80 kB
dist/assets/CarOutlined-dQz3FjQ0.js  2.00 kB 鈹?gzip: 0.98 kB
dist/assets/index-DFuZdxqB.js  2.04 kB 鈹?gzip: 1.00 kB
dist/assets/systemSetting-Du4ydvo8.js  2.11 kB 鈹?gzip: 0.45 kB
dist/assets/PlayCircleFilled-CpNFZPGa.js  2.14 kB 鈹?gzip: 1.04 kB
dist/assets/LinkOutlined-DIT1vcCm.js  2.19 kB 鈹?gzip: 0.82 kB
dist/assets/index-DUin50PQ.js  2.19 kB 鈹?gzip: 1.14 kB
dist/assets/SettingOutlined-D_j9s6J1.js  2.35 kB 鈹?gzip: 1.21 kB
dist/assets/VideoPlayer-CIv7hT9k.js  2.39 kB 鈹?gzip: 1.33 kB
dist/assets/PictureTwoTone-BaO8R2Hf.js  2.42 kB 鈹?gzip: 1.04 kB
dist/assets/index-B5YBJdOw.js  2.42 kB 鈹?gzip: 1.16 kB
dist/assets/LoadingOutlined-EmNUEJCp.js  2.94 kB 鈹?gzip: 1.38 kB
dist/assets/SaveOutlined-CMHNvWPT.js  3.28 kB 鈹?gzip: 1.40 kB
dist/assets/device-CZX1s96N.js  3.38 kB 鈹?gzip: 0.76 kB
dist/assets/index-CBNqhCuo.js  3.61 kB 鈹?gzip: 1.27 kB
dist/assets/row-DptZftaY.js  3.74 kB 鈹?gzip: 1.64 kB
dist/assets/OnlineUser-B-LR1RQ1.js  3.74 kB 鈹?gzip: 1.77 kB
dist/assets/MonitorOutlined-8H67Im48.js  3.85 kB 鈹?gzip: 1.29 kB
dist/assets/VideoCameraAddOutlined-ujFYsK7R.js  3.86 kB 鈹?gzip: 1.42 kB
dist/assets/VideoManagement-lgnfcqOK.js  3.89 kB 鈹?gzip: 1.78 kB
dist/assets/index-s0uyhwL6.js  4.14 kB 鈹?gzip: 1.74 kB
dist/assets/MenuUnfoldOutlined-oe1UMe64.js  4.16 kB 鈹?gzip: 1.30 kB
dist/assets/ScanOutlined-B9n4kgAv.js  4.19 kB 鈹?gzip: 1.38 kB
dist/assets/index-ChYh3EL1.js  4.21 kB 鈹?gzip: 1.58 kB
dist/assets/index-DaNu9UFa.js  4.38 kB 鈹?gzip: 1.75 kB
dist/assets/Organization-CvcvXgnR.js  4.44 kB 鈹?gzip: 1.95 kB
dist/assets/index-BrtQE5cA.js  4.58 kB 鈹?gzip: 1.91 kB
dist/assets/WomanOutlined-DhEdcpR3.js  4.82 kB 鈹?gzip: 1.76 kB
dist/assets/LineChartOutlined-D9mAa485.js  5.07 kB 鈹?gzip: 1.82 kB
dist/assets/Roles-peab14ff.js  5.30 kB 鈹?gzip: 2.32 kB
dist/assets/index-UaCBHKR_.js  5.44 kB 鈹?gzip: 2.24 kB
dist/assets/index-DedMQGVM.js  5.57 kB 鈹?gzip: 2.20 kB
dist/assets/Users-CMzPBXJp.js  5.60 kB 鈹?gzip: 2.22 kB
dist/assets/index-BL30Qw1c.js  5.83 kB 鈹?gzip: 2.63 kB
dist/assets/Sider-DXAaqKLr.js  6.64 kB 鈹?gzip: 2.85 kB
dist/assets/Input-4q6jt_g5.js  6.73 kB 鈹?gzip: 2.71 kB
dist/assets/LogManagement-O6D_fZci.js  6.94 kB 鈹?gzip: 3.29 kB
dist/assets/dayjs.min-buSFCgKN.js  7.15 kB 鈹?gzip: 3.11 kB
dist/assets/Timeline-CRgNRkZL.js  7.47 kB 鈹?gzip: 2.32 kB
dist/assets/Profile-Cm2WO_xU.js  7.73 kB 鈹?gzip: 2.71 kB
dist/assets/index-6bOsNg-M.js  7.98 kB 鈹?gzip: 2.71 kB
dist/assets/GlobalOutlined-BiQGRkIB.js  8.13 kB 鈹?gzip: 2.73 kB
dist/assets/StationManage-gzXazdWA.js  9.47 kB 鈹?gzip: 3.25 kB
dist/assets/CaretUpOutlined-CJ_QY0uc.js  9.72 kB 鈹?gzip: 3.73 kB
dist/assets/index-D3Ma-lrv.js  9.90 kB 鈹?gzip: 4.14 kB
dist/assets/index-BuRUqopv.js  10.86 kB 鈹?gzip: 3.66 kB
dist/assets/index-ALYSXNVI.js  13.09 kB 鈹?gzip: 4.32 kB
dist/assets/RealTimeMonitor-BJaT8v8y.js  13.49 kB 鈹?gzip: 3.91 kB
dist/assets/Downloads-B6JNnYAG.js  14.52 kB 鈹?gzip: 5.40 kB
dist/assets/UserPage-W6MtChV2.js  14.56 kB 鈹?gzip: 5.10 kB
dist/assets/Layout-B5v7n-84.js  15.26 kB 鈹?gzip: 6.20 kB
dist/assets/mowerAllView-CIVQaOob.js  15.31 kB 鈹?gzip: 5.15 kB
dist/assets/progress-VpEMMtch.js  16.32 kB 鈹?gzip: 5.97 kB
dist/assets/Video-CfHlTupO.js  17.68 kB 鈹?gzip: 6.43 kB
dist/assets/VideoMonitorPage-B8eUkNSX.js  19.41 kB 鈹?gzip: 5.77 kB
dist/assets/Home-CsUKVxVV.js  19.63 kB 鈹?gzip: 5.88 kB
dist/assets/ReportCenter-vBJ0zNbs.js  20.12 kB 鈹?gzip: 5.58 kB
dist/assets/AIAnalysis-BXDx_pZ0.js  20.79 kB 鈹?gzip: 6.03 kB
dist/assets/index-B_4lrhUw.js  21.18 kB 鈹?gzip: 7.75 kB
dist/assets/SystemMaintain-B7rVVlmq.js  21.20 kB 鈹?gzip: 6.39 kB
dist/assets/Agri-Bb29XfiI.js  23.53 kB 鈹?gzip: 6.77 kB
dist/assets/CleanOptimization-CWLVvh62.js  23.86 kB 鈹?gzip: 6.84 kB
dist/assets/BasicSettings-CuuSvPVD.js  24.01 kB 鈹?gzip: 6.95 kB
dist/assets/Pagination-uKW-OnZm.js  24.07 kB 鈹?gzip: 7.27 kB
dist/assets/index-FQVBqmQl.js  24.29 kB 鈹?gzip: 6.84 kB
dist/assets/index-CGjCB8GI.js  24.61 kB 鈹?gzip: 8.41 kB
dist/assets/DeviceAccessPage-CVno98nd.js  27.21 kB 鈹?gzip: 7.74 kB
dist/assets/DeviceStatusPage-uF0YhWCt.js  27.91 kB 鈹?gzip: 8.86 kB
dist/assets/AntdIcon-Vj78gm8A.js  28.67 kB 鈹?gzip: 10.61 kB
dist/assets/index-BPOI5hR7.js  30.34 kB 鈹?gzip: 10.80 kB
dist/assets/index-f3imQPvT.js  31.74 kB 鈹?gzip: 11.04 kB
dist/assets/TextArea-CsLo8K_h.js  33.23 kB 鈹?gzip: 9.32 kB
dist/assets/DeviceOverviewPage-CD8FxotZ.js  35.09 kB 鈹?gzip: 9.63 kB
dist/assets/Map-DUcm8af3.js  35.52 kB 鈹?gzip: 16.50 kB
dist/assets/DeviceParamModal-DSGwPtsu.js  36.02 kB 鈹?gzip: 12.66 kB
dist/assets/Alerts-ZPDUGkfy.js  43.36 kB 鈹?gzip: 13.62 kB
dist/assets/WayLinePage-y2GT_PFs.js  47.95 kB 鈹?gzip: 12.81 kB
dist/assets/RobotTaskPage-CZh-hfge.js  50.72 kB 鈹?gzip: 15.77 kB
dist/assets/index-BTopT9-s.js  53.48 kB 鈹?gzip: 17.46 kB
dist/assets/index-0KzkHCl3.js  85.39 kB 鈹?gzip: 28.40 kB
dist/assets/index-CjPizwAt.js  104.37 kB 鈹?gzip: 33.62 kB
dist/assets/Table-CA0nEd5i.js  105.27 kB 鈹?gzip: 35.10 kB
dist/assets/index-CowEi-GC.js  118.83 kB 鈹?gzip: 40.16 kB
dist/assets/Login-DfIjGkfy.js  127.88 kB 鈹?gzip: 42.31 kB
dist/assets/useMqtt-DpGm36ma.js  372.77 kB 鈹?gzip: 112.38 kB
dist/assets/recharts-CrJS7ZSc.js  376.93 kB 鈹?gzip: 108.14 kB
dist/assets/index-Dt7gCQGU.js  651.89 kB 鈹?gzip: 215.80 kB
dist/assets/DeviceManagement-DpXxWEOS.js  722.91 kB 鈹?gzip: 158.92 kB
dist/assets/MenuList-pnm43MIv.js  857.20 kB 鈹?gzip: 165.01 kB
dist/assets/WorkOrder-DM_q3eps.js  1,507.95 kB 鈹?gzip: 445.53 kB
dist/assets/DroneVideoPlayer-DsyzzCPW.js  2,529.14 kB 鈹?gzip: 708.00 kB

(!) Some chunks are larger than 1000 kB after minification. Consider:
- Using dynamic import() to code-split the application
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
[vite-plugin-static-copy] Copied 1 items.
鉁?built in 2m 34s

View File

@@ -169,6 +169,9 @@ const BasicSettings = () => {
return (
<div className="normal-spacing" style={{ padding: '8px 0px', background: '#f5f7fa', minHeight: '100vh' }}>
<div style={{ padding: '0px 0px 8px' }}>
<Typography.Title level={4} style={{ margin: 0, fontSize: 'clamp(14px, 1.2vw, 18px)' }}>基础设置</Typography.Title>
</div>
{/* 顶部统计卡片 */}

View File

@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
import {
Card, Table, Button, Space, Form, Modal, Input, Select, InputNumber,
Tabs, Tag, Popconfirm, message, Divider, Switch, Empty, Checkbox,
Typography,
} from 'antd';
import {
PlusOutlined, EditOutlined, DeleteOutlined, ProductOutlined,
@@ -494,6 +495,9 @@ export default function DeviceAccessPage() {
return (
<div className="p-0 pt-[8px] bg-[#f5f7fa] min-h-screen">
{contextHolder}
<div style={{ padding: '0px 0px 8px' }}>
<Typography.Title level={4} style={{ margin: 0, fontSize: 'clamp(14px, 1.2vw, 18px)' }}>设备接入</Typography.Title>
</div>
<Card className="mb-4">
<Tabs activeKey={activeTab} onChange={setActiveTab}>
<TabPane tab={<span><VideoCameraOutlined />设备管理</span>} key="device" />

View File

@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react';
import {
Tabs,
Typography,
} from 'antd';
import SerialNumGenerate from "./SerialNumGenerate"
@@ -77,6 +78,9 @@ export default function DeviceManagementPage() {
return (
<div style={{ padding: "8px 0px", paddingTop: 0 }}>
<div style={{ padding: '0px 0px 4px' }}>
<Typography.Title level={4} style={{ margin: 0, fontSize: 'clamp(14px, 1.2vw, 18px)' }}>设备管理</Typography.Title>
</div>
<Tabs
activeKey={activeTab}
onChange={setActiveTab}
@@ -88,6 +92,7 @@ export default function DeviceManagementPage() {
<Tabs.TabPane key="camera" tab="摄像头管理" />
<Tabs.TabPane key="serialGenerate" tab="机器人生成" />
</Tabs>
{renderTabContent()}
</div >
);

View File

@@ -0,0 +1,155 @@
import React, { useState, useMemo } from 'react';
import {
Card as AntdCard, Table, Select, Input, Button, Tag, Space, Typography, Row, Col, DatePicker, message
} from 'antd';
import {
FileTextOutlined, SearchOutlined, ReloadOutlined, DownloadOutlined,
InfoCircleOutlined, WarningOutlined, ExclamationCircleOutlined, CheckCircleOutlined
} from '@ant-design/icons';
import dayjs from 'dayjs';
const { Title, Text } = Typography;
const { RangePicker } = DatePicker;
// ======================
// 日志管理(仅 admin 可见)
// 说明:当前为前端展示页,数据使用本地 mock。
// 接入真实数据时,可对接命令服务日志 GET /api/commands(设备控制流水)、
// 状态服务历史 GET /api/status/:deviceId/history,或后端 /system/operlog 操作日志接口。
// ======================
const MOCK_LOGS = [
{ id: 1, time: '2026-08-10 13:02:11', level: 'info', source: '系统', operator: '系统', message: '机器人智慧服务系统 Alpha 三服务(接入/状态/命令)启动完成' },
{ id: 2, time: '2026-08-10 12:58:40', level: 'action', source: '命令服务', operator: '张运维', message: '向 ROBOT-001 下发移动命令(seq=d6b5d545),执行结果:成功' },
{ id: 3, time: '2026-08-10 12:51:22', level: 'action', source: '用户管理', operator: '张运维', message: '新增运维工程师账号「李工」,角色:运维工程师' },
{ id: 4, time: '2026-08-10 12:40:05', level: 'warning', source: '设备接入', operator: '系统', message: 'DRONE-001 心跳超时(>60s),已自动置为离线' },
{ id: 5, time: '2026-08-10 12:33:18', level: 'error', source: '命令服务', operator: '系统', message: 'ROBOT-002 执行「自主巡检」超时,已自动重试 1 次后仍失败' },
{ id: 6, time: '2026-08-10 12:20:09', level: 'action', source: '系统设置', operator: '张运维', message: '修改系统名称与数据刷新频率(5min → 1min)' },
{ id: 7, time: '2026-08-10 11:55:44', level: 'info', source: '状态服务', operator: '系统', message: '设备 ROBOT-001 上报状态:电量 85%,模式 patrol' },
{ id: 8, time: '2026-08-10 11:30:02', level: 'warning', source: '告警中心', operator: '系统', message: '逆变器 INV-03 输出功率较昨日下降 16%,触发预警' },
{ id: 9, time: '2026-08-10 10:48:37', level: 'action', source: '视频管理', operator: '王班长', message: '调整 CAM-04 热成像通道码率参数' },
{ id: 10, time: '2026-08-10 10:12:15', level: 'info', source: '系统', operator: '系统', message: '每日自动备份完成,备份文件:backup-20260810-0200.tar' },
{ id: 11, time: '2026-08-10 09:40:50', level: 'error', source: '设备接入', operator: '系统', message: '未知设备尝试注册(secret 校验失败),已拒绝,来源 IP:10.0.12.45' },
{ id: 12, time: '2026-08-10 09:05:23', level: 'action', source: '角色管理', operator: '张运维', message: '为「数据分析师」角色授权「报表中心-导出」权限' },
];
const LEVEL_CONFIG: Record<string, { color: string; label: string; icon: React.ReactNode }> = {
info: { color: 'blue', label: '信息', icon: <InfoCircleOutlined /> },
action: { color: 'green', label: '操作', icon: <CheckCircleOutlined /> },
warning: { color: 'orange', label: '警告', icon: <WarningOutlined /> },
error: { color: 'red', label: '错误', icon: <ExclamationCircleOutlined /> },
};
const LogManagement = () => {
const [data, setData] = useState(MOCK_LOGS);
const [levelFilter, setLevelFilter] = useState<string>('all');
const [keyword, setKeyword] = useState('');
const [dateRange, setDateRange] = useState<[any, any] | null>(null);
const filtered = useMemo(() => {
return data.filter((log) => {
if (levelFilter !== 'all' && log.level !== levelFilter) return false;
if (keyword && !(log.message.includes(keyword) || log.operator.includes(keyword) || log.source.includes(keyword))) return false;
if (dateRange && dateRange[0] && dateRange[1]) {
const t = dayjs(log.time, 'YYYY-MM-DD HH:mm:ss');
if (t.isBefore(dateRange[0].startOf('day')) || t.isAfter(dateRange[1].endOf('day'))) return false;
}
return true;
});
}, [data, levelFilter, keyword, dateRange]);
const handleRefresh = () => {
setData(MOCK_LOGS);
setLevelFilter('all');
setKeyword('');
setDateRange(null);
message.success('日志已刷新');
};
const handleExport = () => {
const header = '时间,级别,来源,操作人,消息\n';
const rows = filtered.map((l) => `${l.time},${LEVEL_CONFIG[l.level].label},${l.source},${l.operator},"${l.message}"`).join('\n');
const blob = new Blob([header + rows], { type: 'text/csv;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `系统日志_${dayjs().format('YYYYMMDD_HHmmss')}.csv`;
a.click();
URL.revokeObjectURL(url);
message.success(`已导出 ${filtered.length} 条日志`);
};
const columns = [
{ title: '时间', dataIndex: 'time', key: 'time', width: 170, fixed: 'left' as const },
{
title: '级别', dataIndex: 'level', key: 'level', width: 80,
render: (lv: string) => {
const c = LEVEL_CONFIG[lv] || LEVEL_CONFIG.info;
return <Tag color={c.color} icon={c.icon} style={{ margin: 0 }}>{c.label}</Tag>;
},
},
{ title: '来源', dataIndex: 'source', key: 'source', width: 120 },
{ title: '操作人', dataIndex: 'operator', key: 'operator', width: 100 },
{ title: '消息', dataIndex: 'message', key: 'message', ellipsis: true },
];
return (
<div style={{ padding: '8px 0px', background: '#f5f7fa', minHeight: '100vh' }}>
<div style={{ marginBottom: 8 }}>
<Title level={4} style={{ margin: 0, fontSize: 'clamp(14px, 1.1vw, 18px)' }}>日志管理</Title>
<Text type="secondary" style={{ fontSize: 12 }}>系统运行、设备控制与管理员操作流水,仅超级管理员可查看</Text>
</div>
<AntdCard bordered={false} style={{ borderRadius: 10, marginBottom: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }} bodyStyle={{ padding: 12 }}>
<Row gutter={[8, 8]} align="middle">
<Col xs={24} sm={8} md={5}>
<Select
size="small" style={{ width: '100%' }}
value={levelFilter}
onChange={setLevelFilter}
options={[
{ value: 'all', label: '全部级别' },
{ value: 'info', label: '信息' },
{ value: 'action', label: '操作' },
{ value: 'warning', label: '警告' },
{ value: 'error', label: '错误' },
]}
/>
</Col>
<Col xs={24} sm={10} md={8}>
<Input
size="small" allowClear placeholder="搜索消息 / 操作人 / 来源"
prefix={<SearchOutlined />} value={keyword} onChange={(e) => setKeyword(e.target.value)}
/>
</Col>
<Col xs={24} sm={14} md={8}>
<RangePicker
size="small" style={{ width: '100%' }}
showTime={{ format: 'HH:mm' }} format="YYYY-MM-DD HH:mm"
value={dateRange as any} onChange={(v) => setDateRange(v as any)}
/>
</Col>
<Col xs={24} sm={10} md={3}>
<Space>
<Button size="small" icon={<ReloadOutlined />} onClick={handleRefresh}>刷新</Button>
<Button size="small" type="primary" icon={<DownloadOutlined />} onClick={handleExport}>导出</Button>
</Space>
</Col>
</Row>
</AntdCard>
<AntdCard bordered={false} style={{ borderRadius: 10, boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }} bodyStyle={{ padding: 4 }}>
<Table
size="small"
rowKey="id"
columns={columns}
dataSource={filtered}
scroll={{ x: 'max-content' }}
pagination={{ size: 'small', pageSize: 10, showTotal: (t) => `共 ${t} 条` }}
/>
</AntdCard>
</div>
);
};
export default LogManagement;

View File

@@ -1,7 +1,8 @@
import React, { useState, useEffect } from 'react';
import {
Table, Button, Space, Modal, Form, Tree, message,
Input, Popconfirm, Select, Card, InputNumber, Row, Col, Tooltip
Input, Popconfirm, Select, Card, InputNumber, Row, Col, Tooltip,
Typography
} from 'antd';
// 引入全部常用图标
import * as AllIcons from '@ant-design/icons';
@@ -186,7 +187,10 @@ export default function MenuList() {
return (
<div style={{ padding: '8px 0px' }}>
{contextHolder}
<Card title="菜单管理" style={{ borderRadius: 8 }}>
<div style={{ padding: '0px 0px 8px' }}>
<Typography.Title level={4} style={{ margin: 0, fontSize: 'clamp(14px, 1.2vw, 18px)' }}>菜单管理</Typography.Title>
</div>
<Card style={{ borderRadius: 8 }}>
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between' }}>
<Button type="primary" icon={<PlusOutlined />} onClick={() => {
setEditMenu(null);

View File

@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import {
Input, Button, Table, message, Space, Tabs, Card
Input, Button, Table, message, Space, Tabs, Card,
Typography
} from 'antd';
import { SearchOutlined, ReloadOutlined } from '@ant-design/icons';
@@ -87,6 +88,9 @@ const OnlineUserPage = () => {
return (
<div style={{ padding: "8px 0px", background: '#f5f7fa', minHeight: '100vh' }}>
<div style={{ padding: '0px 0px 8px' }}>
<Typography.Title level={4} style={{ margin: 0, fontSize: 'clamp(14px, 1.2vw, 18px)' }}>系统监控</Typography.Title>
</div>
{/* ====================== 顶部 Tabs ====================== */}
<Tabs
activeKey={activeTab}

View File

@@ -150,8 +150,11 @@ export default function Organization() {
return (
<div style={{ padding: '8px 0px' }}>
{contextHolder}
<div style={{ padding: '0px 0px 8px' }}>
<Typography.Title level={4} style={{ margin: 0, fontSize: 'clamp(14px, 1.2vw, 18px)' }}>组织管理</Typography.Title>
</div>
<AntdCard title="组织管理" style={{ borderRadius: 8 }}>
<AntdCard style={{ borderRadius: 8 }}>
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between' }}>
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
新增组织

View File

@@ -1,7 +1,8 @@
import React, { useState, useEffect } from 'react';
import {
Card, Table, Button, Space, Modal, Form, Input, Popconfirm, Select,
Switch, Divider, Row, Col, Tag, message
Switch, Divider, Row, Col, Tag, message,
Typography
} from 'antd';
import {
PlusOutlined, EditOutlined, DeleteOutlined, BgColorsOutlined
@@ -271,8 +272,12 @@ export default function StationManage() {
return (
<div style={{ padding: '8px 0px' }}>
{contextHolder}
<div style={{ padding: '0px 0px 8px' }}>
<Typography.Title level={4} style={{ margin: 0, fontSize: 'clamp(14px, 1.2vw, 18px)' }}>场站管理</Typography.Title>
</div>
<Card title="场站管理" style={{ borderRadius: 8 }}>
<Card style={{ borderRadius: 8 }}>
{!userInfo?.orgId && (
<div style={{ marginBottom: 16, display: 'flex', alignItems: 'center', gap: 16 }}>
<Select

View File

@@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';
import { Card, Tabs } from 'antd';
import { Card, Tabs, Typography } from 'antd';
import { ExclamationCircleOutlined, AlertOutlined, FormOutlined } from '@ant-design/icons';
import ErrorManagement from './ErrorManagement';
import AlarmSettingsManagement from './AlarmSettingsManagement';
@@ -28,6 +28,9 @@ export const SystemMaintenancePage = () => {
return (
<div className="p-0 pt-[8px] bg-[#f5f7fa] min-h-screen">
<div style={{ padding: '0px 0px 8px' }}>
<Typography.Title level={4} style={{ margin: 0, fontSize: 'clamp(14px, 1.2vw, 18px)' }}>系统维护</Typography.Title>
</div>
<Card className="mb-4">
<Tabs activeKey={activeSubTab} onChange={handleTabChange}>
<TabPane tab={<span><ExclamationCircleOutlined />错误管理</span>} key="error" />

View File

@@ -398,6 +398,9 @@ export default function UserRolePage() {
background: '#f5f7fa'
}}>
{contextHolder}
<div style={{ padding: '0px 0px 8px' }}>
<Typography.Title level={4} style={{ margin: 0, fontSize: 'clamp(14px, 1.2vw, 18px)' }}>用户与角色</Typography.Title>
</div>
<AntdCard style={{ borderRadius: 12, boxShadow: '0 2px 10px rgba(0,0,0,0.06)' }}>
<Tabs defaultActiveKey="user" type="card" size="large" style={{ marginBottom: 20 }}>
{/* 用户管理 */}

View File

@@ -0,0 +1,106 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
Card as AntdCard, Tag, Button, Space, Typography, Row, Col, message
} from 'antd';
import {
VideoCameraOutlined, PlayCircleOutlined, HistoryOutlined, SettingOutlined,
CameraOutlined, EyeOutlined
} from '@ant-design/icons';
const { Title, Text } = Typography;
// ======================
// 视频管理(仅 admin 可见)
// 说明:当前为前端展示页,数据使用本地 mock 视频通道。
// 接入真实视频时,「实时预览」可跳转至监控页 /monitor(VideoMonitorPage),
// 或对接 WebRTC / 流媒体服务获取实时画面与回放地址。
// ======================
const MOCK_CHANNELS = [
{ id: 'CAM-01', name: '1区组件巡检摄像头', device: '巡检机器人-01', status: 'online', location: 'A排 1-12 组件', lastUpdate: '2026-08-10 12:58' },
{ id: 'CAM-02', name: '升压站全景摄像头', device: '固定球机', status: 'online', location: '升压站', lastUpdate: '2026-08-10 12:59' },
{ id: 'CAM-03', name: '无人机航拍通道', device: '无人机-01', status: 'offline', location: '全场', lastUpdate: '2026-08-09 18:20' },
{ id: 'CAM-04', name: '汇流箱热成像', device: '巡检机器人-02', status: 'online', location: 'B排汇流箱', lastUpdate: '2026-08-10 13:01' },
{ id: 'CAM-05', name: '逆变器舱监控', device: '固定枪机', status: 'online', location: '逆变器室', lastUpdate: '2026-08-10 13:00' },
{ id: 'CAM-06', name: '组件热斑巡检通道', device: '无人机-02', status: 'offline', location: 'C排组件', lastUpdate: '2026-08-09 17:45' },
];
const VideoManagement = () => {
const navigate = useNavigate();
const [channels] = useState(MOCK_CHANNELS);
const onlineCount = channels.filter((c) => c.status === 'online').length;
const handlePreview = (ch: any) => {
if (ch.status !== 'online') {
message.warning(`${ch.name} 当前离线,无法预览`);
return;
}
// 跳转至实时监控页,可携带通道信息
navigate('/monitor', { state: { channel: ch.id, name: ch.name } });
};
return (
<div style={{ padding: '8px 0px', background: '#f5f7fa', minHeight: '100vh' }}>
<div style={{ marginBottom: 8 }}>
<Title level={4} style={{ margin: 0, fontSize: 'clamp(14px, 1.1vw, 18px)' }}>视频管理</Title>
<Text type="secondary" style={{ fontSize: 12 }}>视频通道总览与预览管理,仅超级管理员可查看 · 在线 {onlineCount}/{channels.length}</Text>
</div>
<Row gutter={[8, 8]}>
{channels.map((ch) => {
const online = ch.status === 'online';
return (
<Col xs={24} sm={12} lg={8} xl={6} key={ch.id}>
<AntdCard
bordered={false}
style={{ borderRadius: 10, boxShadow: '0 2px 8px rgba(0,0,0,0.04)', overflow: 'hidden' }}
bodyStyle={{ padding: 12 }}
>
{/* 视频快照占位 */}
<div style={{
position: 'relative', height: 120, borderRadius: 8, marginBottom: 10,
background: online
? 'linear-gradient(135deg, #0b2545 0%, #134074 100%)'
: 'linear-gradient(135deg, #2b2b2b 0%, #4a4a4a 100%)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
}}>
<CameraOutlined style={{ fontSize: 36, color: online ? 'rgba(255,255,255,0.85)' : 'rgba(255,255,255,0.35)' }} />
<div style={{
position: 'absolute', top: 8, left: 8,
padding: '0 8px', borderRadius: 10, fontSize: 10,
background: online ? 'rgba(82,196,26,0.9)' : 'rgba(0,0,0,0.45)',
color: '#fff',
}}>
{online ? '● 在线' : '○ 离线'}
</div>
<div style={{
position: 'absolute', bottom: 8, right: 8,
padding: '0 8px', borderRadius: 10, fontSize: 10,
background: 'rgba(0,0,0,0.45)', color: '#fff',
}}>{ch.id}</div>
</div>
<div style={{ fontSize: 13, fontWeight: 600, color: '#262626', marginBottom: 4 }}>{ch.name}</div>
<Space size={4} style={{ fontSize: 11, color: '#8c8c8c', marginBottom: 8 }} wrap>
<VideoCameraOutlined />
<span>{ch.device}</span>
<Tag bordered={false} style={{ margin: 0, fontSize: 10 }} color={online ? 'green' : 'default'}>{ch.location}</Tag>
</Space>
<div style={{ display: 'flex', gap: 6, marginTop: 4 }}>
<Button size="small" type="primary" icon={<PlayCircleOutlined />} style={{ flex: 1 }} onClick={() => handlePreview(ch)}>预览</Button>
<Button size="small" icon={<HistoryOutlined />} disabled={!online} onClick={() => message.info(`${ch.name} 回放功能开发中`)}>回放</Button>
<Button size="small" icon={<SettingOutlined />} onClick={() => message.info(`${ch.name} 参数设置`)} />
</div>
</AntdCard>
</Col>
);
})}
</Row>
</div>
);
};
export default VideoManagement;

View File

@@ -60,10 +60,10 @@ export default function SystemSettings() {
return (
<Layout style={{ minHeight: '100vh', overflow: 'hidden' }}>
<div style={{ padding: '4px 0px 0' }}>
{/*<div style={{ padding: '4px 0px 0' }}>
<Typography.Title level={4} style={{ margin: 0, fontSize: 'clamp(14px, 1.2vw, 18px)' }}>系统设置</Typography.Title>
<Text type="secondary" style={{ fontSize: '12px' }}>告警规则配置、用户角色、通知管理与系统参数设置</Text>
</div>
</div>*/}
<Content style={{

View File

@@ -41,6 +41,8 @@ const DeviceAccessPage = lazy(() => import('../components/SystemSetting/DeviceAc
const OnlineUserPage = lazy(() => import('../components/SystemSetting/OnlineUser'));
const DeviceManagementPage = lazy(() => import('../components/SystemSetting/DeviceManagement'));
const SystemMaintenancePage = lazy(() => import('../components/SystemSetting/SystemMaintain'));
const LogManagement = lazy(() => import('../components/SystemSetting/LogManagement'));
const VideoManagement = lazy(() => import('../components/SystemSetting/VideoManagement'));
//const ErrorManagement = lazy(() => import('../components/SystemSetting/ErrorManagement'));
//const AlarmSettingsManagement = lazy(() => import('../components/SystemSetting/AlarmSettingsManagement'));
//const WorkOrderSettingsManagement = lazy(() => import('../components/SystemSetting/WorkOrderSettingsManagement'));
@@ -70,6 +72,8 @@ export const routeMeta: Record<string, {
'/task': { title: '工单任务', roles: ['admin', 'operator'], requiresAuth: true },
'/table': { title: '报表中心', roles: ['admin', 'operator'], requiresAuth: true },
'/systemSetting': { title: '系统设置', roles: ['admin'], requiresAuth: true },
'/systemSetting/log': { title: '日志管理', roles: ['admin'], requiresAuth: true },
'/systemSetting/videoManage': { title: '视频管理', roles: ['admin'], requiresAuth: true },
'/downloads': { title: '视频下载', roles: ['admin', 'operator'], requiresAuth: true },
'/manage/users': { title: '用户管理', roles: ['admin'], requiresAuth: true },
'/manage/roles': { title: '角色管理', roles: ['admin'], requiresAuth: true },
@@ -244,6 +248,8 @@ const routes: RouteObject[] = [
{ path: 'systemMaintenance', element: <Suspense fallback={<PageFallback />}><SystemMaintenancePage /></Suspense> },
{ path: 'monitor', element: <Suspense fallback={<PageFallback />}><OnlineUserPage /></Suspense> },
{ path: 'devices', element: <Suspense fallback={<PageFallback />}><DeviceManagementPage /></Suspense> },
{ path: 'log', element: <Suspense fallback={<PageFallback />}><LogManagement /></Suspense> },
{ path: 'videoManage', element: <Suspense fallback={<PageFallback />}><VideoManagement /></Suspense> },
]
},
{