完成管理端代码
This commit is contained in:
parent
cfbaf585a7
commit
963a80a670
@ -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', '天津滨海国际机场', '天津'),
|
||||||
|
@ -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;
|
3
Project/Manager/data_source/flights/add.csv
Normal file
3
Project/Manager/data_source/flights/add.csv
Normal 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,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, '已降落'),
|
||||||
|
@ -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');
|
@ -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,
|
||||||
|
BIN
Project/Manager/func/__pycache__/config.cpython-311.pyc
Normal file
BIN
Project/Manager/func/__pycache__/config.cpython-311.pyc
Normal file
Binary file not shown.
BIN
Project/Manager/func/__pycache__/index.cpython-311.pyc
Normal file
BIN
Project/Manager/func/__pycache__/index.cpython-311.pyc
Normal file
Binary file not shown.
BIN
Project/Manager/func/__pycache__/login.cpython-311.pyc
Normal file
BIN
Project/Manager/func/__pycache__/login.cpython-311.pyc
Normal file
Binary file not shown.
@ -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'
|
||||||
|
@ -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'))
|
||||||
|
|
||||||
|
@ -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'))
|
@ -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(
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
@ -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}`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
@ -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>© 2024 KJF航班订票. 保留所有权利。</p>
|
<p>© 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>
|
||||||
|
Binary file not shown.
@ -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'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user