完成大作业服务端代码,新建管理端,管理端不可用

This commit is contained in:
2024-06-14 14:53:50 +08:00
parent ad767a806b
commit cfbaf585a7
80 changed files with 3563 additions and 128 deletions

Binary file not shown.

Binary file not shown.

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,92 @@
from flask import render_template, request, redirect, url_for, g, flash, session
from .config import db
import pymysql
from datetime import datetime
def book():
flight_id = request.args.get('flight_id')
if request.method == 'GET':
if not g.user:
return redirect(url_for("login"))
conn = pymysql.connect(**db)
cursor = conn.cursor(pymysql.cursors.DictCursor)
flight_sql = """
SELECT f.*, d.Name as Departure_airport_name, a.Name as Arrival_airport_name
FROM Flights f
JOIN Airports d ON f.Departure_airport = d.ID
JOIN Airports a ON f.Arrival_airport = a.ID
WHERE f.ID = %s;
"""
cursor.execute(flight_sql, (flight_id,))
flight = cursor.fetchone()
cursor.close()
conn.close()
return render_template('book.html', flight=flight, username=g.name)
if request.method == 'POST':
passengers = []
passenger_keys = ['card_code', 'name', 'phone_number', 'seat_class']
for i in range(len(request.form) // len(passenger_keys)):
passenger = {}
for key in passenger_keys:
passenger[key] = request.form.get(f'passengers[{i}][{key}]')
passengers.append(passenger)
conn = pymysql.connect(**db)
cursor = conn.cursor()
try:
# 插入订单记录
insert_order_sql = """
INSERT INTO Orders (Order_time, Paid, User_phone_number)
VALUES (%s, 0, %s)
"""
cursor.execute(insert_order_sql, (datetime.now(), g.user))
order_id = cursor.lastrowid
for passenger in passengers:
passenger_id = passenger['card_code']
name = passenger['name']
phone_number = passenger['phone_number']
seat_class = passenger['seat_class']
price_field = f"{seat_class.replace(' ', '_').lower()}_price"
price_sql = f"SELECT {price_field} FROM Flights WHERE ID = %s"
cursor.execute(price_sql, (flight_id,))
price = cursor.fetchone()[0]
insert_passenger_sql = """
INSERT INTO Passengers (ID, Name, Phone_number)
VALUES (%s, %s, %s)
ON DUPLICATE KEY UPDATE Name=VALUES(Name), Phone_number=VALUES(Phone_number);
"""
cursor.execute(insert_passenger_sql, (passenger_id, name, phone_number))
update_seat_sql = f"""
UPDATE Flights
SET {seat_class.replace(' ', '_').lower()}_seats_remaining = {seat_class.replace(' ', '_').lower()}_seats_remaining - 1
WHERE ID = %s
"""
cursor.execute(update_seat_sql, (flight_id,))
insert_ticket_sql = """
INSERT INTO Tickets (Price, FlightID, Seat_class, PassengerID, OrderID)
VALUES (%s, %s, %s, %s, %s)
"""
cursor.execute(insert_ticket_sql, (price, flight_id, seat_class, passenger_id, order_id))
conn.commit()
return redirect(url_for('order', order_id=order_id))
except Exception as e:
conn.rollback()
print(e)
flash("订票失败", "error")
return redirect(url_for('search'))
finally:
cursor.close()
conn.close()

View File

@@ -0,0 +1,51 @@
from flask import request, redirect, url_for, g
from .config import db
import pymysql
def cancel_order():
order_id = request.args.get('order_id')
if not g.user:
return redirect(url_for("login"))
conn = pymysql.connect(**db)
cursor = conn.cursor(pymysql.cursors.DictCursor)
# 检查订单是否存在
check_order_sql = "SELECT ID FROM Orders WHERE ID = %s"
cursor.execute(check_order_sql, (order_id,))
order_exists = cursor.fetchone()
if not order_exists:
cursor.close()
conn.close()
return redirect(url_for("order_list"))
# 查询订单中所有机票的航班ID和座位级别
tickets_sql = "SELECT FlightID, Seat_class FROM Tickets WHERE OrderID = %s"
cursor.execute(tickets_sql, (order_id,))
tickets = cursor.fetchall()
# 恢复航班的对应余座数
for ticket in tickets:
flight_id = ticket['FlightID']
seat_class = ticket['Seat_class']
seat_column = seat_class.replace(' ', '_').lower() + "_seats_remaining"
update_seat_sql = f"""
UPDATE Flights
SET {seat_column} = {seat_column} + 1
WHERE ID = %s
"""
cursor.execute(update_seat_sql, (flight_id,))
# 删除对应的机票
delete_tickets_sql = "DELETE FROM Tickets WHERE OrderID = %s"
cursor.execute(delete_tickets_sql, (order_id,))
# 删除订单
delete_order_sql = "DELETE FROM Orders WHERE ID = %s"
cursor.execute(delete_order_sql, (order_id,))
conn.commit()
cursor.close()
conn.close()
return redirect(url_for('order_list'))

View File

@@ -0,0 +1,18 @@
db = {
'host':'localhost',
'user':'kejingfan',
'password':'KJF2811879',
'database':'TESTDB'
}
SECRET_KEY = 'ILOVEDATABASETECH'
slideshow_images = [
{"link": "https://www.csair.com/mcms/mcms/SG/zh/2024/20240208_6/index-zh.html?lang=zh&country=sg&utm_campaign=2402gwstu&utm_source=gw&utm_channel=sg-lb", "src": "https://www.csair.com/mcms/20240321/3ee85acd463f481bb33f0d535a5814c6.jpg"},
{"link": "https://www.csair.com/mcms/mcms/SG/zh/2024/20240605_10/index_sg_cn.html?lang=zh&country=sg&utm_source=sg&utm_campaign=ZB001lydc&utm_channel=gw", "src": "https://www.csair.com/mcms/20240605/a553252769834188b0c76a9698292f27.jpg"},
{"link": "https://www.csair.com/mcms/mcms/SG/zh/2024/20240426_17/index_cn.html?lang=zh&country=sg&utm_source=sg&utm_campaign=ZB001znjp&utm_channel=gw", "src": "https://www.csair.com/mcms/20240321/820cd99c111849408b84c2b579086ef6.jpg"},
{"link": "https://www.csair.com/mcms/mcms/SG/zh/2024/20240524_2/index_cn.html?lang=zh&country=sg&country=my&utm_source=us&utm_campaign=ZB001nhzgx&utm_channel=gw", "src": "https://www.csair.com/mcms/20240321/61889331ca174670babd144bb064d398.jpg"},
{"link": "https://www.csair.com/mcms/mcms/SG/zh/2024/20240514_7/coupon.html?lang=zh&country=sg", "src": "https://www.csair.com/mcms/20240321/97e67c05291b4c64a8905e8a0c915d89.jpg"},
{"link": "#", "src": "https://www.csair.com/mcms/1026/43124f5d5124487f8d6678745ae42f57.jpg"},
{"link": "https://www.csair.com/mcms/mcms/SG/zh/2023/20231120_2/index_sg_cn.html?lang=zh&country=sg&utm_source=sg&utm_campaign=ZB001mq&utm_channel=gw", "src": "https://www.csair.com/mcms/1026/ea00e9cb9d9b43bea6497a6895c6d9e1.jpg"}
]

View File

@@ -0,0 +1,18 @@
from flask import render_template, request, g, redirect, url_for, session
from .config import slideshow_images
from .utils import get_cities
def index():
if request.method == 'GET':
if not g.user:
return redirect(url_for("login"))
images = slideshow_images
return render_template(
'index.html', cities=get_cities(),
images=images, username=g.name
)
def logout():
session.clear()
session.pop('user_id', None)
return redirect(url_for('login'))

View File

@@ -0,0 +1,35 @@
from flask import request, jsonify, session, url_for, render_template
from .config import db, slideshow_images
import pymysql
def connect(mobileNo, encrypted_password):
conn = pymysql.connect(**db)
cursor = conn.cursor(pymysql.cursors.DictCursor)
args = (mobileNo, encrypted_password)
verify_sql = "SELECT Phone_number FROM Users WHERE Phone_number = %s AND `Password` = %s;"
cursor.execute(verify_sql, args)
user = cursor.fetchone()
cursor.close()
conn.close()
return user
def login():
if request.method == 'GET':
images = slideshow_images
return render_template('login.html', images=images)
if request.method == 'POST':
session.pop('user_id', None)
mobileNo = request.json.get('username')
encrypted_password = request.json.get('password')
try:
user = connect(mobileNo, encrypted_password)
if not user:
return jsonify({'message': '用户不存在,请点击注册按钮注册'}), 401
session['user_id'] = mobileNo
session.modified = True
return jsonify({'redirect': url_for('index')})
except Exception as e:
print(e)
return jsonify({'message': '数据库错误,请稍后再试'}), 500

View File

@@ -0,0 +1,119 @@
from flask import render_template, request, flash, redirect, url_for, session, g
from typing import Dict
from pymysql.cursors import Cursor
import pymysql
from .config import db
def verify_user(cursor: Cursor, phone_number: str, password: str) -> str:
sql = """
SELECT Password FROM Users WHERE Phone_number = %s;
"""
cursor.execute(sql, (phone_number,))
record = cursor.fetchone()
if not record:
return "NO_USER"
if record[0] != password:
return "WRONG_PASSWORD"
return "USER_VERIFIED"
class ModifyInfo:
def __init__(self, form: Dict[str, str], user_phone: str):
self.phone_number = user_phone
modifyType = form['modifyType']
self.new_password = form.get('encryptedNewPassword', None)
self.new_phone_number = form.get('mobileNo', None)
self.new_username = form.get('username', None)
modifyType2command = {
'删除账户': 'delete account',
'修改密码': 'modify Password',
'修改手机号': 'modify Phone_Number',
'修改用户名': 'modify Username'
}
self.sql_dict = {
'delete account': 'DELETE FROM Users WHERE Phone_number = %s;',
'modify Password': 'UPDATE Users SET Password = %s WHERE Phone_number = %s;',
'modify Phone_Number': 'UPDATE Users SET Phone_number = %s WHERE Phone_number = %s;',
'modify Username': 'UPDATE Users SET Username = %s WHERE Phone_number = %s;'
}
self.sql_args_dict = {
'delete account': (self.phone_number,),
'modify Password': (self.new_password, self.phone_number),
'modify Phone_Number': (self.new_phone_number, self.phone_number),
'modify Username': (self.new_username, self.phone_number)
}
self.ok_message_dict = {
'delete account': "删除账户成功",
'modify Password': "修改密码成功",
'modify Phone_Number': "修改手机号成功",
'modify Username': "修改用户名成功"
}
self.fail_message_dict = {
'delete account': "数据库异常,删除账户失败",
'modify Password': "数据库异常,修改密码失败",
'modify Phone_Number': "数据库异常,修改手机号失败",
'modify Username': "数据库异常,修改用户名失败"
}
self.command = modifyType2command[modifyType]
def get_sql(self):
return self.sql_dict[self.command]
def get_args(self):
return self.sql_args_dict[self.command]
def get_ok_message(self):
return self.ok_message_dict[self.command]
def get_fail_message(self):
return self.fail_message_dict[self.command]
def modify():
if request.method == 'GET':
if not g.user:
return redirect(url_for("login"))
user_phone = session.get('user_id')
return render_template('modify.html', current_user_phone=user_phone, current_username=g.name)
if request.method == 'POST':
user_phone = session.get('user_id')
password = request.form['encryptedPassword']
conn = pymysql.connect(**db)
cursor = conn.cursor(pymysql.cursors.DictCursor)
verify_info = verify_user(cursor, user_phone, password)
if verify_info == "NO_USER":
session.clear()
return redirect(url_for('login'))
elif verify_info == "WRONG_PASSWORD":
flash("密码错误")
conn.close()
return redirect(url_for('modify'))
modifyInfo = ModifyInfo(request.form, user_phone)
if modifyInfo.command == 'modify Phone_Number':
check_sql = "SELECT COUNT(*) FROM Users WHERE Phone_number = %s;"
cursor.execute(check_sql, (modifyInfo.new_phone_number,))
if cursor.fetchone()[0] > 0:
flash("手机号已存在,请使用其他手机号")
conn.close()
return redirect(url_for('modify'))
try:
cursor.execute(modifyInfo.get_sql(), modifyInfo.get_args())
conn.commit()
flash(modifyInfo.get_ok_message())
conn.close()
if modifyInfo.command in ['modify Phone_Number', 'modify Password', 'delete account']:
session.clear()
session.pop("user_id", None)
return redirect(url_for('login'))
elif modifyInfo.command == 'modify Username':
return redirect(url_for('modify'))
except Exception as e:
conn.rollback()
print(e)
flash(modifyInfo.get_fail_message())
conn.close()
return redirect(url_for('modify'))

View File

@@ -0,0 +1,47 @@
from flask import render_template, request, redirect, url_for, g
from .config import db
import pymysql
def order():
order_id = request.args.get('order_id')
if not g.user:
return redirect(url_for("login"))
conn = pymysql.connect(**db)
cursor = conn.cursor(pymysql.cursors.DictCursor)
# 查询订单信息和航班信息
order_sql = """
SELECT o.*, f.*, d.Name as Departure_airport_name, a.Name as Arrival_airport_name
FROM Orders o
JOIN Tickets t ON o.ID = t.OrderID
JOIN Flights f ON t.FlightID = f.ID
JOIN Airports d ON f.Departure_airport = d.ID
JOIN Airports a ON f.Arrival_airport = a.ID
WHERE o.ID = %s
"""
cursor.execute(order_sql, (order_id,))
order_info = cursor.fetchone()
# 如果订单信息不存在,返回订单列表页面
if not order_info:
cursor.close()
conn.close()
return redirect(url_for("order_list"))
# 查询乘客信息和票据信息
tickets_sql = """
SELECT t.*, p.Name, p.Phone_number
FROM Tickets t
JOIN Passengers p ON t.PassengerID = p.ID
WHERE t.OrderID = %s
"""
cursor.execute(tickets_sql, (order_id,))
tickets = cursor.fetchall()
cursor.close()
conn.close()
total_price = sum(ticket['Price'] for ticket in tickets)
return render_template('order.html', order=order_info, tickets=tickets, total_price=total_price, username=g.name)

View File

@@ -0,0 +1,54 @@
from flask import render_template, request, redirect, url_for, g
from .config import db
import pymysql
def order_list():
if not g.user:
return redirect(url_for("login"))
conn = pymysql.connect(**db)
cursor = conn.cursor(pymysql.cursors.DictCursor)
# 查询用户关联的所有订单信息
orders_sql = """
SELECT o.ID as OrderID, o.Order_time, o.Paid, f.ID as FlightID, f.Airline,
d.Name as Departure_airport_name, a.Name as Arrival_airport_name,
f.Departure_time, f.Status, p.Name as PassengerName, t.Price
FROM Orders o
JOIN Tickets t ON o.ID = t.OrderID
JOIN Flights f ON t.FlightID = f.ID
JOIN Airports d ON f.Departure_airport = d.ID
JOIN Airports a ON f.Arrival_airport = a.ID
JOIN Passengers p ON t.PassengerID = p.ID
WHERE o.User_phone_number = %s
"""
cursor.execute(orders_sql, (g.user,))
orders = cursor.fetchall()
cursor.close()
conn.close()
# 整理订单信息
order_dict = {}
for order in orders:
order_id = order['OrderID']
if order_id not in order_dict:
order_dict[order_id] = {
'OrderID': order_id,
'Order_time': order['Order_time'],
'Paid': order['Paid'],
'FlightID': order['FlightID'],
'Airline': order['Airline'],
'Departure_airport_name': order['Departure_airport_name'],
'Arrival_airport_name': order['Arrival_airport_name'],
'Departure_time': order['Departure_time'],
'Status': order['Status'],
'Passengers': [],
'TotalPrice': 0
}
order_dict[order_id]['Passengers'].append(order['PassengerName'])
order_dict[order_id]['TotalPrice'] += order['Price']
order_list = list(order_dict.values())
return render_template('order_list.html', orders=order_list, username=g.name)

View File

@@ -0,0 +1,30 @@
from flask import redirect, url_for, g, request
from .config import db
import pymysql
def pay_confirm():
order_id = request.args.get('order_id')
if not g.user:
return redirect(url_for("login"))
conn = pymysql.connect(**db)
cursor = conn.cursor()
# 检查订单是否存在
check_order_sql = "SELECT ID FROM Orders WHERE ID = %s"
cursor.execute(check_order_sql, (order_id,))
order_exists = cursor.fetchone()
if not order_exists:
cursor.close()
conn.close()
return redirect(url_for("order_list"))
update_order_sql = "UPDATE Orders SET Paid = 1 WHERE ID = %s"
cursor.execute(update_order_sql, (order_id,))
conn.commit()
cursor.close()
conn.close()
return redirect(url_for('order_list', order_id=order_id))

View File

@@ -0,0 +1,49 @@
from flask import render_template, request, g, abort, redirect, url_for
from .config import db
from .utils import get_cities
import pymysql
import datetime
def search():
if not g.user:
return redirect(url_for("login"))
departure_city = request.args.get('departure')
destination_city = request.args.get('destination')
departure_date = request.args.get('departure-date')
passengers = int(request.args.get('passengers', 1))
# Date validation
try:
departure_date_obj = datetime.datetime.strptime(departure_date, '%Y-%m-%d').date()
if departure_date_obj < datetime.date.today():
abort(400, description="Departure date cannot be in the past.")
except ValueError:
abort(400, description="Invalid date format.")
conn = pymysql.connect(**db)
cursor = conn.cursor(pymysql.cursors.DictCursor)
search_sql = """
SELECT f.*, d.Name as Departure_airport_name, a.Name as Arrival_airport_name
FROM Flights f
JOIN Airports d ON f.Departure_airport = d.ID
JOIN Airports a ON f.Arrival_airport = a.ID
WHERE d.City = %s AND a.City = %s
AND DATE(f.Departure_time) = %s
AND (f.First_class_seats_remaining + f.Business_class_seats_remaining + f.Economy_class_seats_remaining) >= %s
AND f.Status NOT IN ('已起飞', '已降落');
"""
cursor.execute(search_sql, (departure_city, destination_city, departure_date, passengers))
flights = cursor.fetchall()
cursor.close()
conn.close()
return render_template(
'search.html',
cities=get_cities(),
flights=flights,
username=g.name
)

View File

@@ -0,0 +1,74 @@
from flask import render_template, request, redirect, url_for
from .config import db
import re
import pymysql
def signup():
error_messages = {
'username': '',
'mobileNo': '',
'password': '',
'confirmPassword': ''
}
if request.method == 'GET':
return render_template('signup.html', errors=error_messages)
if request.method == 'POST':
username = request.form['username']
phone_number = request.form['mobileNo']
password = request.form['encryptedPassword']
confirm_password = request.form['encryptedConfirmPassword']
# Basic validation for phone number
if not re.match(r'^\d{11}$', phone_number):
error_messages['mobileNo'] = '手机号格式有误'
# Check password length after MD5 hash
if len(password) != 32: # MD5 hash length is 32 characters
error_messages['password'] = '密码格式有误'
# Confirm password validation
if password != confirm_password:
error_messages['confirmPassword'] = '两次输入的密码不一致'
if any(error_messages.values()):
return render_template('signup.html', errors=error_messages)
conn = pymysql.connect(**db)
cursor = conn.cursor(pymysql.cursors.DictCursor)
# 检查已有用户
sql = """
SELECT COUNT(*) FROM Users \
WHERE Phone_number = %s;
"""
try:
cursor.execute(sql, (phone_number,))
phone_exist = cursor.fetchall()[0]['COUNT(*)']
except Exception as e:
error_messages['mobileNo'] = "数据库异常,查询失败"
print(e)
return render_template('signup.html', errors=error_messages)
if phone_exist != 0:
error_messages['mobileNo'] = "该手机号已注册,请勿重复注册"
conn.close()
return render_template('signup.html', errors=error_messages)
# 插入
sql = '''
INSERT INTO Users (Phone_number, Username, `Password`) \
VALUES (%s, %s, %s); \
'''
try:
cursor.execute(sql, (phone_number, username, password))
conn.commit()
return redirect(url_for('index'))
except Exception as e:
conn.rollback()
print(e)
error_messages['mobileNo'] = "数据库异常,注册失败"
return render_template('signup.html', errors=error_messages)
finally:
conn.close()

View File

@@ -0,0 +1,20 @@
from .config import db
import pymysql
from xpinyin import Pinyin
from pymysql.cursors import Cursor
def get_cities():
conn = pymysql.connect(**db)
cursor = conn.cursor(pymysql.cursors.DictCursor)
cursor.execute("SELECT DISTINCT City FROM Airports")
cities = [row['City'] for row in cursor.fetchall()]
cursor.close()
conn.close()
p = Pinyin()
cities = [
(row, p.get_pinyin(row).replace("-", ""))
for row in cities
]
cities = sorted(cities, key=lambda x: x[1])
cities = [row[0] for row in cities]
return cities

98
Project/Service/main.py Normal file
View File

@@ -0,0 +1,98 @@
from flask import Flask, redirect, url_for, session, g
from flask_httpauth import HTTPTokenAuth
from flask_cors import CORS
from func.config import db, SECRET_KEY
import pymysql
import func.login
import func.signup
import func.modify
import func.index
import func.search
import func.book
import func.pay_confirm
import func.cancel_order
import func.order
import func.order_list
app = Flask(__name__)
app.config["SECRET_KEY"] = SECRET_KEY
app.config["JSON_AS_ASCII"] = False
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SECURE'] = False
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
CORS(app, supports_credentials=True)
auth = HTTPTokenAuth(scheme='Bearer')
@app.before_request
def before_request():
g.user = None
g.name = None
if 'user_id' in session:
conn = pymysql.connect(**db)
cursor = conn.cursor(pymysql.cursors.DictCursor)
args = (session.get('user_id'),)
verify_sql = "SELECT Username FROM Users WHERE Phone_number = %s;"
cursor.execute(verify_sql, args)
res = cursor.fetchone()
if not res:
g.user = None
g.name = None
else:
g.user = session.get('user_id')
g.name = res['Username']
@app.route("/")
def home():
return redirect(url_for('index'))
@app.route("/index", methods=['GET', 'POST'])
def index():
return func.index.index()
@app.route("/login", methods=['GET', 'POST'])
def login():
return func.login.login()
@app.route('/logout')
def logout():
return func.index.logout()
@app.route("/signup", methods=['GET', 'POST'])
def signup():
return func.signup.signup()
@app.route("/modify", methods=['GET', 'POST'])
def modify():
return func.modify.modify()
@app.route("/search", methods=['GET'])
def search():
return func.search.search()
@app.route("/book", methods=['GET', 'POST'])
def book():
return func.book.book()
@app.route("/order", methods=['GET'])
def order():
return func.order.order()
@app.route('/cancel_order', methods=['POST'])
def cancel_order():
return func.cancel_order.cancel_order()
@app.route('/pay_confirm', methods=['POST'])
def pay_confirm():
return func.pay_confirm.pay_confirm()
@app.route("/order_list", methods=['GET', 'POST'])
def order_list():
return func.order_list.order_list()
if __name__ == "__main__":
app.run(
host="0.0.0.0",
port=8888,
debug=True
)

View File

@@ -0,0 +1,12 @@
blinker==1.8.1
cffi==1.16.0
click==8.1.7
configparser==7.0.0
cryptography==42.0.6
Flask==3.0.3
itsdangerous==2.2.0
Jinja2==3.1.3
MarkupSafe==2.1.5
pycparser==2.22
PyMySQL==1.1.0
Werkzeug==3.0.2

3
Project/Service/run.sh Normal file
View File

@@ -0,0 +1,3 @@
SECRET_KEY='ILOVEDATABASETECH' FLASK_APP=main FLASK_ENV=development flask run \
--host 0.0.0.0 \
--port 5000 # default 5000

View File

@@ -0,0 +1,238 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: rgba(28, 108, 178, 0.9);
color: white;
padding: 20px;
width: 100%;
position: fixed;
top: 0;
z-index: 1000;
}
.header-content {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
}
.logo {
font-size: 24px;
font-weight: bold;
margin-left: 20px;
}
.nav-buttons {
display: flex;
gap: 20px;
}
.nav-buttons a {
color: white;
text-decoration: none;
font-size: 16px;
padding: 10px;
border-radius: 5px;
}
.nav-buttons a:hover {
background-color: #155a8c;
}
.user-menu {
position: relative;
display: flex;
align-items: center;
margin-right: 20px;
}
.user-menu span {
margin-right: 10px;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropbtn {
background-color: rgba(28, 108, 178, 0.9);
color: white;
border: none;
cursor: pointer;
font-size: 16px;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
right: 0; /* 确保下拉菜单靠右对齐 */
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown-content a:hover {
background-color: #f1f1f1;
}
.dropdown:hover .dropdown-content {
display: block;
}
main {
flex: 1;
width: 80%;
margin: 20px auto;
padding-top: 80px; /* 给主内容增加顶部填充,以避免被固定导航栏遮挡 */
}
h2 {
text-align: center;
color: #1c6cb2;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
table, th, td {
border: 1px solid #ccc;
}
th, td {
padding: 10px;
text-align: center;
}
th {
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;
}
form {
background-color: rgba(255, 255, 255, 0.8);
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
}
.passenger {
margin-bottom: 20px;
transition: transform 0.3s ease, box-shadow 0.3s ease, border-radius 0.3s ease;
}
.passenger:hover {
transform: scale(1.02);
box-shadow: 0 0 10px rgba(28, 108, 178, 0.5);
border-radius: 10px;
}
.passenger h3 {
margin-top: 0;
}
.delete-btn {
background-color: #ff4d4d;
color: white;
border: none;
cursor: pointer;
padding: 5px 10px;
border-radius: 5px;
float: left;
}
.form-row {
display: flex;
justify-content: space-between;
margin-bottom: 15px;
}
label {
flex: 1;
margin-right: 10px;
text-align: right;
line-height: 32px;
}
input, select {
flex: 2;
padding: 8px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 5px;
}
.error-message {
color: red;
font-size: 12px;
margin-top: 5px;
}
.total-price {
font-size: 18px;
font-weight: bold;
margin-top: 20px;
margin-bottom: 20px;
}
.btn {
padding: 10px 20px;
background-color: #1c6cb2;
color: white;
border: none;
border-radius: 5px;
text-decoration: none;
display: inline-block;
cursor: pointer;
margin-top: 10px;
}
.btn:hover {
background-color: #155a8c;
}
footer {
background-color: rgba(28, 108, 178, 0.9);
color: white;
text-align: center;
padding: 10px 0;
width: 100%;
position: relative;
margin-top: auto; /* 将footer推到页面底部 */
}

View File

@@ -0,0 +1,237 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: rgba(28, 108, 178, 0.9);
color: white;
padding: 20px;
width: 100%;
position: fixed;
top: 0;
z-index: 1000;
}
.header-content {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
}
.logo {
font-size: 24px;
font-weight: bold;
margin-left: 20px;
}
.nav-buttons {
display: flex;
gap: 20px;
}
.nav-buttons a {
color: white;
text-decoration: none;
font-size: 16px;
padding: 10px;
border-radius: 5px;
}
.nav-buttons a:hover {
background-color: #155a8c;
}
.user-menu {
position: relative;
display: flex;
align-items: center;
margin-right: 20px;
}
.user-menu span {
margin-right: 10px;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropbtn {
background-color: rgba(28, 108, 178, 0.9);
color: white;
border: none;
cursor: pointer;
font-size: 16px;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
right: 0; /* 确保下拉菜单靠右对齐 */
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown-content a:hover {
background-color: #f1f1f1;
}
.dropdown:hover .dropdown-content {
display: block;
}
main {
flex: 1;
width: 100%;
margin: 20px auto;
padding-top: 80px; /* 给主内容增加顶部填充,以避免被固定导航栏遮挡 */
}
.slides {
position: relative;
width: 100%;
height: 620px;
overflow: hidden;
}
.slides ul {
display: flex;
padding: 0;
width: 100%;
height: 100%;
list-style: none;
margin: 0;
transition: transform 0.6s ease-in-out;
}
.slides li {
min-width: 100%;
box-sizing: border-box;
}
.slides img {
width: 100%;
height: 100%;
object-fit: cover;
}
.content {
background-color: rgba(255, 255, 255, 0.8);
padding: 20px;
border-radius: 10px;
text-align: center;
width: 80%;
max-width: 800px;
margin: -170px auto 20px;
position: relative;
}
.tabcontent {
display: block;
padding: 20px;
}
.form-row {
display: flex;
justify-content: flex-start; /* Left-align the form items */
margin-bottom: 15px;
}
.form-row label {
flex: 0 0 120px; /* Fixed width for labels */
margin-right: 10px;
text-align: right;
line-height: 32px;
}
.form-row input,
.form-row select {
flex: 1;
padding: 8px;
font-size: 16px;
border: 1px solid #ccc;
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 {
padding: 10px 20px;
background-color: #1c6cb2;
color: white;
border: none;
border-radius: 5px;
text-decoration: none;
display: inline-block;
cursor: pointer;
}
.btn:hover {
background-color: #155a8c;
}
.error-message {
color: red;
font-size: 12px;
margin-top: 5px;
text-align: left;
}
footer {
background-color: rgba(28, 108, 178, 0.9);
color: white;
text-align: center;
padding: 10px 0;
width: 100%;
position: relative;
margin-top: auto; /* 将footer推到页面底部 */
}

View File

@@ -0,0 +1,121 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #ffffff;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
}
header {
background-color: rgba(28, 108, 178, 0.9);
color: white;
padding: 20px 0;
text-align: center;
width: 100%;
}
main {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
position: relative;
}
.slides {
position: relative;
width: 100%;
height: 600px;
overflow: hidden;
}
.slides ul {
display: flex;
padding: 0;
width: 100%;
height: 100%;
list-style: none;
margin: 0;
transition: transform 0.6s ease-in-out;
}
.slides li {
min-width: 100%;
box-sizing: border-box;
}
.slides img {
width: 100%;
height: 100%;
object-fit: cover;
}
.content {
background-color: rgba(255, 255, 255, 0.8);
padding: 20px;
border-radius: 10px;
text-align: center;
position: absolute;
}
h2 {
color: #1c6cb2;
}
.login-form {
margin-bottom: 20px;
}
.login-form input {
display: block;
margin: 10px auto;
padding: 10px;
font-size: 16px;
width: 80%;
max-width: 300px;
margin-bottom: 20px; /* 增加外边距 */
}
.buttons {
display: flex;
justify-content: center;
gap: 10px;
}
.login-form button,
.btn {
padding: 10px 20px;
background-color: #1c6cb2;
color: white;
border: none;
border-radius: 5px;
text-decoration: none;
display: inline-block;
line-height: 20px;
cursor: pointer;
}
.login-form button:hover,
.btn:hover {
background-color: #155a8c;
}
footer {
background-color: rgba(28, 108, 178, 0.9);
color: white;
text-align: center;
padding: 10px 0;
width: 100%;
}
.error-message {
color: red;
font-size: 12px;
text-align: left;
margin-top: -10px;
margin-bottom: 10px;
}

View File

@@ -0,0 +1,149 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: rgba(28, 108, 178, 0.9);
color: white;
padding: 20px;
width: 100%;
position: fixed;
top: 0;
z-index: 1000;
}
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
width: 90%;
margin: 0 auto;
position: relative;
}
.header-content .btn-back {
padding: 10px 20px;
background-color: #1c6cb2;
color: white;
border: none;
border-radius: 5px;
text-decoration: none;
}
.header-content .btn-back:hover {
background-color: #155a8c;
}
.header-content h1 {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
.header-content .header-spacer {
width: 100px; /* 占位符,确保标题居中 */
}
main {
flex: 1;
width: 80%;
margin: 20px auto;
padding-top: 80px; /* 给主内容增加顶部填充,以避免被固定导航栏遮挡 */
}
.content {
background-color: rgba(255, 255, 255, 0.9);
padding: 30px;
border-radius: 10px;
text-align: center;
width: 400px;
position: relative;
margin: auto;
}
h2 {
color: #1c6cb2;
}
form {
margin-top: 20px;
}
.tab {
display: flex;
justify-content: space-around;
margin-bottom: 20px;
}
.tab button {
background-color: #f1f1f1;
border: none;
outline: none;
cursor: pointer;
padding: 10px 20px;
transition: 0.3s;
font-size: 17px;
border-radius: 5px;
}
.tab button:hover {
background-color: #ddd;
}
.tab button.active {
background-color: #1c6cb2;
color: white;
}
.tabcontent {
display: none;
}
.form-group {
margin-bottom: 15px;
text-align: left;
}
.form-group div {
margin-bottom: 5px;
font-weight: bold;
}
.form-group input {
width: 100%;
padding: 10px;
box-sizing: border-box;
}
button[type="submit"] {
padding: 10px 20px;
background-color: #1c6cb2;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
width: 100%;
font-size: 16px;
}
button[type="submit"]:hover {
background-color: #155a8c;
}
footer {
background-color: rgba(28, 108, 178, 0.9);
color: white;
text-align: center;
padding: 10px 0;
width: 100%;
position: relative;
margin-top: auto; /* 将footer推到页面底部 */
}

View File

@@ -0,0 +1,191 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: rgba(28, 108, 178, 0.9);
color: white;
padding: 20px;
width: 100%;
position: fixed;
top: 0;
z-index: 1000;
}
.header-content {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
}
.logo {
font-size: 24px;
font-weight: bold;
margin-left: 20px;
}
.nav-buttons {
display: flex;
gap: 20px;
}
.nav-buttons a {
color: white;
text-decoration: none;
font-size: 16px;
padding: 10px;
border-radius: 5px;
}
.nav-buttons a:hover {
background-color: #155a8c;
}
.user-menu {
position: relative;
display: flex;
align-items: center;
margin-right: 20px;
}
.user-menu span {
margin-right: 10px;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropbtn {
background-color: rgba(28, 108, 178, 0.9);
color: white;
border: none;
cursor: pointer;
font-size: 16px;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
right: 0;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown-content a:hover {
background-color: #f1f1f1;
}
.dropdown:hover .dropdown-content {
display: block;
}
main {
flex: 1;
width: 80%;
margin: 20px auto;
padding-top: 80px; /* 给主内容增加顶部填充,以避免被固定导航栏遮挡 */
}
h2 {
text-align: center;
color: #1c6cb2;
}
.order-info, .passenger-info, .order-summary {
background-color: rgba(255, 255, 255, 0.8);
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
}
.order-info p, .order-summary p {
margin: 5px 0;
}
table {
width: 100%;
border-collapse: collapse;
}
table, th, td {
border: 1px solid #ccc;
}
th, td {
padding: 10px;
text-align: center;
}
th {
background-color: #f2f2f2;
}
.btn {
padding: 10px 20px;
background-color: #1c6cb2;
color: white;
border: none;
border-radius: 5px;
text-decoration: none;
display: inline-block;
cursor: pointer;
margin-top: 10px;
}
.btn:hover {
background-color: #155a8c;
}
.cancel-btn {
background-color: #ff4d4d;
}
.cancel-btn:hover {
background-color: #cc0000;
}
.back-btn {
background-color: #f2f2f2;
color: black;
border: none;
border-radius: 5px;
padding: 10px 20px;
cursor: pointer;
text-decoration: none;
display: inline-block;
margin: 10px 0;
}
.back-btn:hover {
background-color: #d9d9d9;
}
footer {
background-color: rgba(28, 108, 178, 0.9);
color: white;
text-align: center;
padding: 10px 0;
width: 100%;
position: relative;
margin-top: auto; /* 将footer推到页面底部 */
}

View File

@@ -0,0 +1,151 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: rgba(28, 108, 178, 0.9);
color: white;
padding: 20px;
width: 100%;
position: fixed;
top: 0;
z-index: 1000;
}
.header-content {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
}
.logo {
font-size: 24px;
font-weight: bold;
margin-left: 20px;
}
.nav-buttons {
display: flex;
gap: 20px;
}
.nav-buttons a {
color: white;
text-decoration: none;
font-size: 16px;
padding: 10px;
border-radius: 5px;
}
.nav-buttons a:hover {
background-color: #155a8c;
}
.user-menu {
position: relative;
display: flex;
align-items: center;
margin-right: 20px;
}
.user-menu span {
margin-right: 10px;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropbtn {
background-color: rgba(28, 108, 178, 0.9);
color: white;
border: none;
cursor: pointer;
font-size: 16px;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
right: 0;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown-content a:hover {
background-color: #f1f1f1;
}
.dropdown:hover .dropdown-content {
display: block;
}
main {
flex: 1;
width: 80%;
margin: 20px auto;
padding-top: 80px; /* 给主内容增加顶部填充,以避免被固定导航栏遮挡 */
}
h2 {
text-align: center;
color: #1c6cb2;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
table, th, td {
border: 1px solid #ccc;
}
th, td {
padding: 10px;
text-align: center;
}
th {
background-color: #f2f2f2;
}
footer {
background-color: rgba(28, 108, 178, 0.9);
color: white;
text-align: center;
padding: 10px 0;
width: 100%;
position: relative;
margin-top: auto; /* 将footer推到页面底部 */
}
.order-row {
transition: transform 0.3s ease, box-shadow 0.3s ease, border-radius 0.3s ease;
}
.order-row:hover {
transform: scale(1.02);
box-shadow: 0 0 10px rgba(28, 108, 178, 0.5);
border-radius: 10px;
}

View File

@@ -0,0 +1,251 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: rgba(28, 108, 178, 0.9);
color: white;
padding: 20px;
width: 100%;
position: fixed;
top: 0;
z-index: 1000;
}
.header-content {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
}
.logo {
font-size: 24px;
font-weight: bold;
margin-left: 20px;
}
.nav-buttons {
display: flex;
gap: 20px;
}
.nav-buttons a {
color: white;
text-decoration: none;
font-size: 16px;
padding: 10px;
border-radius: 5px;
}
.nav-buttons a:hover {
background-color: #155a8c;
}
.user-menu {
position: relative;
display: flex;
align-items: center;
margin-right: 20px;
}
.user-menu span {
margin-right: 10px;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropbtn {
background-color: rgba(28, 108, 178, 0.9);
color: white;
border: none;
cursor: pointer;
font-size: 16px;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
right: 0; /* 确保下拉菜单靠右对齐 */
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown-content a:hover {
background-color: #f1f1f1;
}
.dropdown:hover .dropdown-content {
display: block;
}
main {
flex: 1;
width: 80%;
margin: 20px auto;
padding-top: 80px; /* 给主内容增加顶部填充,以避免被固定导航栏遮挡 */
}
h2 {
text-align: center;
color: #1c6cb2;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
table, th, td {
border: 1px solid #ccc;
}
th, td {
padding: 10px;
text-align: center;
}
th {
background-color: #f2f2f2;
}
.no-results {
text-align: center;
color: red;
font-size: 18px;
margin-top: 20px;
}
footer {
background-color: rgba(28, 108, 178, 0.9);
color: white;
text-align: center;
padding: 10px 0;
width: 100%;
position: relative;
margin-top: auto; /* 将footer推到页面底部 */
}
.content {
background-color: rgba(255, 255, 255, 0.8);
padding: 20px;
border-radius: 10px;
text-align: center;
width: 80%;
max-width: 800px;
margin: 20px auto;
position: relative;
}
.tabcontent {
display: block;
padding: 20px;
}
.form-row {
display: flex;
justify-content: flex-start; /* Left-align the form items */
margin-bottom: 15px;
}
.form-row label {
flex: 0 0 120px; /* Fixed width for labels */
margin-right: 10px;
text-align: right;
line-height: 32px;
}
.form-row input,
.form-row select {
flex: 1;
padding: 8px;
font-size: 16px;
border: 1px solid #ccc;
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 {
padding: 10px 20px;
background-color: #1c6cb2;
color: white;
border: none;
border-radius: 5px;
text-decoration: none;
display: inline-block;
cursor: pointer;
}
.btn:hover {
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

@@ -0,0 +1,71 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #ffffff;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
}
form {
background-color: rgba(255, 255, 255, 0.9);
padding: 40px; /* 增加内边距 */
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 500px; /* 增大表单框的最大宽度 */
margin: auto;
margin-top: 100px;
}
ul {
list-style-type: none;
padding: 0;
margin: 0;
}
li {
margin-bottom: 20px;
}
div {
display: flex;
flex-direction: column;
}
div div {
flex: 1;
padding-top: 5px;
}
input[type="text"], input[type="password"] {
width: 100%;
padding: 10px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 5px;
}
button {
width: 100%;
padding: 10px;
background-color: #1c6cb2;
color: white;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
}
button:hover {
background-color: #155a8c;
}
.error-message {
color: red;
font-size: 12px;
height: 12px;
margin-top: 5px;
}

View File

@@ -0,0 +1,15 @@
var checkInfo = {};
checkInfo.checkMobileNo = function() {
let mobileNo = document.getElementById('mobileNo').value;
let regexMobileNo = /^1[3-9]\d{9}$/;
return regexMobileNo.test(mobileNo);
}
checkInfo.checkPassword = function() {
let password = document.getElementById('password').value;
let regexPassword = /^[A-Za-z0-9\W_]{6,20}$/;
let isValidPassword = regexPassword.test(password);
let confirmPassword = document.getElementById('confirmPassword').value;
return isValidPassword && (password === confirmPassword);
}

View File

@@ -0,0 +1,29 @@
function openTab(evt, tabName) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
document.getElementById(tabName).style.display = "block";
evt.currentTarget.className += " active";
}
function increment() {
var passengers = document.getElementById("passengers");
var value = parseInt(passengers.value, 10);
if (value < 50) {
passengers.value = value + 1;
}
}
function decrement() {
var passengers = document.getElementById("passengers");
var value = parseInt(passengers.value, 10);
if (value > 1) {
passengers.value = value - 1;
}
}

View File

@@ -0,0 +1,81 @@
window.onload = function() {
autoLogin();
};
var checkInfo = {};
checkInfo.checkMobileNo = function() {
let mobileNo = document.getElementById('mobileNo').value;
let regexMobileNo = /^1[3-9]\d{9}$/;
if (!regexMobileNo.test(mobileNo)) {
document.getElementById('mobileNoError').textContent = '手机号格式有误';
return false;
}
document.getElementById('mobileNoError').textContent = '';
return true;
}
checkInfo.checkPassword = function() {
let password = document.getElementById('password').value;
let regexPassword = /^[A-Za-z0-9\W_]{6,20}$/;
if (!regexPassword.test(password)) {
document.getElementById('loginError').textContent = "密码须为长度为6-20位字母、数字或符号";
return false;
}
document.getElementById('loginError').textContent = '';
return true;
}
function submitForm() {
if (checkInfo.checkMobileNo() && checkInfo.checkPassword()) {
document.getElementById('encryptedPassword').value = md5(
document.getElementById('password').value
);
login();
return true;
}
return false;
}
// 登录函数
async function login() {
const mobileNo = document.getElementById('mobileNo').value;
const encryptedPassword = document.getElementById('encryptedPassword').value;
try {
const response = await fetch('/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username: mobileNo, password: encryptedPassword }),
credentials: 'include' // 确保请求包含凭据cookies
});
const data = await response.json();
if (response.ok) {
alert('登录成功');
// 自动跳转到主页
window.location.href = data.redirect;
} else {
document.getElementById('loginError').textContent = data.message;
}
} catch (error) {
alert('数据库错误,请稍后再试');
}
}
// 自动登录函数
async function autoLogin() {
const token = localStorage.getItem('token');
if (token) {
const response = await fetch('http://localhost:5000/index', {
headers: {
'Authorization': 'Bearer ' + token
}
});
if (response.ok) {
document.getElementById('content').innerText = '已自动登录';
} else {
document.getElementById('content').innerText = '自动登录失败';
}
}
}

View File

@@ -0,0 +1,31 @@
var modify = {};
modify.showModifyPassword = function() {
let modifyType = document.querySelector('.tablinks.active').textContent;
let info = {
modifyPasswordLis: document.getElementsByClassName('modifyPassword'),
modifymobileNoLis: document.getElementsByClassName('modifymobileNo'),
modifyUsernameLis: document.getElementsByClassName('modifyUsername'),
}
for (let key in info) {
let elements = info[key];
for (let item of elements) {
item.style.display = 'none';
}
}
if (modifyType === "修改密码") {
for (let item of info.modifyPasswordLis) {
item.style.display = 'block';
}
} else if (modifyType === "修改手机号") {
for (let item of info.modifymobileNoLis) {
item.style.display = 'block';
}
} else if (modifyType === "修改用户名") {
for (let item of info.modifyUsernameLis) {
item.style.display = 'block';
}
}
}

View File

@@ -0,0 +1,42 @@
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() {
var passengers = document.getElementById("passengers");
var value = parseInt(passengers.value, 10);
if (value < 50) {
passengers.value = value + 1;
}
}
function decrement() {
var passengers = document.getElementById("passengers");
var value = parseInt(passengers.value, 10);
if (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

@@ -0,0 +1,32 @@
function submitForm() {
let isValid = true;
clearErrors();
if (!checkInfo.checkMobileNo()) {
document.getElementById('mobileNoError').innerText = '手机号格式有误';
isValid = false;
}
if (!checkInfo.checkPassword()) {
document.getElementById('passwordError').innerText = '密码须为长度为6-20位字母、数字或符号';
document.getElementById('confirmPasswordError').innerText = '两次输入的密码不一致';
isValid = false;
}
if (isValid) {
document.getElementById('encryptedPassword').value = md5(
document.getElementById('password').value
);
document.getElementById('encryptedConfirmPassword').value = md5(
document.getElementById('confirmPassword').value
);
return true;
}
return false;
}
function clearErrors() {
document.getElementById('mobileNoError').innerText = '';
document.getElementById('passwordError').innerText = '';
document.getElementById('confirmPasswordError').innerText = '';
}

View File

@@ -0,0 +1,14 @@
let slideIndex = 0;
const slides = document.getElementById('slide-container');
const totalSlides = slides.children.length;
function showSlides() {
slideIndex++;
if (slideIndex >= totalSlides) {
slideIndex = 0;
}
slides.style.transform = 'translateX(' + (-slideIndex * 100) + '%)';
setTimeout(showSlides, 5000); // Change image every 5 seconds
}
showSlides();

View File

@@ -0,0 +1,281 @@
<!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="{{ url_for('static', filename='css/book.css') }}">
</head>
<body>
<header>
<div class="header-content">
<div class="logo">KJF航班订票</div>
<div class="nav-buttons">
<a href="{{ url_for('index') }}">首页</a>
<a href="{{ url_for('order_list') }}">我的订单</a>
</div>
<div class="user-menu">
<span>{{ username }}</span>
<div class="dropdown">
<button class="dropbtn"></button>
<div class="dropdown-content">
<a href="{{ url_for('modify') }}">修改账户信息</a>
<a href="{{ url_for('logout') }}">退出登录</a>
</div>
</div>
</div>
</div>
</header>
<main>
<h2>预定航班</h2>
<div class="flight-info">
<table>
<thead>
<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_name }}</td>
<td>{{ flight.Arrival_airport_name }}</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>
<form id="booking-form" method="post" action="{{ url_for('book', flight_id=flight.ID) }}" onsubmit="return validateBookingForm()">
<div id="passenger-list">
<div class="passenger" data-index="0">
<h3>乘机人 1</h3>
<button type="button" class="delete-btn" onclick="removePassenger(this)">删除</button>
<div class="form-row">
<label for="card-code-0">身份证号:</label>
<input type="text" id="card-code-0" name="passengers[0][card_code]" oninput="validateCardCode(this)">
<div class="error-message" id="card-code-error-0"></div>
</div>
<div class="form-row">
<label for="name-0">真实姓名:</label>
<input type="text" id="name-0" name="passengers[0][name]">
</div>
<div class="form-row">
<label for="phone-number-0">手机号:</label>
<input type="text" id="phone-number-0" name="passengers[0][phone_number]" oninput="validatePhoneNumber(this)">
<div class="error-message" id="phone-number-error-0"></div>
</div>
<div class="form-row">
<label for="seat-class-0">座位级别:</label>
<select id="seat-class-0" name="passengers[0][seat_class]" onchange="updateSeatCount(this)">
<option value="" disabled selected>请选择座位级别</option>
<option value="First Class">头等舱</option>
<option value="Business Class">商务舱</option>
<option value="Economy Class">经济舱</option>
</select>
</div>
</div>
</div>
<div class="total-price">
总价: <span id="total-price">0</span>
</div>
<button type="button" class="btn" onclick="addPassenger()">添加乘机人</button>
<button type="submit" class="btn">预定</button>
</form>
</main>
<footer>
<p>&copy; 2024 KJF航班订票. 保留所有权利。</p>
</footer>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Initialize seat counts
var passengerCount = 1;
var firstClassSeats = {{ flight.First_class_seats_remaining }};
var businessClassSeats = {{ flight.Business_class_seats_remaining }};
var economyClassSeats = {{ flight.Economy_class_seats_remaining }};
var firstClassPrice = parseFloat(document.getElementById("first-class-price").textContent);
var businessClassPrice = parseFloat(document.getElementById("business-class-price").textContent);
var economyClassPrice = parseFloat(document.getElementById("economy-class-price").textContent);
window.addPassenger = function() {
passengerCount++;
const passengerList = document.getElementById('passenger-list');
const passengerDiv = document.createElement('div');
passengerDiv.classList.add('passenger');
passengerDiv.setAttribute('data-index', passengerCount - 1);
const disabledFirstClass = firstClassSeats <= 0 ? 'disabled' : '';
const disabledBusinessClass = businessClassSeats <= 0 ? 'disabled' : '';
const disabledEconomyClass = economyClassSeats <= 0 ? 'disabled' : '';
passengerDiv.innerHTML = `
<h3>乘机人 ${passengerCount}</h3>
<button type="button" class="delete-btn" onclick="removePassenger(this)">删除</button>
<div class="form-row">
<label for="card-code-${passengerCount - 1}">身份证号:</label>
<input type="text" id="card-code-${passengerCount - 1}" name="passengers[${passengerCount - 1}][card_code]" oninput="validateCardCode(this)">
<div class="error-message" id="card-code-error-${passengerCount - 1}"></div>
</div>
<div class="form-row">
<label for="name-${passengerCount - 1}">真实姓名:</label>
<input type="text" id="name-${passengerCount - 1}" name="passengers[${passengerCount - 1}][name]">
</div>
<div class="form-row">
<label for="phone-number-${passengerCount - 1}">手机号:</label>
<input type="text" id="phone-number-${passengerCount - 1}" name="passengers[${passengerCount - 1}][phone_number]" oninput="validatePhoneNumber(this)">
<div class="error-message" id="phone-number-error-${passengerCount - 1}"></div>
</div>
<div class="form-row">
<label for="seat-class-${passengerCount - 1}">座位级别:</label>
<select id="seat-class-${passengerCount - 1}" name="passengers[${passengerCount - 1}][seat_class]" onchange="updateSeatCount(this)">
<option value="" disabled selected>请选择座位级别</option>
<option value="First Class" ${disabledFirstClass}>头等舱</option>
<option value="Business Class" ${disabledBusinessClass}>商务舱</option>
<option value="Economy Class" ${disabledEconomyClass}>经济舱</option>
</select>
</div>
`;
passengerList.appendChild(passengerDiv);
};
window.removePassenger = function(button) {
const passengerDiv = button.parentElement;
const index = passengerDiv.getAttribute('data-index');
const seatClassSelect = document.getElementById(`seat-class-${index}`);
const seatClass = seatClassSelect.value;
if (seatClass === 'First Class') firstClassSeats++;
else if (seatClass === 'Business Class') businessClassSeats++;
else if (seatClass === 'Economy Class') economyClassSeats++;
passengerDiv.remove();
updateSeatDisplay();
calculateTotalPrice();
};
window.validateCardCode = function(input) {
const regexCardCode = /^([1-6][1-9]|50)\d{4}(18|19|20)\d{2}((0[1-9])|10|11|12)(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
const errorDiv = document.getElementById(`card-code-error-${input.id.split('-')[2]}`);
if (!regexCardCode.test(input.value)) {
errorDiv.textContent = '身份证号格式不正确';
} else {
errorDiv.textContent = '';
}
};
window.validatePhoneNumber = function(input) {
const regexMobileNo = /^1[3-9]\d{9}$/;
const errorDiv = document.getElementById(`phone-number-error-${input.id.split('-')[2]}`);
if (!regexMobileNo.test(input.value)) {
errorDiv.textContent = '手机号格式不正确';
} else {
errorDiv.textContent = '';
}
};
window.updateSeatCount = function(select) {
const index = select.id.split('-')[2];
const previousClass = select.getAttribute("data-previous-class");
const newClass = select.value;
if (previousClass) {
if (previousClass === 'First Class') firstClassSeats++;
else if (previousClass === 'Business Class') businessClassSeats++;
else if (previousClass === 'Economy Class') economyClassSeats++;
}
if (newClass === 'First Class') {
if (firstClassSeats <= 0) {
alert("头等舱座位已满");
select.value = "";
return;
}
firstClassSeats--;
} else if (newClass === 'Business Class') {
if (businessClassSeats <= 0) {
alert("商务舱座位已满");
select.value = "";
return;
}
businessClassSeats--;
} else if (newClass === 'Economy Class') {
if (economyClassSeats <= 0) {
alert("经济舱座位已满");
select.value = "";
return;
}
economyClassSeats--;
}
select.setAttribute("data-previous-class", newClass);
updateSeatDisplay();
calculateTotalPrice();
};
function updateSeatDisplay() {
document.getElementById("first-class-seats").textContent = firstClassSeats;
document.getElementById("business-class-seats").textContent = businessClassSeats;
document.getElementById("economy-class-seats").textContent = economyClassSeats;
}
function calculateTotalPrice() {
let totalPrice = 0;
const seatClassSelects = document.querySelectorAll("select[id^='seat-class-']");
seatClassSelects.forEach(select => {
if (select.value === 'First Class') totalPrice += firstClassPrice;
else if (select.value === 'Business Class') totalPrice += businessClassPrice;
else if (select.value === 'Economy Class') totalPrice += economyClassPrice;
});
document.getElementById("total-price").textContent = totalPrice.toFixed(2);
}
window.validateBookingForm = function() {
// Validate seat availability
if (firstClassSeats < 0 || businessClassSeats < 0 || economyClassSeats < 0) {
alert("预定的座位数不能超过余座数");
return false;
}
// Validate all card codes and phone numbers
const cardCodes = document.querySelectorAll("input[id^='card-code-']");
const phoneNumbers = document.querySelectorAll("input[id^='phone-number-']");
for (let cardCode of cardCodes) {
validateCardCode(cardCode);
if (document.getElementById(`card-code-error-${cardCode.id.split('-')[2]}`).textContent !== '') {
return false;
}
}
for (let phoneNumber of phoneNumbers) {
validatePhoneNumber(phoneNumber);
if (document.getElementById(`phone-number-error-${phoneNumber.id.split('-')[2]}`).textContent !== '') {
return false;
}
}
return true;
};
});
</script>
</body>
</html>

View File

@@ -0,0 +1,146 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KJF航班订票</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/index.css') }}">
<script src="{{ url_for('static', filename='js/index.js') }}" defer></script>
<script src="https://cdn.bootcss.com/blueimp-md5/2.12.0/js/md5.min.js"></script>
</head>
<body>
<header>
<div class="header-content">
<div class="logo">KJF航班订票</div>
<div class="nav-buttons">
<a href="{{ url_for('index') }}">首页</a>
<a href="{{ url_for('order_list') }}">我的订单</a>
</div>
<div class="user-menu">
<span>{{ username }}</span>
<div class="dropdown">
<button class="dropbtn"></button>
<div class="dropdown-content">
<a href="{{ url_for('modify') }}">修改账户信息</a>
<a href="{{ url_for('logout') }}">退出登录</a>
</div>
</div>
</div>
</div>
</header>
<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 id="ticket" class="tabcontent" style="display: block;">
<form action="{{ url_for('search') }}" method="get" class="search-form" onsubmit="return validateForm()">
<div class="form-row">
<label for="departure">出发地:</label>
<select id="departure" name="departure">
{% for city in cities %}
<option value="{{ city }}" {% if city == "北京" %}selected{% endif %}>{{ city }}</option>
{% endfor %}
</select>
</div>
<div class="form-row">
<label for="destination">目的地:</label>
<select id="destination" name="destination">
{% for city in cities %}
<option value="{{ city }}" {% if city == "上海" %}selected{% endif %}>{{ city }}</option>
{% endfor %}
</select>
<div id="destination-warning" class="error-message"></div>
</div>
<div class="form-row">
<label for="departure-date">出发日期:</label>
<input type="date" id="departure-date" name="departure-date" required>
<div id="date-warning" class="error-message"></div>
</div>
<div class="form-row">
<label for="passengers">乘客人数:</label>
<div class="passenger-input">
<button type="button" onclick="decrement()">-</button>
<input type="number" id="passengers" name="passengers" value="1" min="1" max="50">
<button type="button" onclick="increment()">+</button>
</div>
</div>
<div class="form-row form-row-center">
<button type="submit" class="btn">立即查询</button>
</div>
</form>
</div>
</div>
</main>
<footer>
<p>&copy; 2024 KJF航班订票. 保留所有权利。</p>
</footer>
<script src="{{ url_for('static', filename='js/slideshow.js') }}"></script>
<script>
function validateForm() {
var departure = document.getElementById('departure').value;
var destination = document.getElementById('destination').value;
var warning = document.getElementById('destination-warning');
var dateWarning = document.getElementById('date-warning');
var departureDate = document.getElementById('departure-date').value;
var today = new Date();
var selectedDate = new Date(departureDate);
today.setHours(0, 0, 0, 0); // Ensure time comparison is not affected
if (departure === destination) {
warning.textContent = '出发地和目的地不能相同';
return false;
} else {
warning.textContent = '';
}
if (selectedDate < today) {
dateWarning.textContent = '出发日期不能早于今天';
return false;
} else {
dateWarning.textContent = '';
}
return true;
}
function increment() {
var passengers = document.getElementById("passengers");
var value = parseInt(passengers.value, 10);
if (value < 50) {
passengers.value = value + 1;
}
}
function decrement() {
var passengers = document.getElementById("passengers");
var value = parseInt(passengers.value, 10);
if (value > 1) {
passengers.value = value - 1;
}
}
document.addEventListener('DOMContentLoaded', function() {
// Set default date to tomorrow
var departureDate = document.getElementById('departure-date');
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}`;
departureDate.setAttribute('min', `${year}-${month}-${day}`);
});
</script>
</body>
</html>

View File

@@ -0,0 +1,47 @@
<!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="{{ url_for('static', filename='css/login.css') }}">
<script src="{{ url_for('static', filename='js/login.js') }}" defer></script>
<script src="https://cdn.bootcss.com/blueimp-md5/2.12.0/js/md5.min.js"></script>
</head>
<body>
<header>
<h1>KJF航班订票</h1>
</header>
<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">
<h2>尽享精彩出行服务</h2>
<div class="login-form">
<input type="text" id="mobileNo" placeholder="手机号">
<div id="mobileNoError" class="error-message"></div>
<input type="password" id="password" placeholder="密码">
<div id="loginError" class="error-message"></div>
<input type="hidden" id="encryptedPassword">
<div class="buttons">
<button onclick="submitForm()">登录</button>
<a class="btn" href="{{ url_for('signup') }}">注册</a>
</div>
</div>
</div>
</main>
<footer>
<p>&copy; 2024 KJF航班订票. 保留所有权利。</p>
</footer>
<script src="{{ url_for('static', filename='js/slideshow.js') }}"></script>
</body>
</html>

View File

@@ -0,0 +1,133 @@
<!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="{{ url_for('static', filename='css/modify.css') }}">
<script src="https://cdn.bootcss.com/blueimp-md5/2.12.0/js/md5.min.js"></script>
<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>
<body>
<header>
<div class="header-content">
<a href="{{ url_for('index') }}" class="btn-back">返回主页</a>
<h1>KJF航班订票</h1>
<div class="header-spacer"></div>
</div>
</header>
<main>
<div class="content">
<h2>修改账户信息</h2>
<form action="#" method="post" id="modify-form">
<div class="tab">
<button type="button" class="tablinks active" onclick="openTab(event, 'deleteAccount')">删除账户</button>
<button type="button" class="tablinks" onclick="openTab(event, 'modifyPassword')">修改密码</button>
<button type="button" class="tablinks" onclick="openTab(event, 'modifyPhone')">修改手机号</button>
<button type="button" class="tablinks" onclick="openTab(event, 'modifyUsername')">修改用户名</button>
</div>
<input type="hidden" id="modifyType" name="modifyType" value="删除账户">
<div class="form-group">
<div>登陆密码:</div>
<input type="password" id="password" placeholder="请输入您的密码" required>
<input id="encryptedPassword" name="encryptedPassword" type="hidden">
</div>
<div id="deleteAccount" class="tabcontent">
<p>删除账户将无法恢复,请确认。</p>
</div>
<div id="modifyPassword" class="tabcontent" style="display:none">
<div class="form-group">
<div>新密码:</div>
<input type="password" id="newPassword" placeholder="6-20位字母、数字或符号">
<input id="encryptedNewPassword" name="encryptedNewPassword" type="hidden">
</div>
<div class="form-group">
<div>确认密码:</div>
<input type="password" id="confirmPassword" placeholder="再次输入您的新密码">
</div>
</div>
<div id="modifyPhone" class="tabcontent" style="display:none">
<p>原手机号为:{{ current_user_phone }}</p>
<div class="form-group">
<div>新手机号:</div>
<input type="text" id="mobileNo" name="mobileNo" value title="手机号码" aria-label="手机号码" maxlength="11" placeholder="11位手机号">
</div>
</div>
<div id="modifyUsername" class="tabcontent" style="display:none">
<p>原用户名为:{{ current_username }}</p>
<div class="form-group">
<div>新用户名:</div>
<input type="text" id="username" name="username" placeholder="请输入新的用户名">
</div>
</div>
<div class="form-group">
<button type="submit" onclick="return submitForm()">确认</button>
</div>
</form>
</div>
</main>
<footer>
<p>&copy; 2024 KJF航班订票. 保留所有权利。</p>
</footer>
<script src="{{ url_for('static', filename='js/checkInfo.js') }}"></script>
<script src="{{ url_for('static', filename='js/modify.js') }}"></script>
<script>
function submitForm() {
document.getElementById('encryptedPassword').value = md5(
document.getElementById('password').value
);
let modifyType = document.querySelector('.tablinks.active').textContent;
document.getElementById('modifyType').value = modifyType;
if (modifyType === '删除账户') {
return true;
} else if (modifyType === '修改密码') {
document.getElementById('encryptedNewPassword').value = md5(
document.getElementById('newPassword').value
);
return checkInfo.checkNewPassword();
} else if (modifyType === '修改手机号') {
return checkInfo.checkMobileNo();
} else if (modifyType === '修改用户名') {
return true;
}
}
function openTab(evt, tabName) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
document.getElementById(tabName).style.display = "block";
evt.currentTarget.className += " active";
}
document.addEventListener('DOMContentLoaded', function() {
let activeTab = document.querySelector('.tablinks.active');
if (activeTab) {
openTab({currentTarget: activeTab}, activeTab.getAttribute('onclick').split("'")[1]);
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,83 @@
<!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="{{ url_for('static', filename='css/order.css') }}">
</head>
<body>
<header>
<div class="header-content">
<div class="logo">KJF航班订票</div>
<div class="nav-buttons">
<a href="{{ url_for('index') }}">首页</a>
<a href="{{ url_for('order_list') }}">我的订单</a>
</div>
<div class="user-menu">
<span>{{ username }}</span>
<div class="dropdown">
<button class="dropbtn"></button>
<div class="dropdown-content">
<a href="{{ url_for('modify') }}">修改账户信息</a>
<a href="{{ url_for('logout') }}">退出登录</a>
</div>
</div>
</div>
</div>
</header>
<main>
<button onclick="location.href='{{ url_for('order_list') }}'" class="back-btn">返回订单列表</button>
<h2>订单详情</h2>
<div class="order-info">
<h3>航班信息</h3>
<p>航班号: {{ order.FlightID }}</p>
<p>航空公司: {{ order.Airline }}</p>
<p>出发机场: {{ order.Departure_airport_name }}</p>
<p>到达机场: {{ order.Arrival_airport_name }}</p>
<p>出发时间: {{ order.Departure_time }}</p>
<p>状态: {{ order.Status }}</p>
</div>
<div class="passenger-info">
<h3>乘机人信息</h3>
<table>
<thead>
<tr>
<th>身份证号</th>
<th>姓名</th>
<th>手机号</th>
<th>座位级别</th>
<th>票价</th>
</tr>
</thead>
<tbody>
{% for ticket in tickets %}
<tr>
<td>{{ ticket.PassengerID }}</td>
<td>{{ ticket.Name }}</td>
<td>{{ ticket.Phone_number }}</td>
<td>{{ ticket.Seat_class }}</td>
<td>{{ ticket.Price }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="order-summary">
<p>订单总价: {{ total_price }}</p>
<p>支付状态: {% if order.Paid %}已支付{% else %}未支付{% endif %}</p>
</div>
<form method="post" action="{{ url_for('cancel_order', order_id=order.ID) }}">
<button type="submit" class="btn cancel-btn">取消订单</button>
</form>
{% if not order.Paid %}
<form method="post" action="{{ url_for('pay_confirm', order_id=order.ID) }}">
<button type="submit" class="btn pay-btn">支付订单</button>
</form>
{% endif %}
</main>
<footer>
<p>&copy; 2024 KJF航班订票. 保留所有权利。</p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,72 @@
<!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="{{ url_for('static', filename='css/order_list.css') }}">
</head>
<body>
<header>
<div class="header-content">
<div class="logo">KJF航班订票</div>
<div class="nav-buttons">
<a href="{{ url_for('index') }}">首页</a>
<a href="{{ url_for('order_list') }}">我的订单</a>
</div>
<div class="user-menu">
<span>{{ username }}</span>
<div class="dropdown">
<button class="dropbtn"></button>
<div class="dropdown-content">
<a href="{{ url_for('modify') }}">修改账户信息</a>
<a href="{{ url_for('logout') }}">退出登录</a>
</div>
</div>
</div>
</div>
</header>
<main>
<h2>我的订单</h2>
<div class="order-list">
<table>
<thead>
<tr>
<th>航班号</th>
<th>航空公司</th>
<th>出发机场</th>
<th>到达机场</th>
<th>出发时间</th>
<th>状态</th>
<th>乘机人</th>
<th>订单总价</th>
<th>支付状态</th>
</tr>
</thead>
<tbody>
{% for order in orders %}
<tr class="order-row" onclick="location.href='{{ url_for('order', order_id=order.OrderID) }}'">
<td>{{ order.FlightID }}</td>
<td>{{ order.Airline }}</td>
<td>{{ order.Departure_airport_name }}</td>
<td>{{ order.Arrival_airport_name }}</td>
<td>{{ order.Departure_time }}</td>
<td>{{ order.Status }}</td>
<td>
{% for passenger in order.Passengers %}
{{ passenger }}<br>
{% endfor %}
</td>
<td>{{ order.TotalPrice }}</td>
<td>{% if order.Paid %}已支付{% else %}未支付{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</main>
<footer>
<p>&copy; 2024 KJF航班订票. 保留所有权利。</p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,179 @@
<!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="{{ url_for('static', filename='css/search.css') }}">
</head>
<body>
<header>
<div class="header-content">
<div class="logo">KJF航班订票</div>
<div class="nav-buttons">
<a href="{{ url_for('index') }}">首页</a>
<a href="{{ url_for('order_list') }}">我的订单</a>
</div>
<div class="user-menu">
<span>{{ username }}</span>
<div class="dropdown">
<button class="dropbtn"></button>
<div class="dropdown-content">
<a href="{{ url_for('modify') }}">修改账户信息</a>
<a href="{{ url_for('logout') }}">退出登录</a>
</div>
</div>
</div>
</div>
</header>
<main>
<div class="content">
<div id="ticket" class="tabcontent" style="display: block;">
<form action="{{ url_for('search') }}" method="get" class="search-form" onsubmit="return validateForm()">
<div class="form-row">
<label for="departure">出发地:</label>
<select id="departure" name="departure">
{% for city in cities %}
<option value="{{ city }}" {% if city == request.args.get('departure') %}selected{% endif %}>{{ city }}</option>
{% endfor %}
</select>
</div>
<div class="form-row">
<label for="destination">目的地:</label>
<select id="destination" name="destination">
{% for city in cities %}
<option value="{{ city }}" {% if city == request.args.get('destination') %}selected{% endif %}>{{ city }}</option>
{% endfor %}
</select>
<div id="destination-warning" class="error-message"></div>
</div>
<div class="form-row">
<label for="departure-date">出发日期:</label>
<input type="date" id="departure-date" name="departure-date" value="{{ request.args.get('departure-date', '') }}" required>
<div id="date-warning" class="error-message"></div>
</div>
<div class="form-row">
<label for="passengers">乘客人数:</label>
<div class="passenger-input">
<button type="button" onclick="decrement()">-</button>
<input type="number" id="passengers" name="passengers" value="{{ request.args.get('passengers', 1) }}" min="1" max="50">
<button type="button" onclick="increment()">+</button>
</div>
</div>
<div class="form-row form-row-center">
<button type="submit" class="btn">立即查询</button>
</div>
</form>
</div>
</div>
<h2>航班搜索结果</h2>
{% if flights %}
<table>
<thead>
<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>
{% for flight in flights %}
<tr class="flight-row" onclick="window.location.href='{{ url_for('book', flight_id=flight.ID) }}'">
<td>{{ flight.ID }}</td>
<td>{{ flight.Airline }}</td>
<td>{{ flight.Departure_airport_name }}</td>
<td>{{ flight.Arrival_airport_name }}</td>
<td>{{ flight.Departure_time }}</td>
<td>{{ flight.Arrival_time }}</td>
<td>{{ flight.First_class_seats_remaining }}</td>
<td>{{ flight.Business_class_seats_remaining }}</td>
<td>{{ flight.Economy_class_seats_remaining }}</td>
<td>{{ flight.First_class_price }}</td>
<td>{{ flight.Business_class_price }}</td>
<td>{{ flight.Economy_class_price }}</td>
<td>{{ flight.Status }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="no-results">没有找到符合条件的航班。</p>
{% endif %}
</main>
<footer>
<p>&copy; 2024 KJF航班订票. 保留所有权利。</p>
</footer>
<script src="{{ url_for('static', filename='js/search.js') }}"></script>
<script>
function validateForm() {
var departure = document.getElementById('departure').value;
var destination = document.getElementById('destination').value;
var warning = document.getElementById('destination-warning');
var dateWarning = document.getElementById('date-warning');
var departureDate = document.getElementById('departure-date').value;
var today = new Date();
var selectedDate = new Date(departureDate);
today.setHours(0, 0, 0, 0); // Ensure time comparison is not affected
if (departure === destination) {
warning.textContent = '出发地和目的地不能相同';
return false;
} else {
warning.textContent = '';
}
if (selectedDate < today) {
dateWarning.textContent = '出发日期不能早于今天';
return false;
} else {
dateWarning.textContent = '';
}
return true;
}
function increment() {
var passengers = document.getElementById("passengers");
var value = parseInt(passengers.value, 10);
if (value < 50) {
passengers.value = value + 1;
}
}
function decrement() {
var passengers = document.getElementById("passengers");
var value = parseInt(passengers.value, 10);
if (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}`;
departureDate.setAttribute('min', `${year}-${month}-${day}`);
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>注册 - KJF航班订票</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/signup.css') }}">
<script src="https://cdn.bootcss.com/blueimp-md5/2.12.0/js/md5.min.js"></script>
<script src="{{ url_for('static', filename='js/checkInfo.js') }}"></script>
<script src="{{ url_for('static', filename='js/signup.js') }}"></script>
</head>
<body>
<form action="#" method="post" id="signupForm">
<ul>
<li>
<div>
<div>用户名:</div>
<input type="text" id="username" name="username" placeholder="请输入用户名" required>
<div class="error-message">{{ errors.username }}</div>
</div>
</li>
<li>
<div>
<div>手机号:</div>
<input type="text" id="mobileNo" name="mobileNo" placeholder="请输入手机号" maxlength="11" required>
<div class="error-message" id="mobileNoError">{{ errors.mobileNo }}</div>
</div>
</li>
<li>
<div>
<div>登录密码:</div>
<input type="password" id="password" placeholder="6-20位字母、数字或符号" required>
<input id="encryptedPassword" name="encryptedPassword" type="hidden">
<div class="error-message" id="passwordError">{{ errors.password }}</div>
</div>
</li>
<li>
<div>
<div>确认密码:</div>
<input type="password" id="confirmPassword" placeholder="再次输入您的登录密码" required>
<input id="encryptedConfirmPassword" name="encryptedConfirmPassword" type="hidden">
<div class="error-message" id="confirmPasswordError">{{ errors.confirmPassword }}</div>
</div>
</li>
<div>
<button type="submit" onclick="return submitForm()">注册</button>
</div>
</ul>
</form>
</body>
</html>