37创客科创中心

 找回密码
 立即注册
查看: 237|回复: 1

小学组 607

[复制链接]

194

主题

324

帖子

2399

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2399
发表于 2025-6-7 16:06:02 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
  1. 帮我用html5编写一个http请求发送器的页面,文件名为:AI赋能MQTT67.html,可以发送http请求,并显示返回内容。

  2. 把UI界面设计成带有科技感的可爱的风格,体验互动感强,可以让用户直观感受到科技的魅力,操作时加点互动音效。
  3. 标题:AI赋能MQTT,双击标题弹窗居中窗口可以进行内容修改。

  4. 配置参数输入框5个,名字后面冒号的内容为默认值:
  5. 1、服务器:i.37ck.cn
  6. 2、端口:88
  7. 3、操作:publish
  8. 4、用户名:37ck
  9. 5、密码:20200808
  10. 把界面上的服务器、端口和操作合并一行,用户名和密码也合并为一行。
  11. 增加一个配置参数开关按钮,点击按钮隐藏配置参数的服务器、端口、操作、用户名、密码各项,再次点击按钮则显示各项配置参数,默认状态为隐藏。
  12. 发送操作输入框2个,名字后面冒号的内容为默认值:
  13. 1、主题:37ck/37创客,该项为下拉框,下拉内容有:37ck/莘村中学、37ck/富教小学、37ck/文田中学。
  14. 2、指令:在线,该项为下拉框,下拉内容有:放学啦、下课啦、到安全等待区、正赶往等待车位。
  15. 发送按钮:点击发送按钮,将会发送指令以http方式发送给服务器,服务器将会执行http链接请求指令,并将结果返回到返回框。
  16. 配置参数可以导入和导出为json格式文件。

  17. 增加一个遥控按钮区域,需要有控制按钮:前进、后退、左转、右转、停止、开灯、关灯、红灯、绿灯、黄灯、喇叭,点击按钮时以按钮名称为指令发送网络请求到服务器。

  18. 把配置参数区域、遥控区域用tab标签页分开,可以更好的管理界面。

  19. 最底部为发送和返回情况区域,具体如下:
  20. 发送框:组合:http://+服务器+:+端口+/+操作+?topic=+主题+&msg=+指令+&iname=+用户名+&ipwd=+密码的链接内容。
  21. 返回框:显示http请求返回的文本内容信息。
复制代码


回复

使用道具 举报

45

主题

84

帖子

905

积分

版主

Rank: 7Rank: 7Rank: 7

积分
905
发表于 2025-6-12 15:50:47 | 显示全部楼层
  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>AI赋能MQTT</title>
  7.     <style>
  8.         :root {
  9.             --primary-color: #00a8ff;
  10.             --secondary-color: #192a56;
  11.             --accent-color: #0097e6;
  12.             --success-color: #44bd32;
  13.             --warning-color: #e1b12c;
  14.             --danger-color: #c23616;
  15.             --text-color: #dcdde1;
  16.             --bg-color: #2f3640;
  17.         }

  18.         * {
  19.             margin: 0;
  20.             padding: 0;
  21.             box-sizing: border-box;
  22.             font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  23.         }

  24.         body {
  25.             background: var(--bg-color);
  26.             color: var(--text-color);
  27.             min-height: 100vh;
  28.             padding: 20px;
  29.         }

  30.         .container {
  31.             max-width: 1200px;
  32.             margin: 0 auto;
  33.             background: rgba(0, 0, 0, 0.3);
  34.             border-radius: 15px;
  35.             padding: 20px;
  36.             box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
  37.         }

  38.         .title {
  39.             text-align: center;
  40.             font-size: 2.5em;
  41.             color: var(--primary-color);
  42.             margin-bottom: 30px;
  43.             text-shadow: 0 0 10px rgba(0, 168, 255, 0.5);
  44.             cursor: pointer;
  45.             transition: all 0.3s ease;
  46.             user-select: none;
  47.         }

  48.         .title:hover {
  49.             transform: scale(1.05);
  50.         }

  51.         .tabs {
  52.             margin-bottom: 20px;
  53.         }

  54.         .tab-buttons {
  55.             display: flex;
  56.             gap: 10px;
  57.             margin-bottom: 20px;
  58.         }

  59.         .tab-button {
  60.             padding: 10px 20px;
  61.             background: var(--secondary-color);
  62.             border: none;
  63.             border-radius: 5px;
  64.             color: var(--text-color);
  65.             cursor: pointer;
  66.             transition: all 0.3s ease;
  67.         }

  68.         .tab-button.active {
  69.             background: var(--primary-color);
  70.         }

  71.         .tab-content {
  72.             display: none;
  73.             animation: fadeIn 0.3s ease;
  74.         }

  75.         .tab-content.active {
  76.             display: block;
  77.         }

  78.         .config-section {
  79.             margin-bottom: 20px;
  80.         }

  81.         .form-row {
  82.             display: flex;
  83.             gap: 20px;
  84.             margin-bottom: 15px;
  85.             align-items: center;
  86.         }

  87.         .form-group {
  88.             flex: 1;
  89.         }

  90.         .form-group label {
  91.             display: block;
  92.             margin-bottom: 5px;
  93.             color: var(--primary-color);
  94.         }

  95.         .form-control {
  96.             width: 100%;
  97.             padding: 8px 12px;
  98.             border: 1px solid var(--secondary-color);
  99.             border-radius: 5px;
  100.             background: rgba(0, 0, 0, 0.2);
  101.             color: var(--text-color);
  102.             transition: all 0.3s ease;
  103.         }

  104.         .form-control:focus {
  105.             outline: none;
  106.             border-color: var(--primary-color);
  107.             box-shadow: 0 0 5px var(--primary-color);
  108.         }

  109.         .btn {
  110.             padding: 10px 20px;
  111.             border: none;
  112.             border-radius: 5px;
  113.             cursor: pointer;
  114.             transition: all 0.3s ease;
  115.             background: var(--primary-color);
  116.             color: white;
  117.             font-weight: bold;
  118.         }

  119.         .btn:hover {
  120.             transform: scale(1.05);
  121.             box-shadow: 0 0 10px rgba(0, 168, 255, 0.5);
  122.         }

  123.         .control-panel {
  124.             display: grid;
  125.             grid-template-areas:
  126.                 ". forward ."
  127.                 "left stop right"
  128.                 ". backward ."
  129.                 "functions functions functions";
  130.             grid-template-columns: repeat(3, 100px);
  131.             gap: 10px;
  132.             justify-content: center;
  133.             margin: 20px auto;
  134.             padding: 20px;
  135.             background: rgba(0, 0, 0, 0.3);
  136.             border-radius: 15px;
  137.             box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
  138.         }

  139.         .function-panel {
  140.             grid-area: functions;
  141.             display: grid;
  142.             grid-template-columns: repeat(auto-fit, minmax(90px, 1fr));
  143.             gap: 10px;
  144.             margin-top: 20px;
  145.             padding-top: 20px;
  146.             border-top: 1px solid var(--secondary-color);
  147.         }

  148.         .control-btn {
  149.             padding: 15px;
  150.             border: none;
  151.             border-radius: 50%;
  152.             cursor: pointer;
  153.             transition: all 0.3s ease;
  154.             background: var(--secondary-color);
  155.             color: var(--text-color);
  156.             font-weight: bold;
  157.             position: relative;
  158.             overflow: hidden;
  159.             width: 100px;
  160.             height: 100px;
  161.             display: flex;
  162.             align-items: center;
  163.             justify-content: center;
  164.             box-shadow: 0 0 10px rgba(0, 0, 0, 0.3),
  165.                         inset 0 0 15px rgba(255, 255, 255, 0.1);
  166.         }

  167.         .control-btn::before {
  168.             content: '';
  169.             position: absolute;
  170.             top: -50%;
  171.             left: -50%;
  172.             width: 200%;
  173.             height: 200%;
  174.             background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 70%);
  175.             transform: scale(0);
  176.             transition: transform 0.3s ease-out;
  177.         }

  178.         .control-btn:hover::before {
  179.             transform: scale(1);
  180.         }

  181.         .control-btn:active {
  182.             transform: scale(0.95);
  183.             box-shadow: 0 0 5px rgba(0, 0, 0, 0.3),
  184.                         inset 0 0 20px rgba(0, 0, 0, 0.5);
  185.         }

  186.         .control-btn:hover {
  187.             background: var(--primary-color);
  188.             box-shadow: 0 0 20px var(--primary-color),
  189.                         inset 0 0 15px rgba(255, 255, 255, 0.2);
  190.         }

  191.         .control-btn[data-command="前进"] { grid-area: forward; }
  192.         .control-btn[data-command="后退"] { grid-area: backward; }
  193.         .control-btn[data-command="左转"] { grid-area: left; }
  194.         .control-btn[data-command="右转"] { grid-area: right; }
  195.         .control-btn[data-command="停止"] {
  196.             grid-area: stop;
  197.             background: var(--danger-color);
  198.         }
  199.         .control-btn[data-command="停止"]:hover {
  200.             background: #ff3f34;
  201.         }

  202.         .function-btn {
  203.             padding: 12px;
  204.             border: none;
  205.             border-radius: 10px;
  206.             cursor: pointer;
  207.             transition: all 0.3s ease;
  208.             background: var(--secondary-color);
  209.             color: var(--text-color);
  210.             font-weight: bold;
  211.             position: relative;
  212.             overflow: hidden;
  213.             box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
  214.         }

  215.         .function-btn:hover {
  216.             background: var(--primary-color);
  217.             transform: translateY(-2px);
  218.             box-shadow: 0 5px 15px rgba(0, 168, 255, 0.3);
  219.         }

  220.         .function-btn:active {
  221.             transform: translateY(0);
  222.         }

  223.         .function-btn[data-command="开灯"],
  224.         .function-btn[data-command="关灯"] {
  225.             background: var(--warning-color);
  226.         }

  227.         .function-btn[data-command="红灯"] { background: #c23616; }
  228.         .function-btn[data-command="绿灯"] { background: #44bd32; }
  229.         .function-btn[data-command="黄灯"] { background: #e1b12c; }
  230.         .function-btn[data-command="喇叭"] { background: #8c7ae6; }

  231.         @keyframes pulse {
  232.             0% { transform: scale(1); }
  233.             50% { transform: scale(1.05); }
  234.             100% { transform: scale(1); }
  235.         }

  236.         .control-btn:active::after {
  237.             content: '';
  238.             position: absolute;
  239.             top: 50%;
  240.             left: 50%;
  241.             width: 10px;
  242.             height: 10px;
  243.             background: var(--primary-color);
  244.             border-radius: 50%;
  245.             transform: translate(-50%, -50%);
  246.             animation: pulse 0.5s ease-out;
  247.         }

  248.         .result-section {
  249.             margin-top: 20px;
  250.         }

  251.         .result-box {
  252.             width: 100%;
  253.             min-height: 100px;
  254.             padding: 10px;
  255.             background: rgba(0, 0, 0, 0.2);
  256.             border: 1px solid var(--secondary-color);
  257.             border-radius: 5px;
  258.             color: var(--text-color);
  259.             margin-bottom: 10px;
  260.             font-family: monospace;
  261.         }

  262.         @keyframes fadeIn {
  263.             from { opacity: 0; }
  264.             to { opacity: 1; }
  265.         }

  266.         .arrow {
  267.             font-size: 2em;
  268.             font-style: normal;
  269.             display: block;
  270.             margin-bottom: 5px;
  271.             text-shadow: 0 0 10px rgba(0, 168, 255, 0.5);
  272.         }

  273.         .control-btn span {
  274.             font-size: 0.9em;
  275.             opacity: 0.8;
  276.         }

  277.         @keyframes glow {
  278.             0% { box-shadow: 0 0 5px var(--primary-color); }
  279.             50% { box-shadow: 0 0 20px var(--primary-color), 0 0 30px var(--primary-color); }
  280.             100% { box-shadow: 0 0 5px var(--primary-color); }
  281.         }

  282.         .control-btn:active .arrow {
  283.             transform: scale(0.8);
  284.             transition: transform 0.1s ease;
  285.         }

  286.         .function-btn {
  287.             position: relative;
  288.             overflow: hidden;
  289.         }

  290.         .function-btn::after {
  291.             content: '';
  292.             position: absolute;
  293.             top: -50%;
  294.             left: -50%;
  295.             width: 200%;
  296.             height: 200%;
  297.             background: linear-gradient(
  298.                 transparent,
  299.                 rgba(255, 255, 255, 0.1),
  300.                 transparent
  301.             );
  302.             transform: rotate(45deg);
  303.             transition: transform 0.5s;
  304.             transform: translateX(-100%);
  305.         }

  306.         .function-btn:hover::after {
  307.             transform: translateX(100%);
  308.         }

  309.         .control-panel {
  310.             animation: slideIn 0.5s ease-out;
  311.         }

  312.         @keyframes slideIn {
  313.             from {
  314.                 transform: translateY(20px);
  315.                 opacity: 0;
  316.             }
  317.             to {
  318.                 transform: translateY(0);
  319.                 opacity: 1;
  320.             }
  321.         }

  322.         .function-panel {
  323.             animation: fadeInUp 0.5s ease-out;
  324.         }

  325.         @keyframes fadeInUp {
  326.             from {
  327.                 transform: translateY(10px);
  328.                 opacity: 0;
  329.             }
  330.             to {
  331.                 transform: translateY(0);
  332.                 opacity: 1;
  333.             }
  334.         }

  335.         .control-btn[data-command="停止"] .arrow {
  336.             color: #ff3f34;
  337.             animation: pulse 2s infinite;
  338.         }

  339.         .function-btn:active {
  340.             transform: scale(0.95);
  341.             box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.3);
  342.         }

  343.         /* 状态面板样式 */
  344.         .status-panel {
  345.             background: rgba(0, 0, 0, 0.3);
  346.             border-radius: 10px;
  347.             padding: 15px;
  348.             margin: 20px auto;
  349.             max-width: 400px;
  350.             display: flex;
  351.             justify-content: space-between;
  352.             box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
  353.         }

  354.         .status-indicator {
  355.             display: flex;
  356.             align-items: center;
  357.             gap: 8px;
  358.         }

  359.         .status-label {
  360.             color: var(--primary-color);
  361.             font-weight: bold;
  362.         }

  363.         .status-value {
  364.             background: rgba(0, 0, 0, 0.2);
  365.             padding: 5px 10px;
  366.             border-radius: 5px;
  367.             min-width: 80px;
  368.             text-align: center;
  369.         }

  370.         /* 键盘按键样式 */
  371.         kbd {
  372.             background-color: var(--secondary-color);
  373.             border-radius: 3px;
  374.             border: 1px solid var(--primary-color);
  375.             box-shadow: 0 1px 1px rgba(0, 0, 0, .2);
  376.             color: var(--text-color);
  377.             display: inline-block;
  378.             font-size: 0.85em;
  379.             font-weight: 700;
  380.             line-height: 1;
  381.             padding: 2px 6px;
  382.             margin: 0 2px;
  383.         }

  384.         /* 连接状态指示器 */
  385.         #connectionStatus.connected {
  386.             color: var(--success-color);
  387.             animation: pulse 2s infinite;
  388.         }

  389.         #connectionStatus.disconnected {
  390.             color: var(--danger-color);
  391.         }

  392.         #connectionStatus.pending {
  393.             color: var(--warning-color);
  394.         }

  395.         /* 当前命令高亮 */
  396.         #currentCommand.active {
  397.             color: var(--primary-color);
  398.             font-weight: bold;
  399.         }

  400.         /* 为不同类型的按钮添加特殊效果 */
  401.         .function-btn[data-command="开灯"]:hover,
  402.         .function-btn[data-command="关灯"]:hover {
  403.             animation: glow 1.5s infinite;
  404.         }

  405.         .function-btn[data-command="喇叭"]:hover {
  406.             animation: shake 0.5s infinite;
  407.         }

  408.         @keyframes shake {
  409.             0%, 100% { transform: translateX(0); }
  410.             25% { transform: translateX(-2px); }
  411.             75% { transform: translateX(2px); }
  412.         }

  413.         /* 添加响应式调整 */
  414.         /* 状态指示灯样式 */
  415.         .status-light {
  416.             position: absolute;
  417.             top: 10px;
  418.             right: 10px;
  419.             width: 30px;
  420.             height: 30px;
  421.             border-radius: 50%;
  422.             background: rgba(0, 0, 0, 0.3);
  423.             padding: 5px;
  424.             box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
  425.         }

  426.         .light-indicator {
  427.             width: 100%;
  428.             height: 100%;
  429.             border-radius: 50%;
  430.             background-color: #2f3542;
  431.             transition: all 0.3s ease;
  432.             box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.5);
  433.         }

  434.         .light-indicator.active {
  435.             box-shadow:
  436.                 0 0 10px currentColor,
  437.                 0 0 20px currentColor,
  438.                 inset 0 0 5px rgba(255, 255, 255, 0.5);
  439.             animation: pulseLight 1s infinite;
  440.         }

  441.         @keyframes pulseLight {
  442.             0% {
  443.                 transform: scale(0.95);
  444.                 opacity: 0.8;
  445.             }
  446.             50% {
  447.                 transform: scale(1.05);
  448.                 opacity: 1;
  449.             }
  450.             100% {
  451.                 transform: scale(0.95);
  452.                 opacity: 0.8;
  453.             }
  454.         }

  455.         /* 命令执行动画 */
  456.         @keyframes commandExecute {
  457.             0% {
  458.                 transform: scale(1);
  459.                 opacity: 1;
  460.             }
  461.             50% {
  462.                 transform: scale(1.2);
  463.                 opacity: 0.5;
  464.             }
  465.             100% {
  466.                 transform: scale(1);
  467.                 opacity: 1;
  468.             }
  469.         }

  470.         .executing-command {
  471.             animation: commandExecute 0.5s ease-out;
  472.         }

  473.         /* 响应式调整 */
  474.         @media (max-width: 768px) {
  475.             .control-panel {
  476.                 grid-template-columns: repeat(3, 80px);
  477.                 gap: 8px;
  478.                 padding: 15px;
  479.             }

  480.             .control-btn {
  481.                 width: 80px;
  482.                 height: 80px;
  483.             }

  484.             .arrow {
  485.                 font-size: 1.5em;
  486.             }

  487.             .control-btn span {
  488.                 font-size: 0.8em;
  489.             }

  490.             .status-light {
  491.                 width: 20px;
  492.                 height: 20px;
  493.                 top: 5px;
  494.                 right: 5px;
  495.             }

  496.             .function-panel {
  497.                 grid-template-columns: repeat(3, 1fr);
  498.             }

  499.             .status-panel {
  500.                 flex-direction: column;
  501.                 gap: 10px;
  502.                 align-items: center;
  503.             }
  504.         }

  505.         /* 暗色主题优化 */
  506.         @media (prefers-color-scheme: dark) {
  507.             .status-light {
  508.                 background: rgba(255, 255, 255, 0.1);
  509.             }

  510.             .light-indicator {
  511.                 background-color: #1e272e;
  512.             }
  513.         }

  514.         .switch {
  515.             position: relative;
  516.             display: inline-block;
  517.             width: 60px;
  518.             height: 34px;
  519.         }

  520.         .switch input {
  521.             opacity: 0;
  522.             width: 0;
  523.             height: 0;
  524.         }

  525.         .slider {
  526.             position: absolute;
  527.             cursor: pointer;
  528.             top: 0;
  529.             left: 0;
  530.             right: 0;
  531.             bottom: 0;
  532.             background-color: var(--secondary-color);
  533.             transition: .4s;
  534.             border-radius: 34px;
  535.         }

  536.         .slider:before {
  537.             position: absolute;
  538.             content: "";
  539.             height: 26px;
  540.             width: 26px;
  541.             left: 4px;
  542.             bottom: 4px;
  543.             background-color: white;
  544.             transition: .4s;
  545.             border-radius: 50%;
  546.         }

  547.         input:checked + .slider {
  548.             background-color: var(--primary-color);
  549.         }

  550.         input:checked + .slider:before {
  551.             transform: translateX(26px);
  552.         }

  553.         .modal {
  554.             display: none;
  555.             position: fixed;
  556.             top: 0;
  557.             left: 0;
  558.             width: 100%;
  559.             height: 100%;
  560.             background: rgba(0, 0, 0, 0.5);
  561.             justify-content: center;
  562.             align-items: center;
  563.         }

  564.         .modal-content {
  565.             background: var(--bg-color);
  566.             padding: 20px;
  567.             border-radius: 10px;
  568.             width: 300px;
  569.         }

  570.         .close {
  571.             float: right;
  572.             cursor: pointer;
  573.             color: var(--text-color);
  574.         }
  575.     </style>
  576. </head>
  577. <body>
  578.     <div class="container">
  579.         <h1 class="title" id="mainTitle">AI赋能MQTT</h1>
  580.         
  581.         <div class="tabs">
  582.             <div class="tab-buttons">
  583.                 <button class="tab-button active" data-tab="config">配置参数</button>
  584.                 <button class="tab-button" data-tab="control">遥控面板</button>
  585.             </div>

  586.             <div class="tab-content active" id="config">
  587.                 <div class="config-section">
  588.                     <div class="form-row">
  589.                         <label class="switch">
  590.                             <input type="checkbox" id="configToggle">
  591.                             <span class="slider"></span>
  592.                         </label>
  593.                         <span>显示/隐藏配置</span>
  594.                     </div>

  595.                     <div id="configParams">
  596.                         <div class="form-row">
  597.                             <div class="form-group">
  598.                                 <label>服务器</label>
  599.                                 <input type="text" class="form-control" id="server" value="i.37ck.cn">
  600.                             </div>
  601.                             <div class="form-group">
  602.                                 <label>端口</label>
  603.                                 <input type="text" class="form-control" id="port" value="88">
  604.                             </div>
  605.                             <div class="form-group">
  606.                                 <label>操作</label>
  607.                                 <input type="text" class="form-control" id="operation" value="publish">
  608.                             </div>
  609.                         </div>
  610.                         <div class="form-row">
  611.                             <div class="form-group">
  612.                                 <label>用户名</label>
  613.                                 <input type="text" class="form-control" id="username" value="37ck">
  614.                             </div>
  615.                             <div class="form-group">
  616.                                 <label>密码</label>
  617.                                 <input type="password" class="form-control" id="password" value="20200808">
  618.                             </div>
  619.                         </div>
  620.                     </div>

  621.                     <div class="form-row">
  622.                         <div class="form-group">
  623.                             <label>主题</label>
  624.                             <select class="form-control" id="topic">
  625.                                 <option value="37ck/37创客">37ck/37创客</option>
  626.                                 <option value="37ck/莘村中学">37ck/莘村中学</option>
  627.                                 <option value="37ck/富教小学">37ck/富教小学</option>
  628.                                 <option value="37ck/文田中学">37ck/文田中学</option>
  629.                             </select>
  630.                         </div>
  631.                         <div class="form-group">
  632.                             <label>指令</label>
  633.                             <select class="form-control" id="command">
  634.                                 <option value="在线">在线</option>
  635.                                 <option value="放学啦">放学啦</option>
  636.                                 <option value="下课啦">下课啦</option>
  637.                                 <option value="到安全等待区">到安全等待区</option>
  638.                                 <option value="正赶往等待车位">正赶往等待车位</option>
  639.                             </select>
  640.                         </div>
  641.                     </div>

  642.                     <div class="form-row">
  643.                         <button class="btn" id="importConfig">导入配置</button>
  644.                         <button class="btn" id="exportConfig">导出配置</button>
  645.                         <button class="btn" id="sendBtn">发送请求</button>
  646.                     </div>
  647.                 </div>
  648.             </div>

  649.             <div class="tab-content" id="control">
  650.                 <h3 style="text-align: center; margin-bottom: 20px; color: var(--primary-color);">遥控操作面板</h3>
  651.                 <div class="control-panel">
  652.                     <button class="control-btn" data-command="前进">
  653.                         <i class="arrow">↑</i>
  654.                         <span>前进</span>
  655.                     </button>
  656.                     <button class="control-btn" data-command="左转">
  657.                         <i class="arrow">←</i>
  658.                         <span>左转</span>
  659.                     </button>
  660.                     <button class="control-btn" data-command="停止">
  661.                         <i class="arrow">■</i>
  662.                         <span>停止</span>
  663.                     </button>
  664.                     <button class="control-btn" data-command="右转">
  665.                         <i class="arrow">→</i>
  666.                         <span>右转</span>
  667.                     </button>
  668.                     <button class="control-btn" data-command="后退">
  669.                         <i class="arrow">↓</i>
  670.                         <span>后退</span>
  671.                     </button>
  672.                     
  673.                     <div class="function-panel">
  674.                         <button class="function-btn" data-command="开灯">开灯</button>
  675.                         <button class="function-btn" data-command="关灯">关灯</button>
  676.                         <button class="function-btn" data-command="红灯">红灯</button>
  677.                         <button class="function-btn" data-command="绿灯">绿灯</button>
  678.                         <button class="function-btn" data-command="黄灯">黄灯</button>
  679.                         <button class="function-btn" data-command="喇叭">喇叭</button>
  680.                     </div>
  681.                 </div>
  682.                
  683.                 <div class="status-panel">
  684.                     <div class="status-indicator">
  685.                         <span class="status-label">状态:</span>
  686.                         <span class="status-value" id="connectionStatus">就绪</span>
  687.                     </div>
  688.                     <div class="status-indicator">
  689.                         <span class="status-label">当前命令:</span>
  690.                         <span class="status-value" id="currentCommand">无</span>
  691.                     </div>
  692.                 </div>
  693.                
  694.                 <div style="text-align: center; margin-top: 20px; font-size: 0.9em; color: var(--text-color);">
  695.                     <p>点击按钮或使用键盘方向键控制</p>
  696.                     <p><kbd>↑</kbd> 前进, <kbd>↓</kbd> 后退, <kbd>←</kbd> 左转, <kbd>→</kbd> 右转, <kbd>空格</kbd> 停止</p>
  697.                 </div>
  698.             </div>
  699.         </div>

  700.         <div class="result-section">
  701.             <div class="form-group">
  702.                 <label>发送内容</label>
  703.                 <div class="result-box" id="requestUrl"></div>
  704.             </div>
  705.             <div class="form-group">
  706.                 <label>返回内容</label>
  707.                 <div class="result-box" id="response"></div>
  708.             </div>
  709.         </div>
  710.     </div>

  711.     <div class="modal" id="titleModal">
  712.         <div class="modal-content">
  713.             <span class="close">&times;</span>
  714.             <div class="form-group">
  715.                 <label>修改标题</label>
  716.                 <input type="text" class="form-control" id="newTitle">
  717.             </div>
  718.             <button class="btn" id="saveTitle">保存</button>
  719.         </div>
  720.     </div>

  721.     <audio id="clickSound" src="data:audio/mpeg;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjI5LjEwMAAAAAAAAAAAAAAA//OEAAAAAAAAAAAAAAAAAAAAAAAASW5mbwAAAA8AAAAEAAABIADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV6urq6urq6urq6urq6urq6urq6urq6urq6v////////////////////////////////8AAAAATGF2YzU4LjU0AAAAAAAAAAAAAAAAJAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//MUZAAAAAGkAAAAAAAAA0gAAAAATEFN//MUZAMAAAGkAAAAAAAAA0gAAAAARTMu//MUZAYAAAGkAAAAAAAAA0gAAAAARTMz//MUZAkAAAGkAAAAAAAAA0gAAAAARTMz//MUZAwAAAGkAAAAAAAAA0gAAAAARTMz//MUZAAAAAGkAAAAAAAAA0gAAAAATEFN//MUZAAAAAGkAAAAAAAAA0gAAAAATEFN//MUZAAAAAGkAAAAAAAAA0gAAAAATEFN//MUZAAAAAGkAAAAAAAAA0gAAAAATEFN//MUZAAAAAGkAAAAAAAAA0gAAAAATEFN" preload="auto"></audio>

  722.     <script>
  723.         // 音效播放函数
  724.         function playClickSound() {
  725.             const sound = document.getElementById('clickSound');
  726.             sound.currentTime = 0;
  727.             sound.play().catch(e => console.log('Audio play failed:', e));
  728.         }

  729.         // 标题编辑功能
  730.         const mainTitle = document.getElementById('mainTitle');
  731.         const titleModal = document.getElementById('titleModal');
  732.         const newTitleInput = document.getElementById('newTitle');
  733.         const saveTitleBtn = document.getElementById('saveTitle');
  734.         const closeBtn = document.querySelector('.close');

  735.         mainTitle.addEventListener('dblclick', () => {
  736.             titleModal.style.display = 'flex';
  737.             newTitleInput.value = mainTitle.textContent;
  738.             playClickSound();
  739.         });

  740.         closeBtn.addEventListener('click', () => {
  741.             titleModal.style.display = 'none';
  742.             playClickSound();
  743.         });

  744.         saveTitleBtn.addEventListener('click', () => {
  745.             mainTitle.textContent = newTitleInput.value;
  746.             titleModal.style.display = 'none';
  747.             playClickSound();
  748.         });

  749.         // 标签页切换
  750.         const tabButtons = document.querySelectorAll('.tab-button');
  751.         const tabContents = document.querySelectorAll('.tab-content');

  752.         tabButtons.forEach(button => {
  753.             button.addEventListener('click', () => {
  754.                 playClickSound();
  755.                 const tabId = button.getAttribute('data-tab');
  756.                
  757.                 tabButtons.forEach(btn => btn.classList.remove('active'));
  758.                 tabContents.forEach(content => content.classList.remove('active'));
  759.                
  760.                 button.classList.add('active');
  761.                 document.getElementById(tabId).classList.add('active');
  762.             });
  763.         });

  764.         // 配置参数显示/隐藏
  765.         const configToggle = document.getElementById('configToggle');
  766.         const configParams = document.getElementById('configParams');

  767.         configToggle.addEventListener('change', () => {
  768.             playClickSound();
  769.             configParams.style.display = configToggle.checked ? 'block' : 'none';
  770.         });
  771.         configParams.style.display = 'none'; // 默认隐藏

  772.         // 配置导入导出
  773.         const importConfigBtn = document.getElementById('importConfig');
  774.         const exportConfigBtn = document.getElementById('exportConfig');

  775.         function getConfig() {
  776.             return {
  777.                 server: document.getElementById('server').value,
  778.                 port: document.getElementById('port').value,
  779.                 operation: document.getElementById('operation').value,
  780.                 username: document.getElementById('username').value,
  781.                 password: document.getElementById('password').value,
  782.                 topic: document.getElementById('topic').value,
  783.                 command: document.getElementById('command').value
  784.             };
  785.         }

  786.         function setConfig(config) {
  787.             document.getElementById('server').value = config.server || '';
  788.             document.getElementById('port').value = config.port || '';
  789.             document.getElementById('operation').value = config.operation || '';
  790.             document.getElementById('username').value = config.username || '';
  791.             document.getElementById('password').value = config.password || '';
  792.             document.getElementById('topic').value = config.topic || '';
  793.             document.getElementById('command').value = config.command || '';
  794.         }

  795.         exportConfigBtn.addEventListener('click', () => {
  796.             playClickSound();
  797.             const config = getConfig();
  798.             const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' });
  799.             const url = URL.createObjectURL(blob);
  800.             const a = document.createElement('a');
  801.             a.href = url;
  802.             a.download = 'mqtt_config.json';
  803.             document.body.appendChild(a);
  804.             a.click();
  805.             document.body.removeChild(a);
  806.             URL.revokeObjectURL(url);
  807.         });

  808.         importConfigBtn.addEventListener('click', () => {
  809.             playClickSound();
  810.             const input = document.createElement('input');
  811.             input.type = 'file';
  812.             input.accept = 'application/json';
  813.             input.onchange = e => {
  814.                 const file = e.target.files[0];
  815.                 const reader = new FileReader();
  816.                 reader.onload = event => {
  817.                     try {
  818.                         const config = JSON.parse(event.target.result);
  819.                         setConfig(config);
  820.                     } catch (err) {
  821.                         alert('配置文件格式错误!');
  822.                     }
  823.                 };
  824.                 reader.readAsText(file);
  825.             };
  826.             input.click();
  827.         });

  828.         // 状态管理
  829.         const connectionStatus = document.getElementById('connectionStatus');
  830.         const currentCommand = document.getElementById('currentCommand');
  831.         
  832.         function updateStatus(status) {
  833.             connectionStatus.textContent = status;
  834.             connectionStatus.className = '';
  835.             
  836.             switch(status.toLowerCase()) {
  837.                 case '已连接':
  838.                     connectionStatus.classList.add('connected');
  839.                     break;
  840.                 case '断开':
  841.                     connectionStatus.classList.add('disconnected');
  842.                     break;
  843.                 case '连接中':
  844.                 case '发送中':
  845.                     connectionStatus.classList.add('pending');
  846.                     break;
  847.                 default:
  848.                     break;
  849.             }
  850.         }
  851.         
  852.         function updateCurrentCommand(command) {
  853.             currentCommand.textContent = command || '无';
  854.             if (command && command !== '无') {
  855.                 currentCommand.classList.add('active');
  856.                 setTimeout(() => {
  857.                     currentCommand.classList.remove('active');
  858.                 }, 2000);
  859.             } else {
  860.                 currentCommand.classList.remove('active');
  861.             }
  862.         }

  863.         // 发送请求函数
  864.         function sendRequest(command) {
  865.             const config = getConfig();
  866.             const url = `http://${config.server}:${config.port}/${config.operation}?topic=${encodeURIComponent(config.topic)}&msg=${encodeURIComponent(command)}&iname=${encodeURIComponent(config.username)}&ipwd=${encodeURIComponent(config.password)}`;
  867.             
  868.             document.getElementById('requestUrl').textContent = url;
  869.             updateStatus('发送中');
  870.             updateCurrentCommand(command);

  871.             fetch(url)
  872.                 .then(response => {
  873.                     if (!response.ok) {
  874.                         throw new Error(`HTTP error! Status: ${response.status}`);
  875.                     }
  876.                     updateStatus('已连接');
  877.                     return response.text();
  878.                 })
  879.                 .then(data => {
  880.                     document.getElementById('response').textContent = data;
  881.                     
  882.                     // 添加视觉反馈
  883.                     const responseBox = document.getElementById('response');
  884.                     responseBox.style.backgroundColor = 'rgba(0, 168, 255, 0.1)';
  885.                     setTimeout(() => {
  886.                         responseBox.style.backgroundColor = '';
  887.                     }, 500);
  888.                 })
  889.                 .catch(error => {
  890.                     document.getElementById('response').textContent = '请求失败:' + error.message;
  891.                     updateStatus('断开');
  892.                     
  893.                     // 添加错误视觉反馈
  894.                     const responseBox = document.getElementById('response');
  895.                     responseBox.style.backgroundColor = 'rgba(194, 54, 22, 0.1)';
  896.                     setTimeout(() => {
  897.                         responseBox.style.backgroundColor = '';
  898.                     }, 500);
  899.                 });
  900.         }

  901.         // 发送按钮事件
  902.         document.getElementById('sendBtn').addEventListener('click', () => {
  903.             playClickSound();
  904.             const command = document.getElementById('command').value;
  905.             sendRequest(command);
  906.         });

  907.         // 遥控按钮事件和键盘控制
  908.         const controlButtons = document.querySelectorAll('.control-btn, .function-btn');
  909.         const keyCommandMap = {
  910.             'ArrowUp': '前进',
  911.             'ArrowDown': '后退',
  912.             'ArrowLeft': '左转',
  913.             'ArrowRight': '右转',
  914.             ' ': '停止', // 空格键
  915.             'KeyL': '开灯',
  916.             'KeyK': '关灯',
  917.             'KeyR': '红灯',
  918.             'KeyG': '绿灯',
  919.             'KeyY': '黄灯',
  920.             'KeyH': '喇叭'
  921.         };

  922.         let activeButtons = new Set();

  923.         // 添加虚拟遥控器状态指示灯
  924.         const statusLight = document.createElement('div');
  925.         statusLight.className = 'status-light';
  926.         statusLight.innerHTML = `
  927.             <div class="light-indicator"></div>
  928.         `;
  929.         document.querySelector('.control-panel').appendChild(statusLight);

  930.         // 更新按钮激活函数
  931.         function activateButton(command) {
  932.             const button = document.querySelector(`[data-command="${command}"]`);
  933.             if (button && !activeButtons.has(command)) {
  934.                 activeButtons.add(command);
  935.                 button.style.animation = 'glow 1s infinite';
  936.                
  937.                 // 更新状态指示灯
  938.                 const lightIndicator = document.querySelector('.light-indicator');
  939.                 lightIndicator.className = 'light-indicator active';
  940.                
  941.                 // 根据命令类型设置不同颜色
  942.                 if (command === '前进' || command === '后退' || command === '左转' || command === '右转') {
  943.                     lightIndicator.style.backgroundColor = '#00a8ff'; // 蓝色
  944.                 } else if (command === '停止') {
  945.                     lightIndicator.style.backgroundColor = '#ff3f34'; // 红色
  946.                 } else if (command.includes('灯')) {
  947.                     lightIndicator.style.backgroundColor = '#ffa502'; // 黄色
  948.                 } else {
  949.                     lightIndicator.style.backgroundColor = '#7bed9f'; // 绿色
  950.                 }
  951.                
  952.                 playClickSound();
  953.                 debouncedSendRequest(command);
  954.             }
  955.         }

  956.         function deactivateButton(command) {
  957.             const button = document.querySelector(`[data-command="${command}"]`);
  958.             if (button) {
  959.                 activeButtons.delete(command);
  960.                 button.style.animation = '';
  961.                
  962.                 // 如果没有活动按钮,重置状态指示灯
  963.                 if (activeButtons.size === 0) {
  964.                     const lightIndicator = document.querySelector('.light-indicator');
  965.                     lightIndicator.className = 'light-indicator';
  966.                 }
  967.             }
  968.         }

  969.         // 防抖动函数
  970.         function debounce(func, wait) {
  971.             let timeout;
  972.             return function executedFunction(...args) {
  973.                 const later = () => {
  974.                     clearTimeout(timeout);
  975.                     func(...args);
  976.                 };
  977.                 clearTimeout(timeout);
  978.                 timeout = setTimeout(later, wait);
  979.             };
  980.         }

  981.         // 节流函数
  982.         function throttle(func, limit) {
  983.             let inThrottle;
  984.             return function(...args) {
  985.                 if (!inThrottle) {
  986.                     func.apply(this, args);
  987.                     inThrottle = true;
  988.                     setTimeout(() => inThrottle = false, limit);
  989.                 }
  990.             }
  991.         }

  992.         // 防抖动的请求发送
  993.         const debouncedSendRequest = debounce((command) => {
  994.             sendRequest(command);
  995.         }, 300);

  996.         // 节流的按钮激活
  997.         const throttledActivateButton = throttle((command) => {
  998.             const button = document.querySelector(`[data-command="${command}"]`);
  999.             if (button && !activeButtons.has(command)) {
  1000.                 activeButtons.add(command);
  1001.                 button.style.animation = 'glow 1s infinite';
  1002.                 playClickSound();
  1003.                 debouncedSendRequest(command);
  1004.             }
  1005.         }, 200);

  1006.         // 键盘控制
  1007.         document.addEventListener('keydown', (e) => {
  1008.             if (document.activeElement.tagName === 'INPUT') return; // 如果焦点在输入框中,不处理键盘事件
  1009.             
  1010.             const command = keyCommandMap[e.key] || keyCommandMap[e.code];
  1011.             if (command) {
  1012.                 e.preventDefault();
  1013.                 throttledActivateButton(command);
  1014.             }
  1015.         });

  1016.         document.addEventListener('keyup', (e) => {
  1017.             const command = keyCommandMap[e.key] || keyCommandMap[e.code];
  1018.             if (command) {
  1019.                 deactivateButton(command);
  1020.             }
  1021.         });

  1022.         // 触摸事件处理优化
  1023.         let touchStartTime;
  1024.         let longPressTimer;
  1025.         
  1026.         function handleTouchStart(e, command) {
  1027.             e.preventDefault();
  1028.             touchStartTime = Date.now();
  1029.             longPressTimer = setTimeout(() => {
  1030.                 throttledActivateButton(command);
  1031.             }, 500);
  1032.         }

  1033.         function handleTouchEnd(e, command) {
  1034.             e.preventDefault();
  1035.             clearTimeout(longPressTimer);
  1036.             const touchDuration = Date.now() - touchStartTime;
  1037.             
  1038.             if (touchDuration < 500) {
  1039.                 // 短按
  1040.                 throttledActivateButton(command);
  1041.             }
  1042.             deactivateButton(command);
  1043.         }

  1044.         // 更新控制按钮事件监听
  1045.         controlButtons.forEach(button => {
  1046.             const command = button.getAttribute('data-command');

  1047.             // 触摸事件
  1048.             button.addEventListener('touchstart', (e) => handleTouchStart(e, command));
  1049.             button.addEventListener('touchend', (e) => handleTouchEnd(e, command));
  1050.             button.addEventListener('touchcancel', () => {
  1051.                 clearTimeout(longPressTimer);
  1052.                 deactivateButton(command);
  1053.             });

  1054.             // 鼠标事件
  1055.             button.addEventListener('mousedown', () => throttledActivateButton(command));
  1056.             button.addEventListener('mouseup', () => deactivateButton(command));
  1057.             button.addEventListener('mouseleave', () => deactivateButton(command));
  1058.         });

  1059.         // 遥控按钮事件
  1060.         controlButtons.forEach(button => {
  1061.             let pressTimer;
  1062.             const command = button.getAttribute('data-command');

  1063.             button.addEventListener('mousedown', () => {
  1064.                 activateButton(command);
  1065.                 // 长按处理
  1066.                 pressTimer = setTimeout(() => {
  1067.                     button.classList.add('long-press');
  1068.                 }, 500);
  1069.             });

  1070.             button.addEventListener('mouseup', () => {
  1071.                 deactivateButton(command);
  1072.                 clearTimeout(pressTimer);
  1073.                 button.classList.remove('long-press');
  1074.             });

  1075.             button.addEventListener('mouseleave', () => {
  1076.                 deactivateButton(command);
  1077.                 clearTimeout(pressTimer);
  1078.                 button.classList.remove('long-press');
  1079.             });

  1080.             // 触摸设备支持
  1081.             button.addEventListener('touchstart', (e) => {
  1082.                 e.preventDefault();
  1083.                 activateButton(command);
  1084.             });

  1085.             button.addEventListener('touchend', () => {
  1086.                 deactivateButton(command);
  1087.             });
  1088.         });

  1089.         // 添加按钮反馈效果
  1090.         function addButtonFeedback(button) {
  1091.             button.addEventListener('mousedown', () => {
  1092.                 button.style.transform = 'scale(0.95)';
  1093.             });

  1094.             button.addEventListener('mouseup', () => {
  1095.                 button.style.transform = '';
  1096.             });

  1097.             button.addEventListener('mouseleave', () => {
  1098.                 button.style.transform = '';
  1099.             });
  1100.         }

  1101.         // 为所有按钮添加反馈效果
  1102.         document.querySelectorAll('.btn, .control-btn, .function-btn').forEach(addButtonFeedback);

  1103.         // 添加工具提示显示快捷键
  1104.         function addTooltip(button, command) {
  1105.             const keyBinding = Object.entries(keyCommandMap).find(([_, cmd]) => cmd === command);
  1106.             if (keyBinding) {
  1107.                 const key = keyBinding[0].replace('Arrow', '↑↓←→').replace('Key', '');
  1108.                 button.title = `${command} (${key})`;
  1109.             }
  1110.         }

  1111.         controlButtons.forEach(button => {
  1112.             const command = button.getAttribute('data-command');
  1113.             addTooltip(button, command);
  1114.         });

  1115.         // 点击其他区域关闭标题编辑模态框
  1116.         window.addEventListener('click', (e) => {
  1117.             if (e.target === titleModal) {
  1118.                 titleModal.style.display = 'none';
  1119.             }
  1120.         });

  1121.         // 按ESC键关闭标题编辑模态框
  1122.         document.addEventListener('keydown', (e) => {
  1123.             if (e.key === 'Escape' && titleModal.style.display === 'flex') {
  1124.                 titleModal.style.display = 'none';
  1125.             }
  1126.         });
  1127.     </script>
  1128. </body>
  1129. </html>
复制代码
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|37创客科创中心

GMT+8, 2025-12-10 06:43 , Processed in 0.239524 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表