admin.html 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>Docker 镜像代理加速 - 管理面板</title>
  7. <link rel="icon" href="https://cdn.jsdelivr.net/gh/dqzboy/Blog-Image/BlogCourse/docker-proxy.png" type="image/png">
  8. <!-- 引入前端样式表 -->
  9. <link rel="stylesheet" href="style.css">
  10. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
  11. <!-- 引入Bootstrap CSS和JS -->
  12. <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
  13. <script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js"></script>
  14. <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
  15. <!-- 引入 Markdown 编辑器 -->
  16. <link rel="stylesheet" href="https://uicdn.toast.com/editor/3.0.0/toastui-editor.min.css" />
  17. <script src="https://uicdn.toast.com/editor/3.0.0/toastui-editor.min.js"></script>
  18. <script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.6/purify.min.js"></script>
  19. <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
  20. <!-- 自定义样式 -->
  21. <link rel="stylesheet" href="css/admin.css">
  22. <style>
  23. /* 管理面板特定样式 */
  24. .admin-container {
  25. display: flex;
  26. min-height: 100vh;
  27. background-color: var(--background-color);
  28. }
  29. .sidebar {
  30. width: 280px;
  31. background-color: var(--container-bg);
  32. box-shadow: var(--shadow-md);
  33. padding: 1.5rem 0;
  34. z-index: 10;
  35. transition: all 0.3s ease;
  36. }
  37. /* 文档管理新建文档徽章 */
  38. .new-badge {
  39. display: inline-block;
  40. padding: 2px 8px;
  41. background-color: #28a745;
  42. color: white;
  43. border-radius: 12px;
  44. font-size: 0.75rem;
  45. font-weight: bold;
  46. }
  47. @keyframes pulse {
  48. 0% { opacity: 1; }
  49. 50% { opacity: 0.7; }
  50. 100% { opacity: 1; }
  51. }
  52. /* 用户信息部分样式 */
  53. .user-profile {
  54. padding: 1rem 1.5rem;
  55. margin-bottom: 1.5rem;
  56. display: flex;
  57. flex-direction: column;
  58. align-items: center;
  59. position: relative;
  60. }
  61. .user-avatar {
  62. width: 70px;
  63. height: 70px;
  64. border-radius: 50%;
  65. background-color: var(--primary-light);
  66. display: flex;
  67. align-items: center;
  68. justify-content: center;
  69. font-size: 1.8rem;
  70. color: var(--primary-color);
  71. margin-bottom: 0.8rem;
  72. box-shadow: var(--shadow-sm);
  73. }
  74. .user-info {
  75. text-align: center;
  76. margin-bottom: 0.5rem;
  77. }
  78. .user-name {
  79. font-weight: 600;
  80. font-size: 1.1rem;
  81. color: var(--text-primary);
  82. margin-bottom: 0.25rem;
  83. }
  84. .user-role {
  85. color: var(--text-secondary);
  86. font-size: 0.85rem;
  87. margin-bottom: 0.5rem;
  88. }
  89. .user-actions {
  90. display: flex;
  91. gap: 0.5rem;
  92. margin-top: 0.5rem;
  93. }
  94. .user-action-btn {
  95. padding: 0.4rem 0.8rem;
  96. font-size: 0.85rem;
  97. border-radius: var(--radius-sm);
  98. background-color: rgba(61, 124, 244, 0.1);
  99. color: var(--primary-color);
  100. cursor: pointer;
  101. transition: all var(--transition-fast);
  102. }
  103. .user-action-btn:hover {
  104. background-color: var(--primary-color);
  105. color: white;
  106. }
  107. .user-action-btn.logout {
  108. background-color: rgba(255, 82, 82, 0.1);
  109. color: var(--danger-color);
  110. }
  111. .user-action-btn.logout:hover {
  112. background-color: var(--danger-color);
  113. color: white;
  114. }
  115. /* 仪表盘卡片样式 */
  116. .dashboard-grid {
  117. display: grid;
  118. grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  119. gap: 1.5rem;
  120. margin-bottom: 2rem;
  121. }
  122. .dashboard-card {
  123. background-color: var(--container-bg);
  124. border-radius: var(--radius-md);
  125. box-shadow: var(--shadow-sm);
  126. padding: 1.5rem;
  127. transition: all var(--transition-fast);
  128. border: 1px solid var(--border-light);
  129. position: relative;
  130. overflow: hidden;
  131. }
  132. .dashboard-card:hover {
  133. transform: translateY(-5px);
  134. box-shadow: var(--shadow-md);
  135. border-color: var(--primary-light);
  136. }
  137. .dashboard-card::before {
  138. content: '';
  139. position: absolute;
  140. top: 0;
  141. left: 0;
  142. width: 4px;
  143. height: 100%;
  144. background-color: var(--primary-color);
  145. }
  146. .card-icon {
  147. width: 50px;
  148. height: 50px;
  149. border-radius: 12px;
  150. background-color: rgba(61, 124, 244, 0.1);
  151. display: flex;
  152. align-items: center;
  153. justify-content: center;
  154. margin-bottom: 1rem;
  155. color: var(--primary-color);
  156. font-size: 1.4rem;
  157. }
  158. .card-title {
  159. font-size: 1.1rem;
  160. font-weight: 600;
  161. margin-bottom: 0.5rem;
  162. color: var(--text-primary);
  163. }
  164. .card-value {
  165. font-size: 1.8rem;
  166. font-weight: 700;
  167. color: var(--text-primary);
  168. margin-bottom: 0.5rem;
  169. }
  170. .card-description {
  171. color: var(--text-secondary);
  172. font-size: 0.9rem;
  173. margin-bottom: 1rem;
  174. }
  175. .card-footer {
  176. display: flex;
  177. justify-content: space-between;
  178. align-items: center;
  179. border-top: 1px solid var(--border-light);
  180. padding-top: 1rem;
  181. margin-top: 0.5rem;
  182. }
  183. .trend {
  184. display: flex;
  185. align-items: center;
  186. gap: 0.3rem;
  187. font-size: 0.9rem;
  188. }
  189. .trend.up {
  190. color: var(--success-color);
  191. }
  192. .trend.down {
  193. color: var(--danger-color);
  194. }
  195. .card-action {
  196. color: var(--primary-color);
  197. font-size: 0.9rem;
  198. font-weight: 500;
  199. cursor: pointer;
  200. }
  201. .welcome-banner {
  202. background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%);
  203. border-radius: var(--radius-lg);
  204. padding: 2rem;
  205. margin-bottom: 2rem;
  206. color: white;
  207. position: relative;
  208. overflow: hidden;
  209. box-shadow: var(--shadow-md);
  210. }
  211. .welcome-content {
  212. position: relative;
  213. z-index: 1;
  214. }
  215. .welcome-title {
  216. font-size: 1.8rem;
  217. margin-bottom: 0.5rem;
  218. font-weight: 600;
  219. }
  220. .welcome-subtitle {
  221. font-size: 1rem;
  222. margin-bottom: 1.5rem;
  223. opacity: 0.9;
  224. }
  225. .welcome-action {
  226. background-color: rgba(255, 255, 255, 0.2);
  227. color: white;
  228. border: none;
  229. padding: 0.75rem 1.5rem;
  230. border-radius: var(--radius-md);
  231. font-weight: 500;
  232. cursor: pointer;
  233. transition: all var(--transition-fast);
  234. display: inline-flex;
  235. align-items: center;
  236. gap: 0.5rem;
  237. }
  238. .welcome-action:hover {
  239. background-color: rgba(255, 255, 255, 0.3);
  240. transform: translateY(-2px);
  241. }
  242. .welcome-banner::after {
  243. content: '';
  244. position: absolute;
  245. top: -50%;
  246. right: -10%;
  247. width: 300px;
  248. height: 300px;
  249. background: rgba(255, 255, 255, 0.1);
  250. border-radius: 50%;
  251. }
  252. .welcome-banner::before {
  253. content: '';
  254. position: absolute;
  255. bottom: -30%;
  256. left: -5%;
  257. width: 200px;
  258. height: 200px;
  259. background: rgba(255, 255, 255, 0.1);
  260. border-radius: 50%;
  261. }
  262. /* 用户中心样式 */
  263. .user-center-header {
  264. display: flex;
  265. justify-content: space-between;
  266. align-items: center;
  267. margin-bottom: 2rem;
  268. }
  269. .user-center-title {
  270. font-size: 1.8rem;
  271. font-weight: 600;
  272. color: var(--text-primary);
  273. }
  274. .user-center-subtitle {
  275. color: var(--text-secondary);
  276. margin-top: 0.5rem;
  277. }
  278. .user-center-card {
  279. background-color: var(--container-bg);
  280. border-radius: var(--radius-lg);
  281. box-shadow: var(--shadow-md);
  282. padding: 2rem;
  283. margin-bottom: 2rem;
  284. }
  285. .user-center-section {
  286. margin-bottom: 2rem;
  287. }
  288. .user-center-section-title {
  289. font-size: 1.3rem;
  290. font-weight: 600;
  291. margin-bottom: 1.5rem;
  292. color: var(--text-primary);
  293. position: relative;
  294. padding-bottom: 0.75rem;
  295. }
  296. .user-center-section-title::after {
  297. content: '';
  298. position: absolute;
  299. bottom: 0;
  300. left: 0;
  301. width: 60px;
  302. height: 2px;
  303. background: var(--primary-color);
  304. border-radius: 2px;
  305. }
  306. .user-stats {
  307. display: grid;
  308. grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  309. gap: 1.5rem;
  310. margin-bottom: 2rem;
  311. }
  312. .stat-card {
  313. background-color: var(--container-bg);
  314. border-radius: var(--radius-md);
  315. box-shadow: var(--shadow-sm);
  316. padding: 1.5rem;
  317. border: 1px solid var(--border-light);
  318. text-align: center;
  319. }
  320. .stat-value {
  321. font-size: 2rem;
  322. font-weight: 700;
  323. color: var(--primary-color);
  324. margin-bottom: 0.5rem;
  325. }
  326. .stat-label {
  327. color: var(--text-secondary);
  328. font-size: 0.9rem;
  329. }
  330. .sidebar h2 {
  331. color: var(--text-primary);
  332. padding: 0 1.5rem;
  333. margin-bottom: 2rem;
  334. font-size: 1.3rem;
  335. font-weight: 600;
  336. display: flex;
  337. align-items: center;
  338. gap: 0.75rem;
  339. position: relative;
  340. }
  341. .sidebar h2::after {
  342. content: '';
  343. position: absolute;
  344. bottom: -10px;
  345. left: 1.5rem;
  346. right: 1.5rem;
  347. height: 1px;
  348. background: linear-gradient(to right, var(--primary-light), transparent);
  349. }
  350. .sidebar h2 i {
  351. color: var(--primary-color);
  352. font-size: 1.5rem;
  353. }
  354. .sidebar ul {
  355. list-style: none;
  356. padding: 0;
  357. margin: 0;
  358. }
  359. .sidebar li {
  360. padding: 1rem 1.5rem;
  361. cursor: pointer;
  362. transition: all var(--transition-fast);
  363. color: var(--text-secondary);
  364. display: flex;
  365. align-items: center;
  366. gap: 1rem;
  367. font-weight: 500;
  368. margin: 0.25rem 0;
  369. border-left: 3px solid transparent;
  370. }
  371. .sidebar li:hover {
  372. background-color: rgba(61, 124, 244, 0.08);
  373. color: var(--primary-color);
  374. }
  375. .sidebar li.active {
  376. background-color: rgba(61, 124, 244, 0.1);
  377. color: var(--primary-color);
  378. border-left: 3px solid var(--primary-color);
  379. font-weight: 600;
  380. }
  381. .sidebar li i {
  382. font-size: 1.2rem;
  383. width: 1.5rem;
  384. text-align: center;
  385. }
  386. .content-area {
  387. flex: 1;
  388. padding: 2rem;
  389. overflow-y: auto;
  390. background: linear-gradient(135deg, var(--background-color) 0%, rgba(247, 250, 255, 0.8) 100%);
  391. }
  392. .content-section {
  393. background-color: var(--container-bg);
  394. border-radius: var(--radius-lg);
  395. box-shadow: var(--shadow-md);
  396. padding: 2rem;
  397. margin-bottom: 1.5rem;
  398. display: none;
  399. transform: translateY(10px);
  400. opacity: 0;
  401. transition: transform 0.3s ease, opacity 0.3s ease;
  402. }
  403. .content-section.active {
  404. display: block;
  405. transform: translateY(0);
  406. opacity: 1;
  407. }
  408. .admin-title {
  409. color: var(--text-primary);
  410. font-size: 1.6rem;
  411. font-weight: 600;
  412. margin-bottom: 2rem;
  413. padding-bottom: 1rem;
  414. border-bottom: 1px solid var(--border-light);
  415. position: relative;
  416. }
  417. .admin-title::after {
  418. content: '';
  419. position: absolute;
  420. bottom: -1px;
  421. left: 0;
  422. width: 80px;
  423. height: 3px;
  424. background: var(--primary-color);
  425. border-radius: 2px;
  426. }
  427. .menu-label {
  428. color: var(--text-primary);
  429. font-size: 1.4rem;
  430. font-weight: 600;
  431. margin-bottom: 1.5rem;
  432. position: relative;
  433. padding-bottom: 0.5rem;
  434. }
  435. .menu-label::after {
  436. content: '';
  437. position: absolute;
  438. bottom: 0;
  439. left: 0;
  440. width: 60px;
  441. height: 2px;
  442. background: var(--primary-color);
  443. border-radius: 2px;
  444. }
  445. /* 表单元素样式 */
  446. .content-section input[type="text"],
  447. .content-section input[type="url"],
  448. .content-section input[type="password"],
  449. .content-section input[type="number"],
  450. .content-section select {
  451. width: 100%;
  452. max-width: 500px;
  453. padding: 0.9rem 1rem;
  454. border: 1px solid var(--border-color);
  455. border-radius: var(--radius-md);
  456. background-color: var(--container-bg);
  457. color: var(--text-primary);
  458. font-size: 0.95rem;
  459. margin-bottom: 1.2rem;
  460. transition: all var(--transition-fast);
  461. box-shadow: var(--shadow-sm);
  462. }
  463. .content-section input:focus,
  464. .content-section select:focus {
  465. border-color: var(--primary-color);
  466. box-shadow: 0 0 0 3px rgba(61, 124, 244, 0.2);
  467. outline: none;
  468. }
  469. .content-section button {
  470. background-color: var(--primary-color);
  471. color: white;
  472. border: none;
  473. padding: 0.9rem 1.4rem;
  474. border-radius: var(--radius-md);
  475. cursor: pointer;
  476. font-size: 0.95rem;
  477. font-weight: 500;
  478. transition: all var(--transition-fast);
  479. margin-top: 0.5rem;
  480. margin-right: 0.5rem;
  481. display: inline-flex;
  482. align-items: center;
  483. gap: 0.5rem;
  484. }
  485. .content-section button:hover {
  486. background-color: var(--primary-dark);
  487. transform: translateY(-2px);
  488. box-shadow: var(--shadow-md);
  489. }
  490. .content-section button:active {
  491. transform: translateY(0);
  492. }
  493. .content-section label {
  494. display: block;
  495. margin-bottom: 0.5rem;
  496. color: var(--text-primary);
  497. font-weight: 500;
  498. font-size: 1rem;
  499. }
  500. /* 表格样式 - 增强 Excel 效果 */
  501. .content-section table {
  502. width: 100%;
  503. border-collapse: collapse; /* 合并边框 */
  504. border-spacing: 0;
  505. margin-top: 1.5rem;
  506. margin-bottom: 2rem;
  507. border: 1px solid #ccc; /* 表格外边框 */
  508. font-size: 0.9rem; /* 稍微减小字体 */
  509. box-shadow: 0 2px 5px rgba(0,0,0,0.1); /* 轻微阴影 */
  510. }
  511. .content-section th {
  512. background-color: #f2f2f2; /* Excel 灰色表头背景 */
  513. color: #333;
  514. font-weight: bold; /* 加粗 */
  515. text-align: left;
  516. padding: 0.6rem 0.8rem; /* 调整内边距 */
  517. border: 1px solid #ccc; /* 单元格边框 */
  518. }
  519. .content-section td {
  520. padding: 0.6rem 0.8rem; /* 调整内边距 */
  521. border: 1px solid #ddd; /* 单元格边框,比表头稍浅 */
  522. vertical-align: middle;
  523. background-color: #ffffff;
  524. color: #444; /* 单元格文字颜色 */
  525. word-break: break-word; /* 防止长 ID 撑开单元格 */
  526. }
  527. /* 可选:添加斑马纹效果 */
  528. /*
  529. .content-section tr:nth-child(even) td {
  530. background-color: #f9f9f9;
  531. }
  532. */
  533. .content-section tr:hover td {
  534. background-color: #e9f5ff; /* 鼠标悬停高亮 */
  535. }
  536. /* 表格头部悬停效果 */
  537. .content-section th:hover {
  538. background-color: #e8e8e8;
  539. }
  540. /* Excel风格的表格滚动条 */
  541. .table-container {
  542. overflow-x: auto;
  543. max-width: 100%;
  544. margin-bottom: 1.5rem;
  545. border: 1px solid #e0e0e0;
  546. border-radius: 4px;
  547. }
  548. /* 操作列样式 */
  549. .action-cell {
  550. text-align: center;
  551. min-width: 120px;
  552. }
  553. /* 状态列样式 */
  554. .status-running {
  555. background-color: #28a745;
  556. color: white;
  557. padding: 0.3rem 0.6rem;
  558. border-radius: 4px;
  559. font-size: 0.8rem;
  560. display: inline-block;
  561. text-align: center;
  562. }
  563. .status-stopped, .status-exited {
  564. background-color: #dc3545;
  565. color: white;
  566. padding: 0.3rem 0.6rem;
  567. border-radius: 4px;
  568. font-size: 0.8rem;
  569. display: inline-block;
  570. text-align: center;
  571. }
  572. .status-created {
  573. background-color: #17a2b8;
  574. color: white;
  575. padding: 0.3rem 0.6rem;
  576. border-radius: 4px;
  577. font-size: 0.8rem;
  578. display: inline-block;
  579. text-align: center;
  580. }
  581. .status-paused {
  582. background-color: #ffc107;
  583. color: #212529;
  584. padding: 0.3rem 0.6rem;
  585. border-radius: 4px;
  586. font-size: 0.8rem;
  587. display: inline-block;
  588. text-align: center;
  589. }
  590. .status-unknown {
  591. background-color: #6c757d;
  592. color: white;
  593. padding: 0.3rem 0.6rem;
  594. border-radius: 4px;
  595. font-size: 0.8rem;
  596. display: inline-block;
  597. text-align: center;
  598. }
  599. /* 下拉菜单样式 */
  600. .action-dropdown .dropdown-toggle {
  601. border-color: #d0d0d0;
  602. background-color: #fff;
  603. color: #333;
  604. font-size: 0.9rem;
  605. padding: 0.4rem 0.8rem;
  606. }
  607. .action-dropdown .dropdown-toggle:hover,
  608. .action-dropdown .dropdown-toggle:focus {
  609. background-color: #f0f0f0;
  610. box-shadow: none;
  611. }
  612. .action-dropdown .dropdown-menu {
  613. min-width: 150px;
  614. padding: 0.3rem 0;
  615. border: 1px solid #d0d0d0;
  616. border-radius: 4px;
  617. box-shadow: 0 3px 6px rgba(0,0,0,0.1);
  618. font-size: 0.9rem;
  619. }
  620. .action-dropdown .dropdown-item {
  621. padding: 0.5rem 1rem;
  622. color: #333;
  623. }
  624. .action-dropdown .dropdown-item:hover {
  625. background-color: #f0f7ff;
  626. }
  627. .action-dropdown .dropdown-item i {
  628. width: 1rem;
  629. text-align: center;
  630. margin-right: 0.5rem;
  631. }
  632. .action-dropdown .text-danger {
  633. color: #dc3545 !important;
  634. }
  635. .action-dropdown .dropdown-divider {
  636. margin: 0.3rem 0;
  637. }
  638. /* 登录模态框 */
  639. .login-modal {
  640. display: flex;
  641. position: fixed;
  642. z-index: 1000;
  643. left: 0;
  644. top: 0;
  645. width: 100%;
  646. height: 100%;
  647. overflow: auto;
  648. background: url('/images/login-bg.jpg') center/cover no-repeat;
  649. justify-content: center;
  650. align-items: center;
  651. animation: fadeIn 0.5s ease-in-out;
  652. }
  653. .login-modal::before {
  654. content: '';
  655. position: absolute;
  656. top: 0;
  657. left: 0;
  658. width: 100%;
  659. height: 100%;
  660. background: linear-gradient(135deg, rgba(26, 35, 126, 0.85) 0%, rgba(61, 124, 244, 0.85) 100%);
  661. backdrop-filter: blur(5px);
  662. }
  663. .login-content {
  664. background-color: var(--container-bg);
  665. padding: 2.5rem;
  666. border-radius: var(--radius-lg);
  667. width: 380px;
  668. box-shadow: var(--shadow-lg);
  669. position: relative;
  670. animation: slideUp 0.5s ease-out;
  671. border: 1px solid rgba(255, 255, 255, 0.2);
  672. }
  673. @keyframes fadeIn {
  674. from { opacity: 0; }
  675. to { opacity: 1; }
  676. }
  677. @keyframes slideUp {
  678. from { transform: translateY(30px); opacity: 0; }
  679. to { transform: translateY(0); opacity: 1; }
  680. }
  681. .login-header h2 {
  682. color: var(--text-primary);
  683. font-size: 1.8rem;
  684. margin-bottom: 2rem;
  685. text-align: center;
  686. font-weight: 600;
  687. position: relative;
  688. padding-bottom: 1rem;
  689. }
  690. .login-header h2::after {
  691. content: '';
  692. position: absolute;
  693. bottom: 0;
  694. left: 50%;
  695. transform: translateX(-50%);
  696. width: 50px;
  697. height: 2px;
  698. background: var(--primary-color);
  699. }
  700. .login-form input[type="text"],
  701. .login-form input[type="password"] {
  702. width: 100%;
  703. padding: 1rem 1.2rem;
  704. margin: 0.75rem 0;
  705. border: 1px solid var(--border-color);
  706. border-radius: var(--radius-md);
  707. box-sizing: border-box;
  708. font-size: 1rem;
  709. background-color: var (--container-bg);
  710. color: var(--text-primary);
  711. transition: all var(--transition-fast);
  712. box-shadow: var(--shadow-sm);
  713. }
  714. .login-form input:focus {
  715. border-color: var(--primary-color);
  716. box-shadow: 0 0 0 3px rgba(61, 124, 244, 0.2);
  717. outline: none;
  718. }
  719. .login-form button {
  720. width: 100%;
  721. padding: 1rem;
  722. background-color: var(--primary-color);
  723. color: white;
  724. border: none;
  725. border-radius: var (--radius-md);
  726. cursor: pointer;
  727. font-size: 1.1rem;
  728. font-weight: 500;
  729. margin-top: 1.8rem;
  730. transition: all var(--transition-fast);
  731. display: flex;
  732. align-items: center;
  733. justify-content: center;
  734. gap: 0.5rem;
  735. }
  736. .login-form button:hover {
  737. background-color: var(--primary-dark);
  738. transform: translateY(-2px);
  739. box-shadow: var(--shadow-md);
  740. }
  741. .login-form button:active {
  742. transform: translateY(0);
  743. }
  744. .login-form button:before {
  745. content: "\f2f6";
  746. font-family: "Font Awesome 6 Free";
  747. font-weight: 900;
  748. }
  749. .captcha-container {
  750. display: flex;
  751. align-items: center;
  752. margin: 0.75rem 0;
  753. gap: 0.75rem;
  754. }
  755. .captcha-container input {
  756. flex: 1;
  757. }
  758. .captcha-container span {
  759. padding: 0.85rem;
  760. background-color: var(--background-color);
  761. border: 1px solid var(--border-color);
  762. border-radius: var(--radius-md);
  763. cursor: pointer;
  764. font-size: 0.95rem;
  765. color: var(--text-secondary);
  766. transition: all var(--transition-fast);
  767. display: flex;
  768. align-items: center;
  769. justify-content: center;
  770. }
  771. .captcha-container span:hover {
  772. background-color: var(--border-light);
  773. color: var(--primary-color);
  774. }
  775. /* 加载指示器 */
  776. .loading-spinner {
  777. position: fixed;
  778. top: 50%;
  779. left: 50%;
  780. transform: translate(-50%, -50%);
  781. width: 50px;
  782. height: 50px;
  783. border: 5px solid rgba(61, 124, 244, 0.1);
  784. border-top: 5px solid var(--primary-color);
  785. border-radius: 50%;
  786. animation: spin 1s linear infinite;
  787. z-index: 9999;
  788. }
  789. @keyframes spin {
  790. 0% { transform: translate(-50%, -50%) rotate(0deg); }
  791. 100% { transform: translate(-50%, -50%) rotate(360deg); }
  792. }
  793. #loadingIndicator {
  794. display: flex;
  795. justify-content: center;
  796. align-items: center;
  797. height: 100vh;
  798. background-color: var(--background-color);
  799. color: var(--text-secondary);
  800. font-size: 1.1rem;
  801. }
  802. /* 状态指示样式 */
  803. .disabled {
  804. opacity: 0.5;
  805. pointer-events: none;
  806. }
  807. .status-cell {
  808. position: relative;
  809. min-height: 24px;
  810. }
  811. .status-content {
  812. position: absolute;
  813. top: 50%;
  814. left: 50%;
  815. transform: translate(-50%, -50%);
  816. width: 100%;
  817. text-align: center;
  818. }
  819. .loading-container {
  820. display: flex;
  821. justify-content: center;
  822. align-items: center;
  823. position: absolute;
  824. top: 0;
  825. left: 0;
  826. right: 0;
  827. bottom: 0;
  828. }
  829. .loading {
  830. display: inline-block;
  831. width: 20px;
  832. height: 20px;
  833. border: 3px solid rgba(61, 124, 244, 0.1);
  834. border-radius: 50%;
  835. border-top: 3px solid var(--primary-color);
  836. animation: spin 1s linear infinite;
  837. }
  838. /* 监控配置部分 */
  839. .monitoring-status {
  840. margin-bottom: 1.5rem;
  841. font-size: 1rem;
  842. color: var(--text-secondary);
  843. background-color: rgba(61, 124, 244, 0.05);
  844. padding: 1rem 1.5rem;
  845. border-radius: var(--radius-md);
  846. display: flex;
  847. align-items: center;
  848. gap: 0.75rem;
  849. }
  850. .monitoring-status:before {
  851. content: "\f021";
  852. font-family: "Font Awesome 6 Free";
  853. font-weight: 900;
  854. color: var(--primary-color);
  855. }
  856. .status-indicator {
  857. font-weight: 600;
  858. }
  859. .config-form {
  860. background-color: var (--container-bg);
  861. padding: 1.8rem;
  862. border-radius: var(--radius-md);
  863. margin-bottom: 1.5rem;
  864. border: 1px solid var(--border-light);
  865. box-shadow: var(--shadow-sm);
  866. }
  867. .form-group {
  868. margin-bottom: 1.2rem;
  869. }
  870. .form-control {
  871. width: 100%;
  872. padding: 0.9rem 1rem;
  873. border: 1px solid var(--border-color);
  874. border-radius: var(--radius-md);
  875. color: var(--text-primary);
  876. background-color: var(--container-bg);
  877. }
  878. .button-group {
  879. display: flex;
  880. gap: 0.75rem;
  881. flex-wrap: wrap;
  882. margin-top: 1.5rem;
  883. }
  884. .btn {
  885. padding: 0.8rem 1.2rem;
  886. border: none;
  887. border-radius: var(--radius-md);
  888. cursor: pointer;
  889. transition: all var(--transition-fast);
  890. font-size: 0.95rem;
  891. font-weight: 500;
  892. display: inline-flex;
  893. align-items: center;
  894. gap: 0.5rem;
  895. }
  896. .btn-primary {
  897. background-color: var(--primary-color);
  898. color: white;
  899. }
  900. /* Fix 4: Remove icon from user center buttons */
  901. .user-center-header .btn-primary,
  902. .user-center-section .btn-primary {
  903. background-color: var(--primary-color);
  904. color: white;
  905. padding: 0.8rem 1.2rem;
  906. border-radius: var(--radius-md);
  907. cursor: pointer;
  908. transition: all var(--transition-fast);
  909. font-size: 0.95rem;
  910. font-weight: 500;
  911. display: inline-block; /* Change to inline-block */
  912. }
  913. /* Remove the :before pseudo-element for these specific buttons */
  914. .user-center-header .btn-primary:before,
  915. .user-center-section .btn-primary:before {
  916. display: none;
  917. }
  918. .btn-secondary {
  919. background-color: var(--text-secondary);
  920. color: white;
  921. }
  922. .btn:hover {
  923. transform: translateY(-2px);
  924. box-shadow: var(--shadow-sm);
  925. }
  926. .btn:active {
  927. transform: translateY(0);
  928. }
  929. .section-title {
  930. color: var(--text-primary);
  931. font-size: 1.3rem;
  932. font-weight: 600;
  933. margin: 2rem 0 1.2rem;
  934. position: relative;
  935. padding-left: 1rem;
  936. border-left: 3px solid var(--primary-color);
  937. }
  938. .container-table {
  939. width: 100%;
  940. border-collapse: separate;
  941. border-spacing: 0;
  942. margin-bottom: 1.5rem;
  943. border-radius: var(--radius-md);
  944. overflow: hidden;
  945. box-shadow: var(--shadow-sm);
  946. }
  947. .container-table th {
  948. background-color: var(--primary-color);
  949. color: white;
  950. padding: 0.9rem 1rem;
  951. text-align: left;
  952. font-weight: 500;
  953. }
  954. .container-table td {
  955. padding: 0.9rem 1rem;
  956. border-bottom: 1px solid var(--border-light);
  957. }
  958. .container-table tr:last-child td {
  959. border-bottom: none;
  960. }
  961. .error-message {
  962. color: var(--danger-color);
  963. margin-top: 0.75rem;
  964. padding: 0.75rem;
  965. border-radius: var (--radius-md);;
  966. background-color: rgba(255, 82, 82, 0.1);
  967. font-size: 0.9rem;
  968. display: flex;
  969. align-items: center;
  970. gap: 0.5rem;
  971. }
  972. .error-message:before {
  973. content: "\f071";
  974. font-family: "Font Awesome 6 Free";
  975. font-weight: 900;
  976. }
  977. .success-message {
  978. color: var (--success-color);
  979. margin-top: 0.75rem;
  980. padding: 0.75rem;
  981. border-radius: var (--radius-md);
  982. background-color: rgba(50, 213, 131, 0.1);
  983. font-size: 0.9rem;
  984. display: flex;
  985. align-items: center;
  986. gap: 0.5rem;
  987. }
  988. .success-message:before {
  989. content: "\f00c";
  990. font-family: "Font Awesome 6 Free";
  991. font-weight: 900;
  992. }
  993. /* 网络测试部分 */
  994. #network-test .input-group {
  995. margin-bottom: 1.2rem;
  996. max-width: 600px;
  997. background-color: rgba(61, 124, 244, 0.03);
  998. padding: 1.5rem;
  999. border-radius: var(--radius-md);
  1000. box-shadow: var(--shadow-sm);
  1001. }
  1002. #network-test label {
  1003. display: block;
  1004. margin-bottom: 0.5rem;
  1005. color: var(--text-secondary);
  1006. font-weight: 500;
  1007. }
  1008. #testResults {
  1009. margin-top: 1.5rem;
  1010. padding: 1rem;
  1011. background-color: #1e2532;
  1012. color: #e9ecef;
  1013. border-radius: var(--radius-md);
  1014. white-space: pre-wrap;
  1015. font-family: 'JetBrains Mono', monospace;
  1016. min-height: 300px;
  1017. max-height: 500px;
  1018. overflow-y: auto;
  1019. border: none;
  1020. box-shadow: var(--shadow-md);
  1021. position: relative;
  1022. }
  1023. /* 文档编辑器部分 */
  1024. #editorContainer {
  1025. margin-top: 2rem;
  1026. border-radius: var(--radius-md);
  1027. background-color: var(--container-bg);
  1028. overflow: hidden;
  1029. box-shadow: var(--shadow-md);
  1030. border: 1px solid var(--border-light);
  1031. }
  1032. #documentTitle {
  1033. width: 100%;
  1034. padding: 1rem;
  1035. margin-bottom: 1rem;
  1036. border: 1px solid var(--border-color);
  1037. border-radius: var (--radius-md);;
  1038. font-size: 1.1rem;
  1039. font-weight: 500;
  1040. box-shadow: var(--shadow-sm);
  1041. }
  1042. .password-hint {
  1043. color: var(--text-secondary);
  1044. font-size: 0.9rem;
  1045. margin: 0.5rem 0 1rem;
  1046. display: block;
  1047. background-color: rgba(61, 124, 244, 0.05);
  1048. padding: 0.75rem;
  1049. border-radius: var(--radius-md);
  1050. }
  1051. #passwordStrength {
  1052. font-size: 0.9rem;
  1053. margin: 0.5rem 0;
  1054. display: block;
  1055. }
  1056. /* 警告和确认对话框样式 */
  1057. .swal2-popup {
  1058. font-size: 1rem !important;
  1059. border-radius: var(--radius-lg) !important;
  1060. }
  1061. .swal2-title {
  1062. font-weight: 600 !important;
  1063. color: var(--text-primary) !important;
  1064. }
  1065. .swal2-styled.swal2-confirm {
  1066. background-color: var(--primary-color) !important;
  1067. border-radius: var(--radius-md) !important;
  1068. }
  1069. /* 添加条目按钮 */
  1070. .add-btn {
  1071. background-color: var(--success-color);
  1072. color: white;
  1073. padding: 0.8rem 1.2rem;
  1074. border-radius: var (--radius-md);
  1075. display: inline-flex;
  1076. align-items: center;
  1077. gap: 0.5rem;
  1078. font-weight: 500;
  1079. margin-top: 1rem;
  1080. box-shadow: var(--shadow-sm);
  1081. }
  1082. .add-btn:before {
  1083. content: "";
  1084. /* 移除 Font Awesome 图标,以避免和 HTML 中的图标重复 */
  1085. }
  1086. .add-btn:hover {
  1087. background-color: #29b873;
  1088. transform: translateY(-2px);
  1089. box-shadow: var(--shadow-md);
  1090. }
  1091. /* Docker容器操作按钮样式 */
  1092. .action-cell {
  1093. min-width: 200px;
  1094. }
  1095. .action-buttons {
  1096. display: flex;
  1097. flex-wrap: wrap;
  1098. gap: 4px;
  1099. }
  1100. .action-buttons button {
  1101. padding: 3px 8px;
  1102. font-size: 0.8rem;
  1103. margin-right: 3px;
  1104. white-space: nowrap;
  1105. }
  1106. .action-buttons button i {
  1107. margin-right: 3px;
  1108. font-size: 0.75rem;
  1109. }
  1110. /* 改善按钮在小屏幕上的显示 */
  1111. @media (max-width: 768px) {
  1112. .action-buttons {
  1113. flex-direction: column;
  1114. }
  1115. .action-buttons button {
  1116. margin-bottom: 3px;
  1117. width: 100%;
  1118. }
  1119. }
  1120. /* 响应式设计 */
  1121. @media (max-width: 992px) {
  1122. .admin-container {
  1123. flex-direction: column;
  1124. }
  1125. .sidebar {
  1126. width: 100%;
  1127. padding: 1rem 0;
  1128. }
  1129. .sidebar ul {
  1130. display: flex;
  1131. flex-wrap: wrap;
  1132. justify-content: flex-start;
  1133. }
  1134. .sidebar li {
  1135. padding: 0.5rem 1rem;
  1136. border-left: none;
  1137. border-bottom: 3px solid transparent;
  1138. }
  1139. .sidebar li.active {
  1140. border-left: none;
  1141. border-bottom: 3px solid var(--primary-color);
  1142. }
  1143. .content-area {
  1144. padding: 1rem;
  1145. }
  1146. .content-section {
  1147. padding: 1.5rem;
  1148. }
  1149. }
  1150. @media (max-width: 768px) {
  1151. .content-section th,
  1152. .content-section td {
  1153. padding: 0.75rem 0.5rem;
  1154. font-size: 0.9rem;
  1155. }
  1156. .button-group {
  1157. flex-direction: column;
  1158. }
  1159. .btn, .action-btn {
  1160. width: 100%;
  1161. margin-bottom: 0.5rem;
  1162. }
  1163. .action-buttons {
  1164. flex-direction: column;
  1165. }
  1166. .action-buttons button {
  1167. margin-bottom: 3px;
  1168. width: 100%;
  1169. }
  1170. }
  1171. @media (max-width: 480px) {
  1172. .login-content {
  1173. width: 90%;
  1174. padding: 1.5rem;
  1175. }
  1176. }
  1177. /* Excel风格表格 */
  1178. .excel-table {
  1179. border-collapse: collapse;
  1180. width: 100%;
  1181. font-size: 14px;
  1182. border: 1px solid #ccc;
  1183. }
  1184. .excel-table th {
  1185. background-color: #f2f2f2;
  1186. border: 1px solid #ccc;
  1187. padding: 8px;
  1188. text-align: center;
  1189. position: sticky;
  1190. top: 0;
  1191. z-index: 10;
  1192. }
  1193. .excel-table td {
  1194. border: 1px solid #ccc;
  1195. padding: 6px 8px;
  1196. text-align: left;
  1197. }
  1198. .excel-table tr:nth-child(even) {
  1199. background-color: #f9f9f9;
  1200. }
  1201. .excel-table tr:hover {
  1202. background-color: #f0f7ff;
  1203. }
  1204. /* 容器更新样式 */
  1205. .update-progress {
  1206. padding: 1rem;
  1207. text-align: center;
  1208. }
  1209. .update-progress p {
  1210. margin-bottom: 1rem;
  1211. font-size: 1rem;
  1212. }
  1213. .update-progress strong {
  1214. color: #007bff;
  1215. font-weight: 600;
  1216. }
  1217. .progress-status {
  1218. margin: 1rem 0;
  1219. font-size: 0.9rem;
  1220. color: #555;
  1221. font-weight: 500;
  1222. }
  1223. .progress-container {
  1224. width: 100%;
  1225. height: 10px;
  1226. background-color: #f0f0f0;
  1227. border-radius: 5px;
  1228. overflow: hidden;
  1229. margin: 1rem 0;
  1230. }
  1231. .progress-bar {
  1232. height: 100%;
  1233. background-color: #4CAF50;
  1234. width: 0%;
  1235. transition: width 0.3s ease-in-out;
  1236. border-radius: 5px;
  1237. position: relative;
  1238. }
  1239. /* 修改SweetAlert样式 */
  1240. .swal2-popup.update-popup {
  1241. border-radius: 10px;
  1242. padding: 1.5rem;
  1243. box-shadow: 0 4px 20px rgba(0,0,0,0.15);
  1244. }
  1245. .swal2-title.update-title {
  1246. font-size: 1.5rem;
  1247. font-weight: 600;
  1248. color: #333;
  1249. }
  1250. .swal2-input.update-input {
  1251. box-shadow: none;
  1252. border: 1px solid #ddd;
  1253. border-radius: 5px;
  1254. padding: 0.75rem;
  1255. font-size: 0.95rem;
  1256. }
  1257. .swal2-input.update-input:focus {
  1258. border-color: #3085d6;
  1259. box-shadow: 0 0 0 3px rgba(48, 133, 214, 0.2);
  1260. }
  1261. /* 容器日志样式 */
  1262. .container-logs {
  1263. max-height: 70vh;
  1264. overflow-y: auto;
  1265. background: #1e1e1e;
  1266. color: #f0f0f0;
  1267. padding: 1rem;
  1268. border-radius: 5px;
  1269. font-family: 'Courier New', monospace;
  1270. font-size: 0.9rem;
  1271. white-space: pre-wrap;
  1272. word-break: break-all;
  1273. }
  1274. .swal2-logs-container {
  1275. max-width: 100%;
  1276. padding: 0 !important;
  1277. }
  1278. .swal2-logs-popup {
  1279. max-width: 80% !important;
  1280. }
  1281. /* 容器状态标签样式 */
  1282. .badge.status-running {
  1283. background-color: #28a745;
  1284. color: white;
  1285. padding: 5px 8px;
  1286. border-radius: 4px;
  1287. display: inline-block;
  1288. min-width: 70px;
  1289. text-align: center;
  1290. }
  1291. .badge.status-stopped, .badge.status-exited {
  1292. background-color: #dc3545;
  1293. color: white;
  1294. padding: 5px 8px;
  1295. border-radius: 4px;
  1296. display: inline-block;
  1297. min-width: 70px;
  1298. text-align: center;
  1299. }
  1300. .badge.status-created {
  1301. background-color: #17a2b8;
  1302. color: white;
  1303. padding: 5px 8px;
  1304. border-radius: 4px;
  1305. display: inline-block;
  1306. min-width: 70px;
  1307. text-align: center;
  1308. }
  1309. .badge.status-unknown, .badge.status-paused {
  1310. background-color: #6c757d;
  1311. color: white;
  1312. padding: 5px 8px;
  1313. border-radius: 4px;
  1314. display: inline-block;
  1315. min-width: 70px;
  1316. text-align: center;
  1317. }
  1318. /* 容器表格标题样式 */
  1319. .docker-table-header {
  1320. display: flex;
  1321. justify-content: space-between;
  1322. align-items: center;
  1323. margin-bottom: 15px;
  1324. padding-bottom: 10px;
  1325. border-bottom: 1px solid #eee;
  1326. }
  1327. .docker-table-title {
  1328. font-size: 18px;
  1329. font-weight: 600;
  1330. color: #333;
  1331. margin: 0;
  1332. }
  1333. </style>
  1334. </head>
  1335. <body>
  1336. <div id="loadingSpinner" class="loading-spinner" style="display: none;"></div>
  1337. <div id="loadingIndicator" style="display: flex; justify-content: center; align-items: center; height: 100vh;">
  1338. <p>加载中...</p>
  1339. </div>
  1340. <div class="admin-container" id="adminContainer" style="display: none;">
  1341. <div class="sidebar">
  1342. <div class="user-profile">
  1343. <div class="user-avatar">
  1344. <i class="fas fa-user"></i>
  1345. </div>
  1346. <div class="user-info">
  1347. <div class="user-name" id="currentUsername">管理员</div>
  1348. <div class="user-role">系统管理员</div>
  1349. </div>
  1350. <div class="user-actions">
  1351. <div class="user-action-btn" id="userCenterBtn">个人中心</div>
  1352. <div class="user-action-btn logout" id="logoutBtn">退出登录</div>
  1353. </div>
  1354. </div>
  1355. <h2><i class="fas fa-cogs"></i>管理面板</h2>
  1356. <ul class="sidebar-nav">
  1357. <li data-section="dashboard" class="active">
  1358. <i class="fas fa-tachometer-alt"></i>控制面板
  1359. </li>
  1360. <li data-section="basic-config">
  1361. <i class="fas fa-wrench"></i>基本配置
  1362. </li>
  1363. <li data-section="menu-management">
  1364. <i class="fas fa-bars"></i>菜单管理
  1365. </li>
  1366. <li data-section="documentation-management">
  1367. <i class="fas fa-file-alt"></i>文档管理
  1368. </li>
  1369. <li data-section="network-test">
  1370. <i class="fas fa-network-wired"></i>网络测试
  1371. </li>
  1372. <li data-section="docker-status">
  1373. <i class="fab fa-docker"></i>容器管理
  1374. </li>
  1375. <li data-section="docker-monitoring">
  1376. <i class="fas fa-chart-line"></i>容器监控
  1377. </li>
  1378. <li data-section="user-center">
  1379. <i class="fas fa-user-cog"></i>用户中心
  1380. </li>
  1381. </ul>
  1382. </div>
  1383. <div class="content-area">
  1384. <div class="container">
  1385. <!-- 控制面板 -->
  1386. <div id="dashboard" class="content-section active">
  1387. <div class="welcome-banner">
  1388. <div class="welcome-content">
  1389. <h1 class="welcome-title">欢迎回来,<span id="welcomeUsername">管理员</span>!</h1>
  1390. <p class="welcome-subtitle">这里是 Docker 镜像代理加速系统控制面板</p>
  1391. <button class="welcome-action" id="refreshSystemBtn">
  1392. <i class="fas fa-sync-alt"></i> 刷新系统状态
  1393. </button>
  1394. </div>
  1395. <div class="docker-status" id="dockerStatusIndicator" style="position: absolute; top: 10px; right: 10px; padding: 5px 10px; border-radius: 4px; font-size: 0.9rem; color: white;">
  1396. <i class="fab fa-docker"></i>
  1397. <span id="dockerStatusText">正在检查...</span>
  1398. </div>
  1399. </div>
  1400. <div class="dashboard-grid">
  1401. <!-- 仪表板卡片将由 systemStatus.initDashboard() 动态生成 -->
  1402. </div>
  1403. <div class="dashboard-card">
  1404. <h3 class="card-title">最近容器操作</h3>
  1405. <table id="recentActivitiesTable">
  1406. <!-- 活动表内容将由 systemStatus.refreshSystemStatus() 动态更新 -->
  1407. </table>
  1408. </div>
  1409. </div>
  1410. <!-- 基本配置 -->
  1411. <div id="basic-config" class="content-section">
  1412. <h1 class="admin-title">基本配置</h1>
  1413. <div class="config-form">
  1414. <div class="form-group">
  1415. <label for="logoUrl">Logo URL: (可选)</label>
  1416. <input type="url" id="logoUrl" name="logoUrl" class="form-control">
  1417. </div>
  1418. <button type="button" class="btn btn-primary" onclick="saveConfig({logo: document.getElementById('logoUrl').value})">保存 Logo</button>
  1419. <div class="form-group" style="margin-top: 2rem;">
  1420. <label for="proxyDomain">Docker镜像代理地址: (必填)</label>
  1421. <input type="text" id="proxyDomain" name="proxyDomain" class="form-control" required>
  1422. </div>
  1423. <button type="button" class="btn btn-primary" onclick="saveConfig({proxyDomain: document.getElementById('proxyDomain').value})">保存代理地址</button>
  1424. </div>
  1425. </div>
  1426. <!-- 菜单管理 -->
  1427. <div id="menu-management" class="content-section">
  1428. <h2 class="menu-label">菜单项管理</h2>
  1429. <table id="menuTable">
  1430. <thead>
  1431. <!-- 表头将由 menuManager.renderMenuItems() 动态生成 -->
  1432. </thead>
  1433. <tbody id="menuTableBody">
  1434. <!-- 菜单项将在这里动态添加 -->
  1435. </tbody>
  1436. </table>
  1437. <button type="button" class="add-btn" onclick="menuManager.showNewMenuItemRow()">
  1438. <i class="fas fa-plus"></i> 添加菜单项
  1439. </button>
  1440. </div>
  1441. <!-- 文档管理部分 -->
  1442. <div id="documentation-management" class="content-section">
  1443. <h2 class="menu-label">文档管理</h2>
  1444. <div class="action-bar" style="margin-bottom: 15px;">
  1445. <button type="button" class="btn btn-primary" onclick="documentManager.newDocument()">
  1446. <i class="fas fa-plus"></i> 新建文档
  1447. </button>
  1448. </div>
  1449. <table id="documentTable">
  1450. <thead>
  1451. <!-- 表头将由 documentManager.renderDocumentList() 动态生成 -->
  1452. </thead>
  1453. <tbody id="documentTableBody">
  1454. <!-- 文档列表将在这里动态添加 -->
  1455. </tbody>
  1456. </table>
  1457. <div id="editorContainer" style="display: none;">
  1458. <input type="text" id="documentTitle" placeholder="请输入文档标题" autocomplete="off">
  1459. <div id="editor">
  1460. <!-- 编辑器将在这里初始化 -->
  1461. </div>
  1462. <div class="editor-actions">
  1463. <button type="button" class="btn btn-secondary" onclick="documentManager.cancelEdit()">取消</button>
  1464. <button type="button" class="btn btn-primary" onclick="documentManager.saveDocument()">保存文档</button>
  1465. </div>
  1466. </div>
  1467. </div>
  1468. <!-- 修改密码部分 -->
  1469. <div id="password-change" class="content-section">
  1470. <h2 class="menu-label">修改密码</h2>
  1471. <label for="currentPassword">当前密码</label>
  1472. <input type="password" id="currentPassword" name="currentPassword">
  1473. <label for="newPassword">新密码</label>
  1474. <span class="password-hint" id="passwordHint">密码必须包含至少一个字母、一个数字和一个特殊字符,长度在8到16个字符之间</span>
  1475. <input type="password" id="newPassword" name="newPassword" oninput="userCenter.checkPasswordStrength()">
  1476. <span id="passwordStrength" style="color: red;"></span>
  1477. <button type="button" onclick="userCenter.changePassword()">修改密码</button>
  1478. </div>
  1479. <!-- 网络测试 -->
  1480. <div id="network-test" class="content-section">
  1481. <h1 class="admin-title">网络测试</h1>
  1482. <div class="input-group">
  1483. <label for="domainSelect">目标域名:</label>
  1484. <select id="domainSelect" class="form-control">
  1485. <!-- 选项将由 networkTest.initNetworkTest() 动态生成 -->
  1486. </select>
  1487. </div>
  1488. <div class="input-group">
  1489. <label for="testType">测试类型:</label>
  1490. <select id="testType">
  1491. <!-- 选项将由 networkTest.initNetworkTest() 动态生成 -->
  1492. </select>
  1493. </div>
  1494. <button class="btn btn-primary">开始测试</button>
  1495. <div id="testResults" style="margin-top: 20px; white-space: pre-wrap; font-family: monospace;"></div>
  1496. </div>
  1497. <!-- Docker服务状态 -->
  1498. <div id="docker-status" class="content-section">
  1499. <div class="section-heading">
  1500. <h2 class="section-title">Docker 服务状态</h2>
  1501. <div class="section-description">
  1502. 查看和管理Docker容器状态和操作
  1503. </div>
  1504. </div>
  1505. <!-- 新增表格容器以支持标题和操作区域 -->
  1506. <div id="dockerTableContainer" class="table-responsive">
  1507. <table id="dockerStatusTable" class="excel-table">
  1508. <thead></thead>
  1509. <tbody id="dockerStatusTableBody"></tbody>
  1510. </table>
  1511. </div>
  1512. </div>
  1513. <!-- Docker监控配置 -->
  1514. <div id="docker-monitoring" class="content-section">
  1515. <h1 class="admin-title">Docker 容器监控</h1>
  1516. <div class="monitoring-status">
  1517. <span>监控状态:</span>
  1518. <span id="monitoringStatus" class="status-indicator">加载中...</span>
  1519. </div>
  1520. <div class="config-form">
  1521. <div class="form-group">
  1522. <label for="notificationType">通知方式:</label>
  1523. <select id="notificationType" class="form-control">
  1524. <option value="wechat">企业微信群机器人</option>
  1525. <option value="telegram">Telegram Bot</option>
  1526. </select>
  1527. </div>
  1528. <div id="wechatFields">
  1529. <div class="form-group">
  1530. <label for="webhookUrl">企业微信机器人 Webhook URL:</label>
  1531. <input type="text" id="webhookUrl" name="webhookUrl" class="form-control">
  1532. </div>
  1533. </div>
  1534. <div id="telegramFields" style="display: none;">
  1535. <div class="form-group">
  1536. <label for="telegramToken">Telegram Bot Token:</label>
  1537. <input type="text" id="telegramToken" name="telegramToken" class="form-control">
  1538. </div>
  1539. <div class="form-group">
  1540. <label for="telegramChatId">Telegram Chat ID:</label>
  1541. <input type="text" id="telegramChatId" name="telegramChatId" class="form-control">
  1542. </div>
  1543. </div>
  1544. <div class="form-group">
  1545. <label for="monitorInterval">监控间隔 (秒):</label>
  1546. <input type="number" id="monitorInterval" name="monitorInterval" min="1" value="60" class="form-control">
  1547. </div>
  1548. <div class="button-group">
  1549. <button class="btn btn-secondary" id="testNotifyBtn" onclick="app.testNotification()">测试通知</button>
  1550. <button class="btn btn-primary" id="saveConfigBtn" onclick="app.saveMonitoringConfig()">保存配置</button>
  1551. <button class="btn btn-secondary" id="toggleMonitoringBtn" onclick="app.toggleMonitoring()">开启/关闭监控</button>
  1552. </div>
  1553. </div>
  1554. <div id="messageContainer"></div>
  1555. <h2 class="section-title">已停止的容器</h2>
  1556. <div class="container-list">
  1557. <table id="stoppedContainersTable" class="container-table">
  1558. <thead>
  1559. <tr>
  1560. <th>容器 ID</th>
  1561. <th>名称</th>
  1562. <th>状态</th>
  1563. </tr>
  1564. </thead>
  1565. <tbody id="stoppedContainersBody"></tbody>
  1566. </table>
  1567. </div>
  1568. </div>
  1569. <!-- 用户中心部分 -->
  1570. <div id="user-center" class="content-section">
  1571. <div class="user-center-header">
  1572. <div>
  1573. <h1 class="user-center-title">用户中心</h1>
  1574. <p class="user-center-subtitle">管理您的个人信息和账户安全</p>
  1575. </div>
  1576. <button class="btn btn-primary" id="ucLogoutBtn" style="display: inline-block;">退出登录</button>
  1577. </div>
  1578. <div class="user-center-card">
  1579. <div class="user-center-section">
  1580. <h2 class="user-center-section-title">账户信息</h2>
  1581. <div class="user-stats">
  1582. <div class="stat-card">
  1583. <div class="stat-value" id="loginCount">--</div>
  1584. <div class="stat-label">登录次数</div>
  1585. </div>
  1586. <div class="stat-card">
  1587. <div class="stat-value" id="lastLogin">--</div>
  1588. <div class="stat-label">上次登录</div>
  1589. </div>
  1590. <div class="stat-card">
  1591. <div class="stat-value" id="accountAge">--</div>
  1592. <div class="stat-label">账户天数</div>
  1593. </div>
  1594. </div>
  1595. <!-- 移除用户详细信息部分 -->
  1596. </div>
  1597. <div class="user-center-section">
  1598. <h2 class="user-center-section-title">修改密码</h2>
  1599. <form id="changePasswordForm">
  1600. <label for="ucCurrentPassword">当前密码</label>
  1601. <input type="password" id="ucCurrentPassword" name="currentPassword">
  1602. <label for="ucNewPassword">新密码</label>
  1603. <span class="password-hint" id="ucPasswordHint">密码必须包含至少一个字母、一个数字和一个特殊字符,长度在8到16个字符之间</span>
  1604. <input type="password" id="ucNewPassword" name="newPassword" oninput="userCenter.checkUcPasswordStrength()">
  1605. <label for="ucConfirmPassword">确认新密码</label>
  1606. <input type="password" id="ucConfirmPassword" name="confirmPassword">
  1607. <span id="ucPasswordStrength" style="color: red;"></span>
  1608. <button type="submit" class="btn btn-primary">修改密码</button>
  1609. </form>
  1610. </div>
  1611. </div>
  1612. </div>
  1613. </div>
  1614. </div>
  1615. </div>
  1616. <div class="login-modal" id="loginModal" style="display: none;">
  1617. <div class="login-content">
  1618. <div class="login-header">
  1619. <h2>管理员登录</h2>
  1620. </div>
  1621. <form id="loginForm" class="login-form">
  1622. <input type="text" id="username" name="username" placeholder="用户名" required>
  1623. <input type="password" id="password" name="password" placeholder="密码" required>
  1624. <!-- 修复这里的结构问题 - 添加了缺失的开始标签 -->
  1625. <div class="captcha-container">
  1626. <input type="text" id="captcha" name="captcha" placeholder="验证码" required>
  1627. <span id="captchaText" onclick="auth.refreshCaptcha()">点击刷新验证码</span>
  1628. </div>
  1629. <button type="submit" id="loginButton">登录</button>
  1630. </form>
  1631. </div>
  1632. </div>
  1633. <script src="https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.2/dragula.min.js"></script>
  1634. <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
  1635. <!-- 添加 Bootstrap 5 JavaScript -->
  1636. <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
  1637. <!-- Toast UI Editor - 添加编辑器需要的依赖 -->
  1638. <script src="https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js"></script>
  1639. <link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor.min.css" />
  1640. <!-- 模块化JS文件 - 按依赖顺序加载 -->
  1641. <script src="js/core.js"></script>
  1642. <script src="js/auth.js"></script>
  1643. <script src="js/userCenter.js"></script>
  1644. <script src="js/menuManager.js"></script>
  1645. <script src="js/documentManager.js"></script>
  1646. <script src="js/systemStatus.js"></script>
  1647. <script src="js/dockerManager.js"></script>
  1648. <script src="js/networkTest.js"></script>
  1649. <script src="js/app.js"></script>
  1650. <script>
  1651. // 在DOM加载完成后初始化登录表单
  1652. document.addEventListener('DOMContentLoaded', function() {
  1653. const loginForm = document.getElementById('loginForm');
  1654. if (loginForm) {
  1655. loginForm.addEventListener('submit', function(e) {
  1656. e.preventDefault();
  1657. auth.login();
  1658. });
  1659. }
  1660. const changePasswordForm = document.getElementById('changePasswordForm');
  1661. if (changePasswordForm) {
  1662. changePasswordForm.addEventListener('submit', function(e) {
  1663. e.preventDefault();
  1664. userCenter.changePassword();
  1665. });
  1666. }
  1667. // 设置超时自动显示登录框,避免一直显示"加载中..."
  1668. setTimeout(function() {
  1669. const loadingIndicator = document.getElementById('loadingIndicator');
  1670. if (loadingIndicator && loadingIndicator.style.display !== 'none') {
  1671. console.log('超时自动显示登录框');
  1672. if (core && typeof core.hideLoadingIndicator === 'function') {
  1673. core.hideLoadingIndicator();
  1674. }
  1675. const loginModal = document.getElementById('loginModal');
  1676. if (loginModal) {
  1677. loginModal.style.display = 'flex';
  1678. if (window.auth && typeof window.auth.refreshCaptcha === 'function') {
  1679. window.auth.refreshCaptcha();
  1680. }
  1681. }
  1682. }
  1683. }, 3000); // 3秒后如果仍在加载,则显示登录框
  1684. // 确保saveConfig可用
  1685. window.saveConfig = window.app ? window.app.saveConfig : function(data) {
  1686. console.error('saveConfig未定义');
  1687. };
  1688. });
  1689. </script>
  1690. </body>
  1691. </html>