4G模块应用,内网穿透,前端网页的制作第三讲(环境准备以及AI的快速开发)
本文介绍了一个智能头盔管理系统的开发过程。系统采用前后端分离架构,后端使用Node.js搭建WebSocket服务器实现数据转发功能,前端采用纯HTML/CSS/JS开发,具有现代化UI设计。开发环境需要安装Node.js(v14+)、CMake和Python。系统包含以下核心功能:1) 可配置的WebSocket连接;2) 实时数据监控界面;3) 模拟传感器数据发送;4) 美观的苹果风格UI设计
注:其中使用的关键词可以直接提供给AI,如果操作有区别,请根据您使用的AI提供步骤为准
环境准备:
首先需要下载好cmake和node.js (建议 v14.0 以上)和python
其次需要您打开乐青映射https://www.locyanfrp.cn/的官网下载他的启动器kairo,或者您可以选择下载他的纯净版客户端Frpc,都是可以的

Web前端的开发和Node.js后端迅速开发,AI的快速应用(我使用的是gemini开发的)
注:不一定要使用gemini进行开发,您有多种选择例如codex,cursor等等都可以帮助您进行快速开发
gemini开发教程
第一步关键词说明(如果您使用的是codex,cursor这种有agent功能的ai会开发的更加快速)
我现在需要写一个智能头盔管理系统,框架采用前后端分离的结构,后端使用nodejs,所有的接口需要清晰分明,前端不采用任何框架,单纯使用html,js以及css需要单出生成文件,不要全部放在html里面,首先帮我实现第一步,搭建一个websocket服务器,前端可以自行设置服务器地址端口进行链接,服务器默认逻辑是接收到数据后,转发数据
1.文件创建(这个只是示例,内部文件的命名和位置可根据自己的喜好进行修改)
helmet-system/ # 项目根目录
├── package.json # 项目配置文件
├── node_modules/ # 依赖库
├── backend/ # 后端代码
│ └── server.js # 核心服务器文件
└── frontend/ # 前端代码
├── index.html # 页面结构
├── style.css # 样式表
└── script.js # 交互逻辑
2.打开文件,在文件路径使用cmd(下方是示例)

3.下载初始化与依赖(这个是命令行代码,请一句一句输入)
npm init -y # 初始化项目
npm install ws # 安装 WebSocket 库 (唯一的外部依赖)
4.服务器启动(请在把后面修改了UI的代码复制后再开启服务器进行测试)-(这个是命令行代码,请一句一句输入)
node backend/server.js
效果图(这个只是示例,不同的ai会有不同的网页结构构筑,请以自己的代码为准)

UI的美化以及将前端部署至网页
第二步关键词
前端需要部署到网页上 比如说127.0.0.1 不能直接以静态文件进行打开
第三步关键词
完全重构这个系统UI 不要改动功能业务代码 UI需要符合现代化设计 符合苹果前端设计理念 系统交互需要流畅 动效过渡需要细腻 内容需要丰富
package.json
{
"name": "helmet-system",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"ws": "^8.19.0"
}
}
backend/server.js
/**
* backend/server.js
* 极简版服务器 - 只使用 ws 和原生模块
* 绝对不包含 document 等前端代码
*/
const http = require('http');
const fs = require('fs');
const path = require('path');
const WebSocket = require('ws');
const PORT = 8080;
// 文件类型映射
const MIME_TYPES = {
'.html': 'text/html',
'.css': 'text/css',
'.js': 'text/javascript',
'.json': 'application/json'
};
// 1. 创建 HTTP 服务器 (负责让浏览器能看到网页)
const server = http.createServer((req, res) => {
// 默认访问 index.html
let filePath = req.url === '/' ? 'index.html' : req.url;
// 指向 ../frontend 文件夹
const fullPath = path.join(__dirname, '../frontend', filePath);
const extname = path.extname(fullPath);
const contentType = MIME_TYPES[extname] || 'text/plain';
fs.readFile(fullPath, (err, content) => {
if (err) {
if (err.code === 'ENOENT') {
res.writeHead(404);
res.end('404 Not Found');
} else {
res.writeHead(500);
res.end(`Server Error: ${err.code}`);
}
} else {
res.writeHead(200, { 'Content-Type': contentType });
res.end(content, 'utf-8');
}
});
});
// 2. 创建 WebSocket 服务器 (负责数据实时转发)
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws, req) => {
const ip = req.socket.remoteAddress;
console.log(`[WebSocket] 新连接: ${ip}`);
ws.on('message', (message) => {
// 收到消息,直接转发给其他人
const msgString = message.toString();
console.log(`[转发数据] ${msgString}`);
wss.clients.forEach((client) => {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(msgString);
}
});
});
});
// 3. 启动
server.listen(PORT, () => {
console.log(`-----------------------------------`);
console.log(`服务器已启动!`);
console.log(`请在浏览器打开: http://127.0.0.1:${PORT}`);
console.log(`-----------------------------------`);
});
frontend/index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>iHelmet 智能终端</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="ambient-light"></div>
<div class="app-container">
<nav class="sidebar glass">
<div class="brand">
<div class="logo-icon"></div>
<span>iHelmet Pro</span>
</div>
<ul class="menu">
<li class="active">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>
监控台
</li>
<li>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
设备管理
</li>
</ul>
<div class="user-profile">
<div class="avatar">Admin</div>
<div class="info">
<span class="name">系统管理员</span>
<span class="role">在线</span>
</div>
</div>
</nav>
<main class="dashboard">
<header class="glass-header">
<h1>实时数据中心</h1>
<div class="server-status">
<div id="statusDot" class="status-dot offline"></div>
<span id="statusText">未连接服务器</span>
</div>
</header>
<div class="content-grid">
<div class="col-left">
<section class="card glass-card">
<div class="card-header">
<h3>服务器连接</h3>
</div>
<div class="card-body">
<div class="input-group">
<label>Server Address</label>
<input type="text" id="serverIp" placeholder="127.0.0.1" class="ios-input">
</div>
<div class="input-group">
<label>Port</label>
<input type="number" id="serverPort" placeholder="8080" class="ios-input">
</div>
<button id="btnConnect" class="ios-btn btn-primary">
<span>连接系统</span>
</button>
</div>
</section>
<section class="card glass-card">
<div class="card-header">
<h3>模拟传感器</h3>
</div>
<div class="card-body">
<textarea id="msgInput" class="ios-textarea" placeholder="{ 'gps': [116.3, 39.9], 'bpm': 85 }"></textarea>
<button id="btnSend" class="ios-btn btn-success" disabled>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
发送数据
</button>
</div>
</section>
</div>
<div class="col-right">
<section class="card glass-card full-height">
<div class="card-header flex-header">
<h3>数据流监控</h3>
<button id="btnClear" class="text-btn">清除日志</button>
</div>
<div id="logArea" class="chat-container">
<div class="system-msg">系统准备就绪,等待连接...</div>
</div>
</section>
</div>
</div>
</main>
</div>
<script src="script.js"></script>
</body>
</html>
frontend/style.css
/* frontend/style.css */
:root {
--bg-color: #fbfbfd;
--text-main: #1d1d1f;
--text-secondary: #86868b;
--accent-blue: #0071e3;
--accent-green: #34c759;
--accent-red: #ff3b30;
--glass-bg: rgba(255, 255, 255, 0.75);
--glass-border: rgba(255, 255, 255, 0.4);
--shadow-soft: 0 8px 30px rgba(0, 0, 0, 0.04);
--radius-lg: 20px;
--radius-md: 12px;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", sans-serif;
background-color: #f5f5f7;
color: var(--text-main);
overflow: hidden;
height: 100vh;
}
/* 动态背景光效 */
.ambient-light {
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle at 50% 50%, #e0f0ff 0%, #f5f5f7 40%);
z-index: -1;
animation: pulseLight 10s infinite alternate;
}
@keyframes pulseLight {
0% { transform: scale(1); }
100% { transform: scale(1.1); }
}
.app-container {
display: flex;
height: 100vh;
padding: 20px;
box-sizing: border-box;
gap: 20px;
}
/* --- Sidebar --- */
.sidebar {
width: 240px;
background: var(--glass-bg);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-radius: var(--radius-lg);
border: 1px solid var(--glass-border);
padding: 30px 20px;
display: flex;
flex-direction: column;
box-shadow: var(--shadow-soft);
}
.brand {
font-size: 20px;
font-weight: 700;
margin-bottom: 40px;
display: flex;
align-items: center;
gap: 10px;
}
.menu {
list-style: none;
padding: 0;
flex: 1;
}
.menu li {
padding: 12px 16px;
margin-bottom: 8px;
border-radius: var(--radius-md);
color: var(--text-secondary);
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 12px;
font-weight: 500;
}
.menu li svg { width: 20px; height: 20px; }
.menu li.active, .menu li:hover {
background: white;
color: var(--accent-blue);
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
}
.user-profile {
display: flex;
align-items: center;
gap: 12px;
padding-top: 20px;
border-top: 1px solid rgba(0,0,0,0.05);
}
.avatar {
width: 40px;
height: 40px;
background: linear-gradient(135deg, #0071e3, #42a5f5);
border-radius: 50%;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
}
.info .name { display: block; font-size: 14px; font-weight: 600; }
.info .role { font-size: 12px; color: var(--accent-green); }
/* --- Dashboard --- */
.dashboard {
flex: 1;
display: flex;
flex-direction: column;
gap: 20px;
}
.glass-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 10px;
}
.server-status {
display: flex;
align-items: center;
gap: 8px;
background: white;
padding: 6px 12px;
border-radius: 20px;
font-size: 13px;
font-weight: 600;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
}
.status-dot { width: 8px; height: 8px; border-radius: 50%; background: #ccc; }
.status-dot.online { background: var(--accent-green); box-shadow: 0 0 8px var(--accent-green); }
.status-dot.offline { background: #ccc; }
.status-dot.error { background: var(--accent-red); }
.content-grid {
display: grid;
grid-template-columns: 320px 1fr;
gap: 20px;
height: 100%;
overflow: hidden; /* 防止溢出 */
}
/* --- Cards --- */
.glass-card {
background: var(--glass-bg);
backdrop-filter: blur(20px);
border-radius: var(--radius-lg);
border: 1px solid var(--glass-border);
padding: 24px;
box-shadow: var(--shadow-soft);
display: flex;
flex-direction: column;
transition: transform 0.2s;
}
.col-right .glass-card {
height: calc(100vh - 120px); /* 确保高度填满 */
}
.card-header h3 {
margin: 0 0 20px 0;
font-size: 16px;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.flex-header { display: flex; justify-content: space-between; align-items: center; }
/* --- Inputs & Buttons --- */
.input-group { margin-bottom: 15px; }
.input-group label { display: block; font-size: 12px; color: var(--text-secondary); margin-bottom: 5px; font-weight: 600; }
.ios-input, .ios-textarea {
width: 100%;
padding: 12px;
border: none;
background: white;
border-radius: var(--radius-md);
font-size: 14px;
color: var(--text-main);
box-sizing: border-box;
transition: box-shadow 0.2s;
font-family: inherit;
}
.ios-textarea { height: 80px; resize: none; }
.ios-input:focus, .ios-textarea:focus {
outline: none;
box-shadow: 0 0 0 2px var(--accent-blue);
}
.ios-btn {
width: 100%;
padding: 14px;
border: none;
border-radius: var(--radius-md);
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
color: white;
}
.btn-primary { background: var(--accent-blue); }
.btn-primary:hover { background: #0077ED; transform: scale(1.02); }
.btn-primary:active { transform: scale(0.98); }
.btn-success { background: var(--accent-green); }
.btn-success:disabled { background: #ccc; cursor: not-allowed; opacity: 0.6; }
.text-btn { background: none; border: none; color: var(--accent-blue); cursor: pointer; font-size: 13px; }
/* --- Chat Style Log --- */
.chat-container {
flex: 1;
overflow-y: auto;
padding: 10px;
display: flex;
flex-direction: column;
gap: 12px;
}
.system-msg {
text-align: center;
font-size: 12px;
color: var(--text-secondary);
margin: 10px 0;
}
.msg-bubble {
max-width: 80%;
padding: 10px 16px;
border-radius: 18px;
font-size: 14px;
line-height: 1.4;
position: relative;
animation: popIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
word-break: break-all;
}
@keyframes popIn {
from { opacity: 0; transform: translateY(10px) scale(0.9); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
.msg-sent {
align-self: flex-end;
background: var(--accent-blue);
color: white;
border-bottom-right-radius: 4px;
}
.msg-received {
align-self: flex-start;
background: white;
color: var(--text-main);
border-bottom-left-radius: 4px;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
}
.time-stamp {
font-size: 10px;
opacity: 0.7;
margin-top: 4px;
display: block;
text-align: right;
}
frontend/script.js
/* frontend/script.js */
let ws = null;
// DOM 元素获取 (保持ID不变,适配新UI)
const inputs = {
ip: document.getElementById('serverIp'),
port: document.getElementById('serverPort'),
msg: document.getElementById('msgInput')
};
const btnConnect = document.getElementById('btnConnect');
const btnSend = document.getElementById('btnSend');
const btnClear = document.getElementById('btnClear'); // 新增清除按钮
const logArea = document.getElementById('logArea');
const statusText = document.getElementById('statusText');
const statusDot = document.getElementById('statusDot');
// 自动填充当前地址
window.onload = () => {
inputs.ip.value = window.location.hostname;
inputs.port.value = window.location.port;
};
// --- 事件监听 ---
btnConnect.addEventListener('click', toggleConnection);
btnSend.addEventListener('click', sendMessage);
btnClear.addEventListener('click', () => {
logArea.innerHTML = '<div class="system-msg">日志已清除</div>';
});
// --- 核心逻辑 ---
function toggleConnection() {
if (ws) {
// 如果已连接,则断开
ws.close();
return;
}
const ip = inputs.ip.value;
const port = inputs.port.value;
const url = `ws://${ip}:${port}`;
logToSystem(`正在连接到服务器...`);
try {
ws = new WebSocket(url);
ws.onopen = () => {
updateUIState(true);
logToSystem('连接成功');
};
ws.onmessage = (event) => {
logMessage(event.data, 'received');
};
ws.onclose = () => {
updateUIState(false);
ws = null;
logToSystem('服务器连接已断开');
};
ws.onerror = () => {
updateUIState(false);
statusDot.className = 'status-dot error';
statusText.innerText = '连接错误';
logToSystem('连接发生错误,请检查网络');
};
} catch (e) {
logToSystem('URL 格式错误');
}
}
function sendMessage() {
if (ws && ws.readyState === WebSocket.OPEN) {
const msg = inputs.msg.value;
if (!msg) return;
ws.send(msg);
logMessage(msg, 'sent');
}
}
// --- UI 渲染函数 (这里是改动最大的地方,为了漂亮) ---
function updateUIState(isConnected) {
if (isConnected) {
statusText.innerText = 'System Online';
statusDot.className = 'status-dot online';
btnConnect.innerHTML = '<span>断开连接</span>';
btnConnect.style.background = '#ff3b30'; // 红色
btnSend.disabled = false;
inputs.ip.disabled = true;
inputs.port.disabled = true;
} else {
statusText.innerText = '未连接服务器';
statusDot.className = 'status-dot offline';
btnConnect.innerHTML = '<span>连接系统</span>';
btnConnect.style.background = ''; // 恢复默认蓝色
btnSend.disabled = true;
inputs.ip.disabled = false;
inputs.port.disabled = false;
}
}
// 生成聊天气泡风格的消息
function logMessage(text, type) {
const div = document.createElement('div');
const time = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
div.className = `msg-bubble ${type === 'sent' ? 'msg-sent' : 'msg-received'}`;
div.innerHTML = `
${text}
<span class="time-stamp">${time}</span>
`;
logArea.appendChild(div);
scrollToBottom();
}
// 生成系统提示消息
function logToSystem(text) {
const div = document.createElement('div');
div.className = 'system-msg';
div.innerText = text;
logArea.appendChild(div);
scrollToBottom();
}
function scrollToBottom() {
logArea.scrollTo({
top: logArea.scrollHeight,
behavior: 'smooth'
});
}
效果图如下
恭喜您现在已经完成了内网的访问
更多推荐

所有评论(0)