完成管理端代码

This commit is contained in:
Jingfan Ke 2024-06-14 17:51:13 +08:00
parent cfbaf585a7
commit 963a80a670
18 changed files with 384 additions and 233 deletions

View File

@ -1,4 +1,4 @@
INSERT INTO Airports (`ID`, `Name`, City) VALUES INSERT INTO ServiceDatabase.Airports (`ID`, `Name`, City) VALUES
('NAY', '北京南苑机场', '北京'), ('NAY', '北京南苑机场', '北京'),
('PEK', '北京首都国际机场', '北京'), ('PEK', '北京首都国际机场', '北京'),
('TSN', '天津滨海国际机场', '天津'), ('TSN', '天津滨海国际机场', '天津'),

View File

@ -0,0 +1,17 @@
DROP USER IF EXISTS 'serviceAgent'@'%';
DROP USER IF EXISTS 'managerAgent'@'%';
CREATE USER 'serviceAgent'@'%' IDENTIFIED BY 'password123';
CREATE USER 'managerAgent'@'%' IDENTIFIED BY 'password123';
GRANT SELECT ON ManagerDatabase.Managers TO 'managerAgent'@'%';
GRANT ALL PRIVILEGES ON ServiceDatabase.Flights TO 'managerAgent'@'%';
GRANT ALL PRIVILEGES ON ServiceDatabase.Passengers TO 'serviceAgent'@'%';
GRANT ALL PRIVILEGES ON ServiceDatabase.Orders TO 'serviceAgent'@'%';
GRANT ALL PRIVILEGES ON ServiceDatabase.Users TO 'serviceAgent'@'%';
GRANT ALL PRIVILEGES ON ServiceDatabase.Airports TO 'serviceAgent'@'%';
GRANT ALL PRIVILEGES ON ServiceDatabase.Tickets TO 'serviceAgent'@'%';
GRANT SELECT, UPDATE (First_class_seats_remaining, Business_class_seats_remaining, Economy_class_seats_remaining)
ON ServiceDatabase.Flights TO 'serviceAgent'@'%';
FLUSH PRIVILEGES;

View File

@ -0,0 +1,3 @@
ID,Airline,Departure_airport,Arrival_airport,Departure_time,Arrival_time,First_class_seats_remaining,Business_class_seats_remaining,Economy_class_seats_remaining,First_class_price,Business_class_price,Economy_class_price,Status
FL321,AirlineE,PEK,PVG,2024-06-16 11:00:00,2024-06-16 12:30:00,5,10,100,2000.00,1000.00,500.00,未知
FL421,AirlineG,PEK,PVG,2024-06-16 11:00:00,2024-06-16 12:30:00,3,10,100,2000.00,1000.00,500.00,未知
1 ID Airline Departure_airport Arrival_airport Departure_time Arrival_time First_class_seats_remaining Business_class_seats_remaining Economy_class_seats_remaining First_class_price Business_class_price Economy_class_price Status
2 FL321 AirlineE PEK PVG 2024-06-16 11:00:00 2024-06-16 12:30:00 5 10 100 2000.00 1000.00 500.00 未知
3 FL421 AirlineG PEK PVG 2024-06-16 11:00:00 2024-06-16 12:30:00 3 10 100 2000.00 1000.00 500.00 未知

View File

@ -1,4 +1,4 @@
INSERT INTO Flights (ID, Airline, Departure_airport, Arrival_airport, Departure_time, Arrival_time, First_class_seats_remaining, Business_class_seats_remaining, Economy_class_seats_remaining, First_class_price, Business_class_price, Economy_class_price, `Status`) VALUES INSERT INTO ServiceDatabase.Flights (ID, Airline, Departure_airport, Arrival_airport, Departure_time, Arrival_time, First_class_seats_remaining, Business_class_seats_remaining, Economy_class_seats_remaining, First_class_price, Business_class_price, Economy_class_price, `Status`) VALUES
('FL001', 'AirlineA', 'PEK', 'SHA', '2024-06-14 08:00:00', '2024-06-14 10:00:00', 5, 10, 100, 2000.00, 1000.00, 500.00, '已降落'), ('FL001', 'AirlineA', 'PEK', 'SHA', '2024-06-14 08:00:00', '2024-06-14 10:00:00', 5, 10, 100, 2000.00, 1000.00, 500.00, '已降落'),
('FL002', 'AirlineB', 'TSN', 'PVG', '2024-06-14 09:00:00', '2024-06-14 11:00:00', 3, 15, 80, 2100.00, 1100.00, 600.00, '已降落'), ('FL002', 'AirlineB', 'TSN', 'PVG', '2024-06-14 09:00:00', '2024-06-14 11:00:00', 3, 15, 80, 2100.00, 1100.00, 600.00, '已降落'),
('FL003', 'AirlineC', 'SHA', 'SZX', '2024-06-14 07:00:00', '2024-06-14 09:00:00', 2, 12, 70, 2200.00, 1200.00, 550.00, '已降落'), ('FL003', 'AirlineC', 'SHA', 'SZX', '2024-06-14 07:00:00', '2024-06-14 09:00:00', 2, 12, 70, 2200.00, 1200.00, 550.00, '已降落'),

View File

@ -1,9 +1,13 @@
DROP TABLE IF EXISTS Managers; DROP DATABASE IF EXISTS ManagerDatabase;
CREATE DATABASE ManagerDatabase;
ALTER DATABASE ManagerDatabase CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
CREATE TABLE Managers ( DROP TABLE IF EXISTS ManagerDatabase.Managers;
CREATE TABLE ManagerDatabase.Managers (
ID VARCHAR(255) PRIMARY KEY, ID VARCHAR(255) PRIMARY KEY,
`Password` VARCHAR(255) NOT NULL `Password` VARCHAR(255) NOT NULL
); );
INSERT INTO Managers INSERT INTO ManagerDatabase.Managers
VALUES ('Admin', 'e10adc3949ba59abbe56e057f20f883e'); VALUES ('Admin', 'e10adc3949ba59abbe56e057f20f883e');

View File

@ -1,11 +1,15 @@
DROP TABLE IF EXISTS Tickets; DROP DATABASE IF EXISTS ServiceDatabase;
DROP TABLE IF EXISTS Orders; CREATE DATABASE ServiceDatabase;
DROP TABLE IF EXISTS Users; ALTER DATABASE ServiceDatabase CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
DROP TABLE IF EXISTS Flights;
DROP TABLE IF EXISTS Airports;
DROP TABLE IF EXISTS Passengers;
CREATE TABLE Passengers ( DROP TABLE IF EXISTS ServiceDatabase.Tickets;
DROP TABLE IF EXISTS ServiceDatabase.Orders;
DROP TABLE IF EXISTS ServiceDatabase.Users;
DROP TABLE IF EXISTS ServiceDatabase.Flights;
DROP TABLE IF EXISTS ServiceDatabase.Airports;
DROP TABLE IF EXISTS ServiceDatabase.Passengers;
CREATE TABLE ServiceDatabase.Passengers (
ID VARCHAR(18) PRIMARY KEY, ID VARCHAR(18) PRIMARY KEY,
`Name` VARCHAR(255) NOT NULL, `Name` VARCHAR(255) NOT NULL,
Phone_number BIGINT NOT NULL, Phone_number BIGINT NOT NULL,
@ -13,20 +17,20 @@ CREATE TABLE Passengers (
CHECK (REGEXP_LIKE(Phone_number, '^\\d{11}$')) CHECK (REGEXP_LIKE(Phone_number, '^\\d{11}$'))
); );
CREATE TABLE Users ( CREATE TABLE ServiceDatabase.Users (
Phone_number BIGINT PRIMARY KEY, Phone_number BIGINT PRIMARY KEY,
Username VARCHAR(255) NOT NULL, Username VARCHAR(255) NOT NULL,
`Password` VARCHAR(255) NOT NULL, `Password` VARCHAR(255) NOT NULL,
CHECK (REGEXP_LIKE(Phone_number, '^\\d{11}$')) CHECK (REGEXP_LIKE(Phone_number, '^\\d{11}$'))
); );
CREATE TABLE Airports ( CREATE TABLE ServiceDatabase.Airports (
ID VARCHAR(3) PRIMARY KEY, -- 机场三字码 ID VARCHAR(3) PRIMARY KEY, -- 机场三字码
`Name` VARCHAR(255) UNIQUE NOT NULL, `Name` VARCHAR(255) UNIQUE NOT NULL,
City VARCHAR(255) NOT NULL City VARCHAR(255) NOT NULL
); );
CREATE TABLE Flights ( CREATE TABLE ServiceDatabase.Flights (
ID VARCHAR(255) PRIMARY KEY, ID VARCHAR(255) PRIMARY KEY,
Airline VARCHAR(255) NOT NULL, Airline VARCHAR(255) NOT NULL,
Departure_airport VARCHAR(3) NOT NULL, Departure_airport VARCHAR(3) NOT NULL,
@ -45,7 +49,7 @@ CREATE TABLE Flights (
CHECK (Departure_time < Arrival_time) CHECK (Departure_time < Arrival_time)
); );
CREATE TABLE Orders ( CREATE TABLE ServiceDatabase.Orders (
ID INT AUTO_INCREMENT PRIMARY KEY, ID INT AUTO_INCREMENT PRIMARY KEY,
Order_time DATETIME NOT NULL, Order_time DATETIME NOT NULL,
Paid TINYINT NOT NULL, Paid TINYINT NOT NULL,
@ -53,7 +57,7 @@ CREATE TABLE Orders (
FOREIGN KEY (User_phone_number) REFERENCES Users(Phone_number) ON DELETE CASCADE FOREIGN KEY (User_phone_number) REFERENCES Users(Phone_number) ON DELETE CASCADE
); );
CREATE TABLE Tickets ( CREATE TABLE ServiceDatabase.Tickets (
ID INT AUTO_INCREMENT PRIMARY KEY, ID INT AUTO_INCREMENT PRIMARY KEY,
Price DECIMAL(7, 2) NOT NULL, Price DECIMAL(7, 2) NOT NULL,
FlightID VARCHAR(255) NOT NULL, FlightID VARCHAR(255) NOT NULL,

Binary file not shown.

Binary file not shown.

View File

@ -1,8 +1,8 @@
db = { db = {
'host':'localhost', 'host':'localhost',
'user':'kejingfan', 'user':'managerAgent',
'password':'KJF2811879', 'password':'password123',
'database':'TESTDB' 'database':'ServiceDatabase'
} }
SECRET_KEY = 'ILOVEDATABASETECH' SECRET_KEY = 'ILOVEDATABASETECH'

View File

@ -1,6 +1,8 @@
from flask import render_template, request, g, redirect, url_for, session from flask import render_template, request, g, redirect, url_for, session, jsonify
from .config import db from .config import db
import pymysql import pymysql
import csv
import io
def index(): def index():
if request.method == 'GET': if request.method == 'GET':
@ -8,25 +10,132 @@ def index():
return redirect(url_for("login")) return redirect(url_for("login"))
flightID = request.args.get('flightID') flightID = request.args.get('flightID')
if flightID:
conn = pymysql.connect(**db) conn = pymysql.connect(**db)
cursor = conn.cursor(pymysql.cursors.DictCursor) cursor = conn.cursor(pymysql.cursors.DictCursor)
search_sql = """ """ search_sql = """SELECT * FROM Flights WHERE ID = %s"""
cursor.execute(search_sql, (flightID, )) cursor.execute(search_sql, (flightID, ))
flights = cursor.fetchall() flight = cursor.fetchone()
cursor.close() cursor.close()
conn.close() conn.close()
return render_template( return render_template(
'search.html', 'index.html',
flights=flights, flight=flight,
username=g.user username=g.user
) )
else:
def logout(): return render_template('index.html', username=g.user)
session.clear()
session.pop('user_id', None)
return redirect(url_for('login'))
def modify(): def modify():
pass if request.method == 'POST':
flight_id = request.form.get('flightID')
first_class_change = int(request.form.get('first_class_change', 0))
business_class_change = int(request.form.get('business_class_change', 0))
economy_class_change = int(request.form.get('economy_class_change', 0))
first_class_price = float(request.form.get('first_class_price', 0))
business_class_price = float(request.form.get('business_class_price', 0))
economy_class_price = float(request.form.get('economy_class_price', 0))
status = request.form.get('status', '未知')
conn = pymysql.connect(**db)
cursor = conn.cursor(pymysql.cursors.DictCursor)
try:
# 查询当前座位数
cursor.execute("SELECT First_class_seats_remaining, Business_class_seats_remaining, Economy_class_seats_remaining FROM Flights WHERE ID = %s", (flight_id,))
current_seats = cursor.fetchone()
new_first_class_seats = current_seats['First_class_seats_remaining'] + first_class_change
new_business_class_seats = current_seats['Business_class_seats_remaining'] + business_class_change
new_economy_class_seats = current_seats['Economy_class_seats_remaining'] + economy_class_change
# 检查余座数是否为负值
if new_first_class_seats < 0 or new_business_class_seats < 0 or new_economy_class_seats < 0:
return jsonify({'message': '座位变化后余座数不能为负值'}), 400
# 更新座位数和价格
update_sql = """
UPDATE Flights
SET First_class_seats_remaining = %s, Business_class_seats_remaining = %s, Economy_class_seats_remaining = %s,
First_class_price = %s, Business_class_price = %s, Economy_class_price = %s, Status = %s
WHERE ID = %s
"""
cursor.execute(update_sql, (new_first_class_seats, new_business_class_seats, new_economy_class_seats,
first_class_price, business_class_price, economy_class_price, status, flight_id))
conn.commit()
except Exception as e:
print(e)
conn.rollback()
return jsonify({'message': '数据库错误,请稍后再试'}), 500
finally:
cursor.close()
conn.close()
return jsonify({'message': '座位数、价格和状态更新成功'}), 200
def delete_flight():
if request.method == 'DELETE':
flight_id = request.args.get('flightID')
conn = pymysql.connect(**db)
cursor = conn.cursor()
try:
delete_sql = "DELETE FROM Flights WHERE ID = %s"
cursor.execute(delete_sql, (flight_id,))
conn.commit()
except Exception as e:
print(e)
conn.rollback()
return jsonify({'message': '数据库错误,请稍后再试', 'success': False}), 500
finally:
cursor.close()
conn.close()
return jsonify({'message': '航班删除成功', 'success': True}), 200
from flask import flash, redirect, url_for, jsonify, request
import pymysql
import csv
import io
from .config import db
def upload_csv():
if request.method == 'POST':
file = request.files['file']
if not file:
flash('没有文件上传', 'error')
return redirect(url_for('index'))
conn = pymysql.connect(**db)
cursor = conn.cursor()
try:
stream = io.StringIO(file.stream.read().decode("UTF8"), newline=None)
csv_input = csv.reader(stream)
next(csv_input) # 跳过表头
for row in csv_input:
cursor.execute("""
INSERT INTO Flights (ID, Airline, Departure_airport, Arrival_airport, Departure_time, Arrival_time,
First_class_seats_remaining, Business_class_seats_remaining, Economy_class_seats_remaining,
First_class_price, Business_class_price, Economy_class_price, Status)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
""", row)
conn.commit()
flash('航班批量添加成功', 'success')
except Exception as e:
print(e)
conn.rollback()
flash(f'文件处理错误:{e}', 'error')
finally:
cursor.close()
conn.close()
return redirect(url_for('index'))

View File

@ -1,4 +1,4 @@
from flask import request, jsonify, session, url_for, render_template from flask import request, jsonify, session, url_for, render_template, redirect
from .config import db from .config import db
import pymysql import pymysql
@ -32,3 +32,8 @@ def login():
except Exception as e: except Exception as e:
print(e) print(e)
return jsonify({'message': '数据库错误,请稍后再试'}), 500 return jsonify({'message': '数据库错误,请稍后再试'}), 500
def logout():
session.clear()
session.pop('user_id', None)
return redirect(url_for('login'))

View File

@ -39,7 +39,15 @@ def login():
@app.route('/logout') @app.route('/logout')
def logout(): def logout():
return func.index.logout() return func.login.logout()
@app.route("/delete_flight", methods=['DELETE'])
def delete_flight():
return func.index.delete_flight()
@app.route("/upload_csv", methods=['POST'])
def upload_csv():
return func.index.upload_csv()
if __name__ == "__main__": if __name__ == "__main__":
app.run( app.run(

View File

@ -81,7 +81,7 @@ header {
min-width: 160px; min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1; z-index: 1;
right: 0; /* 确保下拉菜单靠右对齐 */ right: 0;
} }
.dropdown-content a { .dropdown-content a {
@ -130,6 +130,21 @@ th {
background-color: #f2f2f2; background-color: #f2f2f2;
} }
.flight-row {
transition: transform 0.3s ease, box-shadow 0.3s ease, border-radius 0.3s ease;
}
.flight-row:hover {
transform: scale(1.02);
box-shadow: 0 0 10px rgba(28, 108, 178, 0.5);
border-radius: 10px;
}
.flight-info th, .flight-info td {
padding: 10px;
border: 1px solid #ccc;
}
.no-results { .no-results {
text-align: center; text-align: center;
color: red; color: red;
@ -144,7 +159,7 @@ footer {
padding: 10px 0; padding: 10px 0;
width: 100%; width: 100%;
position: relative; position: relative;
margin-top: auto; /* 将footer推到页面底部 */ margin-top: auto;
} }
.content { .content {
@ -153,70 +168,28 @@ footer {
border-radius: 10px; border-radius: 10px;
text-align: center; text-align: center;
width: 80%; width: 80%;
max-width: 800px;
margin: 20px auto; margin: 20px auto;
position: relative; position: relative;
} }
.tabcontent { .form-group {
display: block;
padding: 20px;
}
.form-row {
display: flex;
justify-content: flex-start; /* Left-align the form items */
margin-bottom: 15px; margin-bottom: 15px;
text-align: left;
} }
.form-row label { .form-group label {
flex: 0 0 120px; /* Fixed width for labels */ display: block;
margin-right: 10px; margin-bottom: 5px;
text-align: right;
line-height: 32px;
} }
.form-row input, .form-group input {
.form-row select { width: 100%;
flex: 1;
padding: 8px; padding: 8px;
font-size: 16px; font-size: 16px;
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 5px; border-radius: 5px;
} }
.form-row.form-row-center {
justify-content: center; /* Center-align the button */
}
.passenger-input {
display: flex;
align-items: center;
gap: 5px;
}
.passenger-input button {
padding: 5px 10px;
font-size: 18px;
background-color: #1c6cb2;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.passenger-input button:hover {
background-color: #155a8c;
}
.passenger-input input {
width: 50px;
text-align: center;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 5px;
}
.btn { .btn {
padding: 10px 20px; padding: 10px 20px;
background-color: #1c6cb2; background-color: #1c6cb2;
@ -231,21 +204,3 @@ footer {
.btn:hover { .btn:hover {
background-color: #155a8c; background-color: #155a8c;
} }
.error-message {
color: red;
font-size: 12px;
margin-top: 5px;
text-align: left;
}
/* Add animations for flight rows */
.flight-row {
transition: transform 0.3s ease, box-shadow 0.3s ease, border-radius 0.3s ease;
}
.flight-row:hover {
transform: scale(1.02);
box-shadow: 0 0 10px rgba(28, 108, 178, 0.5);
border-radius: 10px;
}

View File

@ -1,16 +1,3 @@
function validateForm() {
var departure = document.getElementById('departure').value;
var destination = document.getElementById('destination').value;
var warning = document.getElementById('destination-warning');
if (departure === destination) {
warning.textContent = '出发地和目的地不能相同';
return false;
} else {
warning.textContent = '';
}
return true;
}
function increment() { function increment() {
var passengers = document.getElementById("passengers"); var passengers = document.getElementById("passengers");
var value = parseInt(passengers.value, 10); var value = parseInt(passengers.value, 10);
@ -26,17 +13,3 @@ function decrement() {
passengers.value = value - 1; passengers.value = value - 1;
} }
} }
document.addEventListener('DOMContentLoaded', function() {
// Set default date to tomorrow
var departureDate = document.getElementById('departure-date');
if (!departureDate.value) {
var today = new Date();
var tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
var month = ('0' + (tomorrow.getMonth() + 1)).slice(-2);
var day = ('0' + tomorrow.getDate()).slice(-2);
var year = tomorrow.getFullYear();
departureDate.value = `${year}-${month}-${day}`;
}
});

View File

@ -5,8 +5,19 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KJF航班订票</title> <title>KJF航班订票</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/index.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='css/index.css') }}">
<script src="{{ url_for('static', filename='js/index.js') }}" defer></script> <script>
<script src="https://cdn.bootcss.com/blueimp-md5/2.12.0/js/md5.min.js"></script> window.onload = function() {
{% with messages = get_flashed_messages() %}
{% if messages %}
var message = "";
{% for msg in messages %}
message += "{{ msg }}\n";
{% endfor %}
alert(message);
{% endif %}
{% endwith %}
};
</script>
</head> </head>
<body> <body>
<header> <header>
@ -14,14 +25,12 @@
<div class="logo">KJF航班订票</div> <div class="logo">KJF航班订票</div>
<div class="nav-buttons"> <div class="nav-buttons">
<a href="{{ url_for('index') }}">首页</a> <a href="{{ url_for('index') }}">首页</a>
<a href="{{ url_for('order_list') }}">我的订单</a>
</div> </div>
<div class="user-menu"> <div class="user-menu">
<span>{{ username }}</span> <span>{{ username }}</span>
<div class="dropdown"> <div class="dropdown">
<button class="dropbtn"></button> <button class="dropbtn"></button>
<div class="dropdown-content"> <div class="dropdown-content">
<a href="{{ url_for('modify') }}">修改账户信息</a>
<a href="{{ url_for('logout') }}">退出登录</a> <a href="{{ url_for('logout') }}">退出登录</a>
</div> </div>
</div> </div>
@ -29,118 +38,182 @@
</div> </div>
</header> </header>
<main> <main>
<div class="slides">
<ul id="slide-container">
{% for image in images %}
<li>
<a href="{{ image.link }}" target="_blank">
<img src="{{ image.src }}">
</a>
</li>
{% endfor %}
</ul>
</div>
<div class="content"> <div class="content">
<div id="ticket" class="tabcontent" style="display: block;"> <form method="get" action="{{ url_for('index') }}">
<form action="{{ url_for('search') }}" method="get" class="search-form" onsubmit="return validateForm()"> <div class="form-group">
<div class="form-row"> <label for="flightID">航班号:</label>
<label for="departure">出发地:</label> <input type="text" id="flightID" name="flightID" placeholder="请输入航班号">
<select id="departure" name="departure">
{% for city in cities %}
<option value="{{ city }}" {% if city == "北京" %}selected{% endif %}>{{ city }}</option>
{% endfor %}
</select>
</div> </div>
<div class="form-row"> <button type="submit" class="btn">查询</button>
<label for="destination">目的地:</label> </form>
<select id="destination" name="destination">
{% for city in cities %} {% if flight %}
<option value="{{ city }}" {% if city == "上海" %}selected{% endif %}>{{ city }}</option> <div class="flight-info">
{% endfor %} <table>
</select> <thead>
<div id="destination-warning" class="error-message"></div> <tr>
<th>航班号</th>
<th>航空公司</th>
<th>出发机场</th>
<th>到达机场</th>
<th>出发时间</th>
<th>到达时间</th>
<th>头等舱剩余座位</th>
<th>商务舱剩余座位</th>
<th>经济舱剩余座位</th>
<th>头等舱价格</th>
<th>商务舱价格</th>
<th>经济舱价格</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr class="flight-row">
<td>{{ flight.ID }}</td>
<td>{{ flight.Airline }}</td>
<td>{{ flight.Departure_airport }}</td>
<td>{{ flight.Arrival_airport }}</td>
<td>{{ flight.Departure_time }}</td>
<td>{{ flight.Arrival_time }}</td>
<td id="first-class-seats">{{ flight.First_class_seats_remaining }}</td>
<td id="business-class-seats">{{ flight.Business_class_seats_remaining }}</td>
<td id="economy-class-seats">{{ flight.Economy_class_seats_remaining }}</td>
<td id="first-class-price">{{ flight.First_class_price }}</td>
<td id="business-class-price">{{ flight.Business_class_price }}</td>
<td id="economy-class-price">{{ flight.Economy_class_price }}</td>
<td>{{ flight.Status }}</td>
</tr>
</tbody>
</table>
</div> </div>
<div class="form-row">
<label for="departure-date">出发日期:</label> <div class="modify-seats">
<input type="date" id="departure-date" name="departure-date" required> <h3>修改座位数、价格及状态</h3>
<div id="date-warning" class="error-message"></div> <form id="modify-form" method="post" action="{{ url_for('modify') }}" onsubmit="return validateSeatChange()">
</div> <input type="hidden" name="flightID" value="{{ flight.ID }}">
<div class="form-row"> <div class="form-group">
<label for="passengers">乘客人数:</label> <label for="first_class_change">头等舱座位变化:</label>
<div class="passenger-input"> <div class="passenger-input">
<button type="button" onclick="decrement()">-</button> <button type="button" onclick="decrement('first_class_change', {{ flight.First_class_seats_remaining }})">-</button>
<input type="number" id="passengers" name="passengers" value="1" min="1" max="50"> <input type="number" id="first_class_change" name="first_class_change" placeholder="输入变化数量" value="0">
<button type="button" onclick="increment()">+</button> <button type="button" onclick="increment('first_class_change')">+</button>
</div> </div>
</div> </div>
<div class="form-row form-row-center"> <div class="form-group">
<button type="submit" class="btn">立即查询</button> <label for="business_class_change">商务舱座位变化:</label>
<div class="passenger-input">
<button type="button" onclick="decrement('business_class_change', {{ flight.Business_class_seats_remaining }})">-</button>
<input type="number" id="business_class_change" name="business_class_change" placeholder="输入变化数量" value="0">
<button type="button" onclick="increment('business_class_change')">+</button>
</div> </div>
</div>
<div class="form-group">
<label for="economy_class_change">经济舱座位变化:</label>
<div class="passenger-input">
<button type="button" onclick="decrement('economy_class_change', {{ flight.Economy_class_seats_remaining }})">-</button>
<input type="number" id="economy_class_change" name="economy_class_change" placeholder="输入变化数量" value="0">
<button type="button" onclick="increment('economy_class_change')">+</button>
</div>
</div>
<div class="form-group">
<label for="first_class_price">头等舱价格:</label>
<input type="number" step="0.01" id="first_class_price" name="first_class_price" placeholder="输入价格" value="{{ flight.First_class_price }}" oninput="validatePrice(this)">
</div>
<div class="form-group">
<label for="business_class_price">商务舱价格:</label>
<input type="number" step="0.01" id="business_class_price" name="business_class_price" placeholder="输入价格" value="{{ flight.Business_class_price }}" oninput="validatePrice(this)">
</div>
<div class="form-group">
<label for="economy_class_price">经济舱价格:</label>
<input type="number" step="0.01" id="economy_class_price" name="economy_class_price" placeholder="输入价格" value="{{ flight.Economy_class_price }}" oninput="validatePrice(this)">
</div>
<div class="form-group">
<label for="status">航班状态:</label>
<select id="status" name="status">
<option value="候机中" {% if flight.Status == '候机中' %}selected{% endif %}>候机中</option>
<option value="延误" {% if flight.Status == '延误' %}selected{% endif %}>延误</option>
<option value="已起飞" {% if flight.Status == '已起飞' %}selected{% endif %}>已起飞</option>
<option value="已降落" {% if flight.Status == '已降落' %}selected{% endif %}>已降落</option>
<option value="开始检票" {% if flight.Status == '开始检票' %}selected{% endif %}>开始检票</option>
<option value="未知" {% if flight.Status == '未知' %}selected{% endif %}>未知</option>
</select>
</div>
<button type="submit" class="btn">提交</button>
</form> </form>
</div> </div>
<div class="flight-actions">
<h3>其他操作</h3>
<button class="btn" onclick="deleteFlight('{{ flight.ID }}')">删除航班</button>
<form id="upload-csv-form" method="post" action="{{ url_for('upload_csv') }}" enctype="multipart/form-data">
<input type="file" name="file" accept=".csv" required>
<button type="submit" class="btn">批量添加航班</button>
</form>
</div>
{% endif %}
</div> </div>
</main> </main>
<footer> <footer>
<p>&copy; 2024 KJF航班订票. 保留所有权利。</p> <p>&copy; 2024 KJF航班订票. 保留所有权利。</p>
</footer> </footer>
<script src="{{ url_for('static', filename='js/slideshow.js') }}"></script>
<script> <script>
function validateForm() { function validateSeatChange() {
var departure = document.getElementById('departure').value; const firstClassSeats = parseInt(document.getElementById('first-class-seats').textContent);
var destination = document.getElementById('destination').value; const businessClassSeats = parseInt(document.getElementById('business-class-seats').textContent);
var warning = document.getElementById('destination-warning'); const economyClassSeats = parseInt(document.getElementById('economy-class-seats').textContent);
var dateWarning = document.getElementById('date-warning');
var departureDate = document.getElementById('departure-date').value;
var today = new Date(); const firstClassChange = parseInt(document.getElementById('first_class_change').value || 0);
var selectedDate = new Date(departureDate); const businessClassChange = parseInt(document.getElementById('business_class_change').value || 0);
today.setHours(0, 0, 0, 0); // Ensure time comparison is not affected const economyClassChange = parseInt(document.getElementById('economy_class_change').value || 0);
if (departure === destination) { const newFirstClassSeats = firstClassSeats + firstClassChange;
warning.textContent = '出发地和目的地不能相同'; const newBusinessClassSeats = businessClassSeats + businessClassChange;
const newEconomyClassSeats = economyClassSeats + economyClassChange;
if (newFirstClassSeats < 0 || newBusinessClassSeats < 0 || newEconomyClassSeats < 0) {
alert('座位变化后余座数不能为负值');
return false; return false;
} else {
warning.textContent = '';
} }
if (selectedDate < today) {
dateWarning.textContent = '出发日期不能早于今天';
return false;
} else {
dateWarning.textContent = '';
}
return true; return true;
} }
function increment() { function validatePrice(input) {
var passengers = document.getElementById("passengers"); const value = parseFloat(input.value);
var value = parseInt(passengers.value, 10); if (isNaN(value) || value < 0) {
if (value < 50) { input.setCustomValidity('价格不能为负值且仅支持小数点后2位');
passengers.value = value + 1; } else {
input.setCustomValidity('');
} }
} }
function decrement() { function increment(inputId) {
var passengers = document.getElementById("passengers"); var inputElement = document.getElementById(inputId);
var value = parseInt(passengers.value, 10); var value = parseInt(inputElement.value, 10);
if (value > 1) { inputElement.value = value + 1;
passengers.value = value - 1; }
function decrement(inputId, remainingSeats) {
var inputElement = document.getElementById(inputId);
var value = parseInt(inputElement.value, 10);
if (remainingSeats + value > 0) {
inputElement.value = value - 1;
} }
} }
document.addEventListener('DOMContentLoaded', function() { function deleteFlight(flightID) {
// Set default date to tomorrow if (confirm('确定要删除此航班吗?')) {
var departureDate = document.getElementById('departure-date'); fetch(`{{ url_for('delete_flight') }}?flightID=${flightID}`, {
var today = new Date(); method: 'DELETE'
var tomorrow = new Date(today); })
tomorrow.setDate(tomorrow.getDate() + 1); .then(response => response.json())
var month = ('0' + (tomorrow.getMonth() + 1)).slice(-2); .then(data => {
var day = ('0' + tomorrow.getDate()).slice(-2); alert(data.message);
var year = tomorrow.getFullYear(); if (data.success) {
departureDate.value = `${year}-${month}-${day}`; window.location.href = "{{ url_for('index') }}";
departureDate.setAttribute('min', `${year}-${month}-${day}`); }
}); })
.catch(error => console.error('Error:', error));
}
}
</script> </script>
</body> </body>
</html> </html>

View File

@ -1,8 +1,8 @@
db = { db = {
'host':'localhost', 'host':'localhost',
'user':'kejingfan', 'user':'serviceAgent',
'password':'KJF2811879', 'password':'password123',
'database':'TESTDB' 'database':'ServiceDatabase'
} }
SECRET_KEY = 'ILOVEDATABASETECH' SECRET_KEY = 'ILOVEDATABASETECH'