增加侦察者和指挥者之间的通讯,指挥者可以接受和打回侦察者发送的消息,侦察者能看到指挥者的命令,相当于增加参谋功能。同时都能清空过多的消息使网页不显得拥挤

lzh
Kenneth 3 days ago
parent 16f89ac361
commit 048a9d6636

Binary file not shown.

File diff suppressed because it is too large Load Diff

@ -8,6 +8,13 @@ from flask_mysqldb import MySQL
import MySQLdb.cursors import MySQLdb.cursors
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from flask_login import LoginManager, UserMixin, login_user, login_required, current_user from flask_login import LoginManager, UserMixin, login_user, login_required, current_user
from wtforms import Form, StringField
from wtforms.validators import Length, EqualTo, ValidationError
import pymysql
conn = pymysql.connect(host="localhost", port=3306,
user="root", password="lin556688",
database="mydatabase", charset="utf8mb4")
# 初始化 Flask 应用 # 初始化 Flask 应用
app = Flask(__name__) app = Flask(__name__)
@ -81,9 +88,8 @@ def load_user(user_id):
return None return None
@app.route('/') @app.route('/')
def index(): def home():
current_app.logger.info("Index route accessed") return render_template('login.html')
return "Welcome to the Home Page!"
@app.route('/view_logs') @app.route('/view_logs')
@login_required @login_required
@ -149,6 +155,61 @@ def login():
return render_template('login.html') return render_template('login.html')
# 处理数据库语句
def con_my_sql(sql_code):
try:
conn.ping(reconnect=True)
cursor= conn.cursor(pymysql.cursors.DictCursor)
cursor.execute(sql_code)
conn.commit()
conn.close()
return cursor
except pymysql.MySQLError as err_massage:
conn.rollback()
conn.close()
return type(err_massage), err_massage
class RegisterForm(Form):
captcha = StringField(validators=[Length(min=4,max=4,message='校验码格式错误')])
username = StringField(validators=[Length(min=3,max=10,message='用户名长度必须在3到10个字符之间')])
password = StringField(validators=[Length(min=6,max=20,message='密码长度必须在6到20个字符之间')])
password_confirm = StringField(validators=[EqualTo('password',message='两次输入的密码不一致')])
def validate_captcha(self, filed):
if filed.data not in ['1111', '2222', '3333']:
raise ValidationError('校验码错误')
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'GET':
return render_template('register.html')
else:
form = RegisterForm(request.form)
if form.validate():
username = form.username.data
password = form.password.data
captcha = form.captcha.data
# 静态注册码进行角色注册
if captcha == "1111":
role = "侦查者"
if captcha == "2222":
role = "指挥者"
if captcha == "3333":
role = "攻击者"
code = "select * from users where username = '%s'" % username
cursor_ans = con_my_sql(code)
cursor_select = cursor_ans.fetchall()
if len(cursor_select) > 0:
return '用户已经存在 <a href="/">返回登录</a>'
else:
code = "INSERT INTO users(username, password, role) VALUES('%s', '%s', '%s')" % (username, password, role)
print(con_my_sql(code))
return '注册成功 <a href="/">返回登录</a>'
else:
print(form.errors)
return redirect(url_for('register'))
# 侦查者页面 # 侦查者页面
@app.route('/scout') @app.route('/scout')
@login_required @login_required
@ -156,8 +217,75 @@ def scout():
logger.info("Accessing scout page") logger.info("Accessing scout page")
if current_user.role != '侦查者': if current_user.role != '侦查者':
logger.warning("Unauthorized access to scout page") logger.warning("Unauthorized access to scout page")
return '用户无此页面访问权限 <a href="/">返回登录<a/>'
# 获取侦察者的通知
cursor = mysql.connection.cursor()
cursor.execute("SELECT * FROM notifications WHERE user_id = %s ORDER BY created_at DESC", (current_user.id,))
notifications = cursor.fetchall()
cursor.close()
logger.info(f"Notifications fetched: {notifications}")
return render_template('scout.html', notifications=notifications)
# 处理消息
@app.route('/handle_message/<int:message_id>/<action>', methods=['POST'])
@login_required
def handle_message(message_id, action):
logger.info(f"Handling message action: {action} for ID: {message_id}")
if current_user.role != '指挥者':
logger.warning("Unauthorized access to handle message")
return redirect(url_for('login')) return redirect(url_for('login'))
return render_template('scout.html')
cursor = mysql.connection.cursor()
if action == 'accept':
cursor.execute("UPDATE messages SET status = 'accepted' WHERE id = %s", (message_id,))
flash('消息已接受')
elif action == 'reject':
cursor.execute("UPDATE messages SET status = 'rejected' WHERE id = %s", (message_id,))
flash('消息已打回')
mysql.connection.commit()
cursor.close()
logger.info(f"Message status updated: {action}")
# 获取消息内容
cursor = mysql.connection.cursor()
cursor.execute("SELECT message FROM messages WHERE id = %s", (message_id,))
message_data = cursor.fetchone()
cursor.close()
logger.info(f"Message data fetched: {message_data}")
# 将结果反馈给侦察者
cursor = mysql.connection.cursor()
cursor.execute("SELECT id FROM users WHERE role = '侦查者'")
scouts = cursor.fetchall()
cursor.close()
logger.info(f"Scouts found: {scouts}")
for scout in scouts:
cursor = mysql.connection.cursor()
cursor.execute("INSERT INTO notifications (user_id, message_id, action, message) VALUES (%s, %s, %s, %s)",
(scout['id'], message_id, action, message_data['message']))
mysql.connection.commit()
cursor.close()
logger.info(f"Notification inserted for scout: {scout['id']}")
return redirect(url_for('commander'))
# 清除通知
@app.route('/clear_notifications', methods=['POST'])
@login_required
def clear_notifications():
user_id = request.json.get('user_id')
if not user_id:
return jsonify({'success': False, 'message': 'User ID is required'}), 400
cursor = mysql.connection.cursor()
cursor.execute("DELETE FROM notifications WHERE user_id = %s", (user_id,))
mysql.connection.commit()
cursor.close()
return jsonify({'success': True, 'message': 'Notifications cleared successfully'})
# 指挥者页面 # 指挥者页面
@app.route('/commander') @app.route('/commander')
@ -166,7 +294,7 @@ def commander():
logger.info("Accessing commander page") logger.info("Accessing commander page")
if current_user.role != '指挥者': if current_user.role != '指挥者':
logger.warning("Unauthorized access to commander page") logger.warning("Unauthorized access to commander page")
return redirect(url_for('login')) return '用户无此页面访问权限 <a href="/">返回登录<a/>'
# 获取特定目录下的所有文件和攻击坐标状态 # 获取特定目录下的所有文件和攻击坐标状态
directory = 'E:/_Ufo/0000jiegou/TheBattleCar/uploads' directory = 'E:/_Ufo/0000jiegou/TheBattleCar/uploads'
@ -185,7 +313,13 @@ def commander():
attacks = cursor.fetchall() attacks = cursor.fetchall()
cursor.close() cursor.close()
return render_template('commander.html', media_items=media_items, attacks=attacks) # 获取侦察者发送的消息
cursor = mysql.connection.cursor()
cursor.execute("SELECT id, message, photo_url, created_at, status FROM messages ORDER BY created_at DESC")
messages = cursor.fetchall()
cursor.close()
return render_template('commander.html', media_items=media_items, attacks=attacks, messages=messages)
# 指挥者发送攻击坐标 # 指挥者发送攻击坐标
@app.route('/send_attack', methods=['POST']) @app.route('/send_attack', methods=['POST'])
@ -211,6 +345,47 @@ def send_attack():
flash('攻击坐标已发送') flash('攻击坐标已发送')
return redirect(url_for('commander')) return redirect(url_for('commander'))
# 清除照片
@app.route('/clear_photos', methods=['POST'])
@login_required
def clear_photos():
logger.info("Handling clear photos request")
if current_user.role != '指挥者':
logger.warning("Unauthorized access to clear photos")
return redirect(url_for('login'))
directory = app.config['UPLOAD_FOLDER']
for filename in os.listdir(directory):
file_path = os.path.join(directory, filename)
try:
if os.path.isfile(file_path):
os.unlink(file_path)
except Exception as e:
logger.error(f"Error deleting file: {str(e)}")
flash(f"Error deleting file: {str(e)}")
logger.info("Photos cleared successfully")
flash('照片已清空')
return redirect(url_for('commander'))
# 清除侦察者发送的消息
@app.route('/clear_messages', methods=['POST'])
@login_required
def clear_messages():
logger.info("Handling clear messages request")
if current_user.role != '指挥者':
logger.warning("Unauthorized access to clear messages")
return redirect(url_for('login'))
cursor = mysql.connection.cursor()
cursor.execute("DELETE FROM messages")
mysql.connection.commit()
cursor.close()
logger.info("Messages cleared successfully")
flash('消息已清空')
return redirect(url_for('commander'))
# 攻击者页面 # 攻击者页面
@app.route('/attacker') @app.route('/attacker')
@login_required @login_required
@ -218,7 +393,7 @@ def attacker():
logger.info("Accessing attacker page") logger.info("Accessing attacker page")
if current_user.role != '攻击者': if current_user.role != '攻击者':
logger.warning("Unauthorized access to attacker page") logger.warning("Unauthorized access to attacker page")
return redirect(url_for('login')) return '用户无此页面访问权限 <a href="/">返回登录<a/>'
# 获取攻击坐标列表 # 获取攻击坐标列表
cursor = mysql.connection.cursor() cursor = mysql.connection.cursor()
@ -281,7 +456,7 @@ def send_message():
# 插入数据到 MySQL # 插入数据到 MySQL
cursor = mysql.connection.cursor() cursor = mysql.connection.cursor()
cursor.execute("INSERT INTO messages (message, photo_url) VALUES (%s, %s)", (message, photo_url)) cursor.execute("INSERT INTO messages (message, photo_url, status) VALUES (%s, %s, %s)", (message, photo_url, 'pending'))
mysql.connection.commit() mysql.connection.commit()
cursor.close() cursor.close()
logger.info(f"Message sent: {message}, Photo URL: {photo_url}") logger.info(f"Message sent: {message}, Photo URL: {photo_url}")
@ -296,7 +471,7 @@ def send_message():
def get_messages(): def get_messages():
logger.info("Handling get messages request") logger.info("Handling get messages request")
cursor = mysql.connection.cursor() cursor = mysql.connection.cursor()
cursor.execute("SELECT id, message, photo_url, created_at FROM messages") cursor.execute("SELECT id, message, photo_url, created_at, status FROM messages")
result = cursor.fetchall() result = cursor.fetchall()
cursor.close() cursor.close()
@ -306,7 +481,8 @@ def get_messages():
'id': row['id'], 'id': row['id'],
'message': row['message'], 'message': row['message'],
'photo_url': row['photo_url'], 'photo_url': row['photo_url'],
'created_at': row['created_at'] 'created_at': row['created_at'],
'status': row['status']
}) })
return jsonify(messages) return jsonify(messages)

@ -0,0 +1,20 @@
from cryptography.fernet import Fernet
import os
# 生成一个新的Fernet密钥
key = Fernet.generate_key()
# 指定密钥文件的路径
key_path = 'secret.key'
# 将密钥保存到文件中
with open(key_path, 'wb') as key_file:
key_file.write(key)
# 打印密钥以便查看
# print(f"Generated key: {key.decode()}")
# 确保密钥文件的权限设置为只读
os.chmod(key_path, 0o400)
print(f"Key has been saved to {key_path}")

@ -0,0 +1 @@
xO0rhaZ7_TEiOBm2ZH2b6L9VIiECc_aO8FeY5HGt2nY=

@ -0,0 +1,29 @@
// server.js
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketIo(server);
let clients = {};
io.on('connection', (socket) => {
console.log('A client connected:', socket.id);
clients[socket.id] = { status: 'connected' };
socket.on('disconnect', () => {
console.log('A client disconnected:', socket.id);
clients[socket.id] = { status: 'disconnected' };
});
// 定时发送客户端状态
setInterval(() => {
io.emit('clients_status', clients);
}, 5000);
});
server.listen(3000, () => {
console.log('Server listening on port 3000');
});

@ -0,0 +1,211 @@
*{
/*初始化 清除页面元素的内外边距*/
padding: 0;
margin: 0;
/*盒子模型*/
box-sizing: border-box;
}
body {
/*弹性布局 让页面元素垂直+水平居中*/
display: flex;
justify-content: center;
align-items: center;
/*让页面始终占浏览器可视区域总高度*/
height: 100vh;
/*背景渐变色*/
background: linear-gradient(#141e30,#243b55);
}
.login{
/*弹性布局 让子元素称为弹性项目*/
display: flex;
/*
*/
flex-direction: column;
/*
*/
align-items: center;
width: 400px;
padding: 40px;
background-color: rgba(0, 0, 0, 0.2);
box-shadow: 0 15px 25px rgba(0, 0, 0, 0.4);
}
.login h2{
color: #fff;
margin-bottom: 30px;
}
.login h3{
color: #fff;
margin-bottom: 10px;
}
.login .login_box {
/*相对定位*/
position: relative;
width: 100%;
}
.login .login_box input{
/*清除input框自带的边框和轮廓*/
outline: none;
border: none;
width: 100%;
padding: 10px 0;
margin-bottom: 30px;
color: #fff;
font-size: 16px;
border-bottom: 1px solid #fff;
/*背景颜色为透明色*/
background-color: transparent;
}
.login .login_box label{
position:absolute;
top: 0 ;
left: 0;
padding: 10px 0;
color: #fff;
/*auto
none
*/
/*这个就是两者的区别*/
pointer-events: none;
/*加个过度*/
transition: all 0.5s;
}
/*: focus input +
inputlabel*/
/*valid input
inputrequired
required input
required
西
*/
.login .login_box input:focus + label,
.login .login_box input:valid + label{
top: -20px;
color: #03e9f4;
font-size: 12px;
}
.login a{
/*overflow: hidden;*/
position: relative;
padding: 10px 20px;
color: #03e9f4;
/*取消a表现原有的下划线*/
text-decoration: none;
/*同样加个过渡*/
transition: all 0.5s;
}
.login a:hover {
color: #fff;
border-radius: 5px;
background-color: #03e9f4;
box-shadow: 0 0 5px #03e9f4,0 0 25px #03e9f4,0 0 50px #03e9f4,0 0 100px #03e9f4;
}
.login a span{
position: absolute;
}
.login a span:first-child {
top: 0;
left: -100%;
width: 100%;
height: 2px;
/*to right 就是往右边 下面的同理*/
background: linear-gradient(to right,transparent,#03e9f4);
/*动画 名称 时长 linear是匀速运动 infinite是无限次运动*/
animation: move1 1s linear infinite;
}
.login a span:nth-child(2){
right: 0;
top: -100%;
width: 2px;
height: 100%;
background: linear-gradient(transparent,#03e6f4);
/*这里多了个0.25s其实是延迟时间*/
animation: move2 1s linear 0.25s infinite;
}
.login a span:nth-child(3){
right: -100%;
bottom: 0;
width: 100%;
height: 2px;
background: linear-gradient(to left,transparent,#03e9f4);
animation: move3 1s linear 0.5s infinite;
}
.login a span:last-child{
left: 0;
bottom: -100%;
width: 2px;
height: 100%;
background: linear-gradient(#03e9f4,transparent);
animation: move4 1s linear 0.75s infinite;
}
/*写一下动画 */
@keyframes move1{
0%{
left: -100%;
}
50%,
100%{
left: 100%;
}
}
@keyframes move2{
0%{
top: -100%;
}
50%,
100%{
top: 100%;
}
}
@keyframes move3{
0%{
right: -100%;
}
50%,
100%{
right: 100%;
}
}
@keyframes move4{
0%{
bottom: -100%;
}
50%,
100%{
bottom: 100%;
}
}
.register-button {
display: block;
width: 100%;
height: 41px;
text-align: center;
background: #add8e6;
border: 3 solid #999;
border-radius: 5px;
}
.white-text {
color: #fff; /* 设置文本颜色为白色 */
}
.attack-button {
background: #add8e6; /* 背景颜色 */
}

@ -1,18 +1,22 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="zh-CN">
<head> <head>
<title>攻击者界面</title> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>攻击者</title>
<link rel="stylesheet" href="/static/login.css">
</head> </head>
<body> <body>
<h1>攻击坐标列表</h1> <div class="login">
<ul> <h2>攻击坐标列表</h2>
<ul class="white-text">
{% if attacks %} {% if attacks %}
{% for attack in attacks %} {% for attack in attacks %}
<li> <li>
<strong>坐标:</strong> {{ attack.coordinate }} - <em>{{ attack.created_at }}</em> <strong>坐标:</strong> {{ attack.coordinate }} - <em>{{ attack.created_at }}</em>
{% if not attack.attacked %} {% if not attack.attacked %}
<form action="{{ url_for('execute_attack', attack_id=attack.id) }}" method="post" style="display:inline;"> <form action="{{ url_for('execute_attack', attack_id=attack.id) }}" method="post" style="display:inline;">
<button type="submit">打击</button> <button type="submit" class="attack-button">打击</button>
</form> </form>
{% else %} {% else %}
<span>已完成打击</span> <span>已完成打击</span>
@ -23,5 +27,6 @@
<li>No attack coordinates available.</li> <li>No attack coordinates available.</li>
{% endif %} {% endif %}
</ul> </ul>
</div>
</body> </body>
</html> </html>

@ -1,46 +1,152 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>指挥者界面</title> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>指挥者</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(#141e30, #243b55);
}
h1, h2 {
text-align: center;
margin-top: 20px;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border-radius: 8px;
}
form {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
}
label, input, button {
margin: 10px 0;
}
input[type="text"] {
padding: 10px;
width: 100%;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 20px;
background-color: #add8e6;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #0056b3;
}
ul {
list-style-type: none;
padding: 0;
}
li {
padding: 10px;
margin: 10px 0;
border: 1px solid #ccc;
border-radius: 4px;
background-color: #f9f9f9;
}
li strong {
margin-right: 10px;
}
li span {
font-style: italic;
color: #777;
}
li img {
vertical-align: middle;
margin-right: 10px;
max-width: 100px;
}
.center-button{
text-align: center;
}
</style>
</head> </head>
<body> <body>
<h1>指挥者界面</h1> <div class="container">
<h1>指挥者</h1>
<h2>发送攻击坐标</h2>
<form action="{{ url_for('send_attack') }}" method="post">
<label for="coordinate">攻击坐标:</label>
<input type="text" id="coordinate" name="coordinate" required>
<button type="submit">发送消息</button>
</form>
<h2>发送攻击坐标</h2> <h2>攻击坐标状态</h2>
<form action="{{ url_for('send_attack') }}" method="post"> <ul class="white-text">
<label for="coordinate">攻击坐标:</label> {% if attacks %}
<input type="text" id="coordinate" name="coordinate" required> {% for attack in attacks %}
<button type="submit">发送消息</button> <li>
</form> <strong>坐标:</strong> {{ attack.coordinate }}
- <em>{{ attack.created_at }}</em>
- <span>{% if attack.attacked %}已完成打击{% else %}等待打击{% endif %}</span>
</li>
{% endfor %}
{% else %}
<li>No attack coordinates available.</li>
{% endif %}
</ul>
<h2>攻击坐标状态</h2> <h2>查看照片</h2>
<ul> <ul>
{% if attacks %} {% if media_items %}
{% for attack in attacks %} {% for filename in media_items %}
<li> <li>
<strong>坐标:</strong> {{ attack.coordinate }} <a href="{{ url_for('uploaded_file', filename=filename) }}" target="_blank">
- <em>{{ attack.created_at }}</em> <img src="{{ url_for('uploaded_file', filename=filename) }}" width="100">
- <span>{% if attack.attacked %}已完成打击{% else %}等待打击{% endif %}</span> </a>
</li> </li>
{% endfor %} {% endfor %}
{% else %} {% else %}
<li>No attack coordinates available.</li> <li>No images found in the directory.</li>
{% endif %} {% endif %}
</ul> </ul>
<form action="{{ url_for('clear_photos') }}" method="post" class="center-button">
<button type="submit">清空照片</button>
</form>
<h2>查看照片</h2> <h2>侦察者发送的消息和图片</h2>
<ul> <ul>
{% if media_items %} {% if messages %}
{% for filename in media_items %} {% for message in messages %}
<li> <li>
<a href="{{ url_for('uploaded_file', filename=filename) }}" target="_blank"> <strong>消息:</strong> {{ message.message }}
<img src="{{ url_for('uploaded_file', filename=filename) }}" width="100"> - <em>{{ message.created_at }}</em>
</a> - <span>状态: {{ message.status }}</span>
</li> {% if message.photo_url %}
{% endfor %} <img src="{{ message.photo_url }}" alt="Message Photo">
{% else %} {% endif %}
<li>No images found in the directory.</li> <form action="{{ url_for('handle_message', message_id=message.id, action='accept') }}" method="post" style="display: inline;">
{% endif %} <button type="submit">接受</button>
</ul> </form>
<form action="{{ url_for('handle_message', message_id=message.id, action='reject') }}" method="post" style="display: inline;">
<button type="submit">打回</button>
</form>
</li>
{% endfor %}
{% else %}
<li>没有消息和图片。</li>
{% endif %}
</ul>
<form action="{{ url_for('clear_messages') }}" method="post" class="center-button">
<button type="submit">清空消息</button>
</form>
</div>
</body> </body>
</html> </html>

@ -1,25 +1,73 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="zh-CN">
<head> <head>
<title>Login</title> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网络信息系统登录</title>
<link rel="stylesheet" href="/static/login.css">
</head> </head>
<body> <body>
<h2>Login</h2> <div class="login">
<form method="post"> <h2>网络信息系统登录</h2>
<label for="username">Username:</label> <div class="login_box">
<input type="text" id="username" name="username" required><br><br> <!-- 用户名输入框和标签 -->
<input type="text" name="username" id="username" required />
<label for="username">用户名</label>
</div>
<div class="login_box">
<!-- 密码输入框和标签 -->
<input type="password" name="password" id="password" required />
<label for="password">密码</label>
</div>
<!-- 登录链接这里使用JavaScript来阻止默认链接行为并可能需要添加自定义的JavaScript来处理登录逻辑 -->
<a href="javascript:void(0);" onclick="handleLogin()">
登录
<span></span>
<span></span>
<span></span>
<span></span>
</a>
<label for="password">Password:</label> <a href="javascript:void(0);" onclick="window.location.href='{{ url_for('register') }}'">
<input type="password" id="password" name="password" required><br><br> 注册
<span></span>
<span></span>
<span></span>
<span></span>
</a>
<label for="role">Role:</label> <!-- 隐藏的表单,用于实际提交登录数据到服务器 -->
<select id="role" name="role" required> <form id="loginForm" method="POST" action="{{ url_for('login') }}" style="display:none;">
<option value="侦查者">侦查者</option> <input type="text" name="username" id="hiddenUsername" />
<option value="指挥者">指挥者</option> <input type="password" name="password" id="hiddenPassword" />
<option value="攻击者">攻击者</option>
</select><br><br>
<button type="submit">Login</button>
</form> </form>
<script>
function handleLogin() {
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
document.getElementById('hiddenUsername').value = username;
document.getElementById('hiddenPassword').value = password;
document.getElementById('loginForm').submit();
}
function displayMessages(messages) {
var messageDiv = document.getElementById('messages');
messageDiv.innerHTML = '';
messages.forEach(function(message) {
var p = document.createElement('p');
p.textContent = message;
if (message.includes('error')) {
p.style.color = 'red';
} else {
p.style.color = 'green';
}
messageDiv.appendChild(p);
});
}
</script>
</div>
</body> </body>
</html> </html>

@ -0,0 +1,39 @@
<!-- templates/network.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Network Connection Status</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.min.js"></script>
<style>
body { font-family: Arial, sans-serif; }
#network { padding: 20px; }
.client {
color: white;
padding: 10px;
margin: 5px;
border-radius: 5px;
}
.connected { background-color: green; }
.disconnected { background-color: red; }
</style>
</head>
<body>
<h1>Client Connection Status</h1>
<div id="network"></div>
<script>
const socket = io('http://localhost:3000');
const networkElement = document.getElementById('network');
socket.on('clients_status', (clients) => {
networkElement.innerHTML = '';
for (const [id, info] of Object.entries(clients)) {
const clientElement = document.createElement('div');
clientElement.className = 'client ' + info.status;
clientElement.textContent = `Client ID: ${id} - Status: ${info.status}`;
networkElement.appendChild(clientElement);
}
});
</script>
</body>
</html>

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网络信息系统注册</title>
<link rel="stylesheet" href="/static/login.css">
</head>
<body>
<div class="login">
<form action="/register" method="post">
<h2>网络信息系统注册</h2>
<div class="login_box">
<input type="text" name="captcha" id="captcha" required />
<label for="captcha">注册码</label>
</div>
<div class="login_box">
<input type="text" name="username" id="username" required />
<label for="username">用户名</label>
</div>
<div class="login_box">
<input type="password" name="password" id="password" required />
<label for="password">密码</label>
</div>
<div class="login_box">
<input type="password" name="password_confirm" id="password_confirm" required />
<label for="password_confirm">确认密码</label>
</div>
<input type="submit" value="注册" class="register-button">
</div>
</body>
</html>

@ -3,11 +3,82 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Scout Page</title> <title>侦查者</title>
<link rel="stylesheet" href="/static/login.css">
<style>
body {
color: black; /* 字体颜色改为白色 */
font-size: 12px; /* 字体大小调小 */
}
.login h2, .login button {
color: white; /* 按钮和标题字体颜色也改为白色 */
font-size: 14px; /* 调整按钮和标题的字体大小 */
}
.notification-list {
list-style-type: none;
padding: 0;
}
.notification-list li {
padding: 10px;
margin: 10px 0;
border: 1px solid #ccc;
border-radius: 4px;
background-color: #f9f9f9;
word-wrap: break-word; /* 防止长单词溢出 */
}
.notification-list li strong {
margin-right: 10px;
}
.notification-list li span {
font-style: italic;
color: #777;
}
</style>
</head> </head>
<body> <body>
<h1>Scout Page</h1> <div class="login">
<button onclick="window.location.href='http://192.168.78.178:5000/'">控制小车</button> <h2>侦查者</h2>
<button onclick="window.location.href='{{ url_for('send_message') }}'">发送消息</button> <button onclick="window.location.href='http://192.168.146.178:5000/'" class="register-button">控制小车</button>
<button onclick="window.location.href='{{ url_for('send_message') }}'" class="register-button">发送消息</button>
<h2>通知</h2>
<ul class="notification-list">
{% if notifications %}
{% for notification in notifications %}
<li>
消息ID: {{ notification.message_id }} - 操作: {{ notification.action }} - 时间: {{ notification.created_at }} - 内容: {{ notification.message }}
</li>
{% endfor %}
{% else %}
<li>没有通知。</li>
{% endif %}
</ul>
<button onclick="clearNotifications()" class="register-button">清空通知</button>
</div>
<script>
function clearNotifications() {
// 发送请求清空通知
fetch('/clear_notifications', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ user_id: {{ current_user.id }} })
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload(); // 刷新页面以显示更新后的通知列表
} else {
alert('清空通知失败');
}
})
.catch(error => {
console.error('Error:', error);
alert('清空通知失败');
});
}
</script>
</body> </body>
</html> </html>

@ -1,18 +1,21 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="zh-CN">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Send Message</title> <title>发送消息</title>
<link rel="stylesheet" href="/static/login.css">
</head> </head>
<body> <body>
<h1>Send Message</h1> <div class="login">
<h2>发送消息</h2>
<form method="post" enctype="multipart/form-data"> <form method="post" enctype="multipart/form-data">
<label for="message">Message:</label> <h3>消息内容</h3>
<textarea id="message" name="message" rows="4" cols="50" required></textarea><br><br> <textarea id="message" name="message" rows="4" cols="50" required></textarea><br><br>
<label for="photo">Upload Photo:</label> <h3>选择图片</h3>
<input type="file" id="photo" name="photo"><br><br> <input type="file" id="photo" name="photo"><br><br>
<button type="submit">Send Message</button> <button type="submit" class="register-button">发送</button>
</form> </form>
</div>
</body> </body>
</html> </html>
Loading…
Cancel
Save