20250819corp

This commit is contained in:
2025-09-19 16:47:15 +08:00
parent fc5fb355b4
commit b0e8a68204
12 changed files with 1148 additions and 809 deletions
+155 -42
View File
@@ -1,75 +1,188 @@
from flask import Flask, jsonify
import sqlite3
from datetime import datetime from datetime import datetime
from flask import Blueprint, jsonify, request
# 导入数据库工具
from db_utils import get_db_connection, close_db_connection
# 创建Flask蓝图 # 创建Flask蓝图
api_bp = Flask(__name__) api_bp = Blueprint('api', __name__)
# 连接数据库的辅助函数 # API函数1: 返回最新的参数配置记录
def get_db_connection():
conn = sqlite3.connect('car_info.db')
conn.row_factory = sqlite3.Row # 使返回的数据可以通过列名访问
return conn
# API函数1: 返回所有的参数信息
@api_bp.route('/api/parameters', methods=['GET']) @api_bp.route('/api/parameters', methods=['GET'])
def get_all_parameters(): def get_parameters():
try: try:
# 连接数据库 # 连接数据库
conn = get_db_connection() conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
# 查询所有参数配置记录 # 查询所有参数配置记录并只返回第一条(最新的一条)
cursor.execute(""" cursor.execute("""
SELECT id, param1, param2, param3, param4, param5, update_time SELECT id, param1, param2, param3, param4, param5, update_time
FROM parameter_config FROM parameter_config
ORDER BY update_time DESC ORDER BY update_time DESC
LIMIT 1
""")
# 获取第一条查询结果
row = cursor.fetchone()
# 关闭数据库连接
close_db_connection(conn)
# 如果没有查询到数据
if not row:
return jsonify({
'success': False,
'error': '未找到参数配置记录',
'data': None
}), 404
# 格式化查询结果
parameter = {
'id': row[0],
'param1': row[1],
'param2': row[2],
'param3': row[3],
'param4': row[4],
'param5': row[5],
'update_time': row[6]
}
# 返回JSON格式的数据
return jsonify({
'success': True,
'data': parameter
})
except Exception as e:
# 确保在异常情况下也关闭连接
if 'conn' in locals() and conn:
close_db_connection(conn)
return jsonify({
'success': False,
'error': str(e),
'data': []
}), 500
# API函数2: 返回query_history表中所有results_count=-1的数据
@api_bp.route('/api/pending-queries', methods=['GET'])
def get_pending_queries():
try:
# 连接数据库
conn = get_db_connection()
cursor = conn.cursor()
# 查询query_history表中results_count=-1的所有数据
cursor.execute("""
SELECT id, department, query_date, plate_number, phone, results_count, query_ip
FROM query_history
WHERE results_count = -1
ORDER BY query_date DESC
""") """)
# 获取所有查询结果 # 获取所有查询结果
rows = cursor.fetchall() rows = cursor.fetchall()
# 关闭数据库连接 # 关闭数据库连接
conn.close() close_db_connection(conn)
# 如果没有查询到数据
if not rows:
return jsonify({
'success': False,
'error': '未找到参数配置记录',
'data': []
}), 404
# 格式化查询结果 # 格式化查询结果
parameters_list = [] pending_queries = []
for row in rows: for row in rows:
parameters_list.append({ pending_query = {
'id': row['id'], 'id': row['id'],
'param1': row['param1'], 'department': row['department'],
'param2': row['param2'], 'query_date': row['query_date'],
'param3': row['param3'], 'plate_number': row['plate_number'],
'param4': row['param4'], 'phone': row['phone'],
'param5': row['param5'], 'results_count': row['results_count'],
'update_time': row['update_time'] 'query_ip': row['query_ip']
}) }
pending_queries.append(pending_query)
# 返回JSON格式的参数信息 # 返回JSON格式的数据
return jsonify({ return jsonify({
'success': True, 'success': True,
'message': '参数信息获取成功', 'data': pending_queries,
'data': parameters_list, 'count': len(pending_queries)
'total': len(parameters_list) })
}), 200
except Exception as e: except Exception as e:
# 处理异常情况 # 确保在异常情况下也关闭连接
if 'conn' in locals() and conn:
close_db_connection(conn)
return jsonify({ return jsonify({
'success': False, 'success': False,
'error': f'获取参数信息失败: {str(e)}', 'error': str(e),
'data': [] 'data': []
}), 500 }), 500
# 如果直接运行此文件(用于测试) # API函数3: 更新query_history表中对应记录的results_count值
if __name__ == '__main__': @api_bp.route('/api/update-query-result', methods=['POST'])
# 在生产环境中不应使用debug=True def update_query_result():
api_bp.run(debug=True) try:
# 获取前端提交的数据
data = request.json
if not data:
return jsonify({
'success': False,
'error': '请求数据不能为空',
'data': None
}), 400
# 检查必要的字段是否存在
if 'id' not in data or 'results_count' not in data:
return jsonify({
'success': False,
'error': '缺少必要的字段: id 或 results_count',
'data': None
}), 400
# 获取id和results_count值
query_id = data['id']
results_count = data['results_count']
# 连接数据库
conn = get_db_connection()
cursor = conn.cursor()
# 更新query_history表中的对应记录
cursor.execute("""
UPDATE query_history
SET results_count = ?
WHERE id = ?
""", (results_count, query_id))
# 提交更改
conn.commit()
# 获取受影响的行数
affected_rows = cursor.rowcount
# 关闭数据库连接
close_db_connection(conn)
# 检查是否有记录被更新
if affected_rows == 0:
return jsonify({
'success': False,
'error': '未找到id为 {} 的记录'.format(query_id),
'data': None
}), 404
# 返回成功响应
return jsonify({
'success': True,
'message': '记录更新成功',
'data': {
'id': query_id,
'results_count': results_count
}
})
except Exception as e:
# 确保在异常情况下也关闭连接
if 'conn' in locals() and conn:
close_db_connection(conn)
return jsonify({
'success': False,
'error': str(e),
'data': None
}), 500
+117 -79
View File
@@ -1,13 +1,15 @@
from flask import Flask, render_template, request, redirect, url_for, session, jsonify from flask import Flask, render_template, request, redirect, url_for, session, jsonify
from datetime import datetime, timedelta from datetime import datetime, timedelta
import sqlite3
import os import os
import random import random
from flask import Flask, render_template, request, redirect, url_for, session
# 导入用户模块 # 导入用户模块
from user import bp as user_bp, init_user_db from user import bp as user_bp, init_user_db
# 导入API模块 # 导入API模块
from api import api_bp from api import api_bp
# 导入数据库工具
from db_utils import get_db_connection, close_db_connection, close_global_connection
app = Flask(__name__) app = Flask(__name__)
app.secret_key = 'your_secret_key' # 生产环境中应该使用更安全的密钥 app.secret_key = 'your_secret_key' # 生产环境中应该使用更安全的密钥
@@ -27,7 +29,7 @@ def init_db():
# 初始化用户相关的数据库 # 初始化用户相关的数据库
init_user_db() init_user_db()
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
# 创建车辆信息表 # 创建车辆信息表
@@ -66,9 +68,9 @@ def init_db():
('浙E54321', '13500135001', '330501199001015678', '钱七', 'qianqi@example.com', '浙江省湖州市') ('浙E54321', '13500135001', '330501199001015678', '钱七', 'qianqi@example.com', '浙江省湖州市')
] ]
cursor.executemany("INSERT INTO car_owners (plate_number, phone, id_card, name, email, address) VALUES (?, ?, ?, ?, ?, ?)", test_cars) cursor.executemany("INSERT INTO car_owners (plate_number, phone, id_card, name, email, address) VALUES (?, ?, ?, ?, ?, ?)", test_cars)
except sqlite3.IntegrityError: except Exception as e:
# 数据已存在,忽略 # 数据已存在或其他错误,忽略
pass print(f"插入测试数据时出错: {e}")
# 单独处理查询历史演示数据,确保每次都能插入20条记录 # 单独处理查询历史演示数据,确保每次都能插入20条记录
try: try:
@@ -137,8 +139,12 @@ def init_db():
) )
except Exception as e: except Exception as e:
print(f"插入查询历史数据时出错: {e}") print(f"插入查询历史数据时出错: {e}")
conn.commit() try:
conn.close() conn.commit()
except Exception as e:
print(f"提交数据库更改时出错: {e}")
finally:
close_db_connection(conn)
# 首页重定向到登录页 # 首页重定向到登录页
@app.route('/') @app.route('/')
@@ -161,7 +167,7 @@ def query():
if not plate_number and not phone: if not plate_number and not phone:
return render_template('query.html', error='请至少输入车牌号或手机号', recent_queries=formatted_recent_queries) return render_template('query.html', error='请至少输入车牌号或手机号', recent_queries=formatted_recent_queries)
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
query_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S') query_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
@@ -180,32 +186,35 @@ def query():
# 从用户信息表获取部门信息 # 从用户信息表获取部门信息
try: try:
conn_dept = sqlite3.connect('car_info.db') # 可以复用同一个连接,不需要创建新连接
cursor_dept = conn_dept.cursor() cursor.execute("SELECT department FROM users WHERE username = ?", (session['username'],))
cursor_dept.execute("SELECT department FROM users WHERE username = ?", (session['username'],)) user_dept = cursor.fetchone()
user_dept = cursor_dept.fetchone() if user_dept:
department = user_dept[0] if user_dept else '未知部门' department = user_dept[0]
except Exception as e: except Exception as e:
print(f"获取部门信息时出错: {e}") print(f"获取部门信息时出错: {e}")
department = '未知部门'
finally:
if 'conn_dept' in locals():
conn_dept.close()
# 记录查询历史到数据库 # 记录查询历史到数据库
try: try:
conn_history = sqlite3.connect('car_info.db') # 继续使用同一个连接
cursor_history = conn_history.cursor() # 检查是否是从"增加"按钮提交的请求
cursor_history.execute( is_add_query = request.form.get('is_add_query', 'false').lower() == 'true'
# 如果是增加按钮提交的请求,将results_count设置为-1
if is_add_query:
results_count = -1
else:
results_count = len(results)
cursor.execute(
"INSERT INTO query_history (department, query_date, plate_number, phone, results_count, query_ip) VALUES (?, ?, ?, ?, ?, ?)", "INSERT INTO query_history (department, query_date, plate_number, phone, results_count, query_ip) VALUES (?, ?, ?, ?, ?, ?)",
(department, query_date, plate_number if plate_number else None, phone if phone else None, len(results), query_ip) (department, query_date, plate_number if plate_number else None, phone if phone else None, results_count, query_ip)
) )
conn_history.commit() conn.commit()
except Exception as e: except Exception as e:
print(f"记录查询历史时出错: {e}") print(f"记录查询历史时出错: {e}")
finally: finally:
if 'conn_history' in locals(): close_db_connection(conn)
conn_history.close()
# 处理查询结果 # 处理查询结果
car_owners = [] car_owners = []
@@ -241,68 +250,97 @@ def query_history_detail(history_id):
if 'username' not in session: if 'username' not in session:
return jsonify({'error': '未登录'}), 401 return jsonify({'error': '未登录'}), 401
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
# 获取查询历史记录 try:
cursor.execute("SELECT plate_number, phone FROM query_history WHERE id = ?", (history_id,)) # 获取查询历史记录
history = cursor.fetchone() cursor.execute("SELECT plate_number, phone FROM query_history WHERE id = ?", (history_id,))
history = cursor.fetchone()
if not history:
conn.close() if not history:
return jsonify({'error': '查询历史不存在'}), 404 return jsonify({'error': '查询历史不存在'}), 404
plate_number, phone = history plate_number, phone = history
# 根据车牌号和手机号查询车辆信息 # 根据车牌号和手机号查询车辆信息
if plate_number and phone: if plate_number and phone:
cursor.execute("SELECT * FROM car_owners WHERE plate_number = ? AND phone = ?", (plate_number, phone)) cursor.execute("SELECT * FROM car_owners WHERE plate_number = ? AND phone = ?", (plate_number, phone))
elif plate_number: elif plate_number:
cursor.execute("SELECT * FROM car_owners WHERE plate_number = ?", (plate_number,)) cursor.execute("SELECT * FROM car_owners WHERE plate_number = ?", (plate_number,))
else: else:
cursor.execute("SELECT * FROM car_owners WHERE phone = ?", (phone,)) cursor.execute("SELECT * FROM car_owners WHERE phone = ?", (phone,))
results = cursor.fetchall() results = cursor.fetchall()
conn.close()
# 格式化结果
# 格式化结果 car_owners = []
car_owners = [] for result in results:
for result in results: car_owners.append({
car_owners.append({ 'id': result[0],
'id': result[0], 'plate_number': result[1],
'plate_number': result[1], 'phone': result[2],
'phone': result[2], 'id_card': result[3],
'id_card': result[3], 'name': result[4],
'name': result[4], 'email': result[5],
'email': result[5], 'address': result[6]
'address': result[6] })
})
return jsonify({'car_owners': car_owners})
return jsonify({'car_owners': car_owners}) except Exception as e:
print(f"查询历史详情时出错: {e}")
return jsonify({'error': '查询失败'}), 500
finally:
close_db_connection(conn)
# 获取最近查询历史的辅助函数 # 获取最近查询历史的辅助函数
def get_recent_queries(): def get_recent_queries():
# 获取最近20条查询历史记录,按查询日期降序排列 # 获取最近20条查询历史记录,按查询日期降序排列
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute("SELECT id, department, query_date, plate_number, phone, results_count, query_ip FROM query_history ORDER BY query_date DESC LIMIT 20")
recent_queries = cursor.fetchall()
conn.close()
# 格式化查询历史数据 try:
formatted_recent_queries = [] cursor.execute("SELECT id, department, query_date, plate_number, phone, results_count, query_ip FROM query_history ORDER BY query_date DESC LIMIT 20")
for q in recent_queries: recent_queries = cursor.fetchall()
formatted_recent_queries.append({
'id': q[0], # 格式化查询历史数据
'department': q[1], formatted_recent_queries = []
'query_date': q[2], for q in recent_queries:
'plate_number': q[3] if q[3] else '未提供', formatted_recent_queries.append({
'phone': q[4] if q[4] else '未提供', 'id': q[0],
'results_count': q[5], 'department': q[1],
'query_ip': q[6] 'query_date': q[2],
}) 'plate_number': q[3] if q[3] else '未提供',
return formatted_recent_queries 'phone': q[4] if q[4] else '未提供',
'results_count': q[5],
'query_ip': q[6]
})
return formatted_recent_queries
except Exception as e:
print(f"获取最近查询历史时出错: {e}")
return []
finally:
close_db_connection(conn)
import signal
import sys
def signal_handler(sig, frame):
"""处理终止信号,确保在程序退出时关闭数据库连接"""
print('\n接收到终止信号,正在关闭数据库连接...')
close_global_connection()
print('数据库连接已关闭')
sys.exit(0)
if __name__ == '__main__': if __name__ == '__main__':
init_db() # 注册信号处理程序
app.run(debug=True) signal.signal(signal.SIGINT, signal_handler) # 处理Ctrl+C
signal.signal(signal.SIGTERM, signal_handler) # 处理终止信号
try:
init_db()
app.run(debug=True)
finally:
# 确保在程序退出时关闭数据库连接
close_global_connection()
BIN
View File
Binary file not shown.
+53 -29
View File
@@ -1,31 +1,55 @@
import sqlite3 # 导入数据库工具
from db_utils import get_db_connection, close_db_connection
# 连接数据库 # 检查数据库连接是否正常
conn = sqlite3.connect('car_info.db') def check_database_connection():
cursor = conn.cursor() try:
# 连接数据库
conn = get_db_connection()
cursor = conn.cursor()
# 执行简单的SQL查询来测试连接
cursor.execute("SELECT sqlite_version()")
version = cursor.fetchone()[0]
# 检查是否存在必要的表
necessary_tables = ['users', 'query_records']
tables_info = []
for table in necessary_tables:
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table,))
exists = cursor.fetchone() is not None
tables_info.append({"name": table, "exists": exists})
# 关闭数据库连接
close_db_connection(conn)
# 构造返回结果
result = {
"success": True,
"version": version,
"tables": tables_info
}
return result
except Exception as e:
# 确保在异常情况下也关闭连接
if 'conn' in locals() and conn:
close_db_connection(conn)
return {
"success": False,
"error": str(e)
}
# 查询所有表 # 如果直接运行此脚本,则执行检查操作
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'") if __name__ == "__main__":
tables = cursor.fetchall() result = check_database_connection()
print('数据库中的表:', tables) print(f"数据库连接状态: {'成功' if result['success'] else '失败'}")
if result['success']:
# 查询参数配置表 print(f"SQLite版本: {result['version']}")
cursor.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name='parameter_config'") print("表状态:")
result = cursor.fetchone() for table in result['tables']:
print('参数配置表SQL:', result) print(f" - {table['name']}: {'存在' if table['exists'] else '不存在'}")
else:
# 如果参数配置表存在,查询其结构 print(f"错误信息: {result['error']}")
if result:
cursor.execute("PRAGMA table_info(parameter_config)")
columns = cursor.fetchall()
print('参数配置表结构:')
for col in columns:
print(f"字段: {col[1]}, 类型: {col[2]}")
# 查询数据
cursor.execute("SELECT * FROM parameter_config")
data = cursor.fetchall()
print('参数配置数据:', data)
# 关闭连接
conn.close()
+90
View File
@@ -0,0 +1,90 @@
import sqlite3
import threading
# 数据库文件路径
db_path = 'car_info.db'
# 使用线程本地存储来保存每个线程的连接
_thread_local = threading.local()
# 全局连接引用计数
global_ref_count = 0
def get_db_connection():
"""获取数据库连接(线程本地存储模式)
使用线程本地存储确保每个线程都有自己的数据库连接,解决SQLite多线程问题
Returns:
sqlite3.Connection: 当前线程的数据库连接对象
"""
global global_ref_count
# 检查当前线程是否已有连接
if not hasattr(_thread_local, 'connection') or _thread_local.connection is None:
try:
# 为当前线程创建新连接
_thread_local.connection = sqlite3.connect(db_path)
# 设置行工厂,使查询结果以字典形式返回
_thread_local.connection.row_factory = sqlite3.Row
# 增加全局引用计数
global_ref_count += 1
except sqlite3.Error as e:
print(f"数据库连接失败: {e}")
raise
return _thread_local.connection
def close_db_connection(conn=None):
"""关闭数据库连接(线程本地存储模式)
1. 如果传入了连接对象,直接关闭它
2. 如果没有传入连接对象,但当前线程有连接,则关闭当前线程的连接
Args:
conn (sqlite3.Connection): 需要关闭的数据库连接对象(可选)
"""
global global_ref_count
# 如果指定了连接对象,直接关闭
if conn:
try:
conn.close()
# 如果关闭的是当前线程的连接,更新线程本地存储
if hasattr(_thread_local, 'connection') and _thread_local.connection == conn:
_thread_local.connection = None
global_ref_count = max(0, global_ref_count - 1)
except sqlite3.Error as e:
print(f"关闭数据库连接时出错: {e}")
else:
# 如果没有指定连接对象,尝试关闭当前线程的连接
if hasattr(_thread_local, 'connection') and _thread_local.connection is not None:
try:
_thread_local.connection.close()
_thread_local.connection = None
global_ref_count = max(0, global_ref_count - 1)
except sqlite3.Error as e:
print(f"关闭当前线程的数据库连接时出错: {e}")
def close_global_connection():
"""全局连接清理函数(线程本地存储模式)
在应用程序关闭时调用此函数,它会:
1. 关闭当前线程的数据库连接(如果有)
2. 重置全局引用计数
注意:由于使用了线程本地存储,无法直接关闭其他线程的连接。
每个线程应该在结束前自行关闭其连接。
"""
global global_ref_count
# 关闭当前线程的连接(如果有)
if hasattr(_thread_local, 'connection') and _thread_local.connection is not None:
try:
_thread_local.connection.close()
_thread_local.connection = None
except sqlite3.Error as e:
print(f"关闭当前线程的数据库连接时出错: {e}")
# 重置全局引用计数
global_ref_count = 0
+182 -65
View File
@@ -5,9 +5,14 @@
* @param {string} modalId - 模态框的ID * @param {string} modalId - 模态框的ID
*/ */
function showModal(modalId) { function showModal(modalId) {
const modal = document.getElementById(modalId); var modal = document.getElementById(modalId);
if (modal) { if (modal) {
modal.classList.remove('hidden'); // 使用className替代classList以提高兼容性
if (modal.classList) {
modal.classList.remove('hidden');
} else {
modal.className = modal.className.replace('hidden', '');
}
} }
} }
@@ -16,9 +21,14 @@ function showModal(modalId) {
* @param {string} modalId - 模态框的ID * @param {string} modalId - 模态框的ID
*/ */
function closeModal(modalId) { function closeModal(modalId) {
const modal = document.getElementById(modalId); var modal = document.getElementById(modalId);
if (modal) { if (modal) {
modal.classList.add('hidden'); // 使用className替代classList以提高兼容性
if (modal.classList) {
modal.classList.add('hidden');
} else {
modal.className += ' hidden';
}
} }
} }
@@ -27,7 +37,7 @@ function closeModal(modalId) {
* @param {string} modalId - 模态框的ID * @param {string} modalId - 模态框的ID
*/ */
function bindModalOutsideClick(modalId) { function bindModalOutsideClick(modalId) {
const modal = document.getElementById(modalId); var modal = document.getElementById(modalId);
if (modal) { if (modal) {
modal.addEventListener('click', function(e) { modal.addEventListener('click', function(e) {
if (e.target === this) { if (e.target === this) {
@@ -43,29 +53,69 @@ function bindModalOutsideClick(modalId) {
* @param {boolean} isSuccess - 是否为成功消息 * @param {boolean} isSuccess - 是否为成功消息
* @param {number} duration - 消息显示时长(毫秒) * @param {number} duration - 消息显示时长(毫秒)
*/ */
function showMessage(message, isSuccess = true, duration = 5000) { function showMessage(message, isSuccess, duration) {
const messageContainer = document.getElementById('messageContainer'); // 为参数设置默认值,不使用ES6默认参数
const successMessage = document.getElementById('successMessage'); isSuccess = (isSuccess === undefined) ? true : isSuccess;
const errorMessage = document.getElementById('errorMessage'); duration = (duration === undefined) ? 5000 : duration;
var messageContainer = document.getElementById('messageContainer');
var successMessage = document.getElementById('successMessage');
var errorMessage = document.getElementById('errorMessage');
if (!messageContainer || !successMessage || !errorMessage) return; if (!messageContainer || !successMessage || !errorMessage) return;
if (isSuccess) { if (isSuccess) {
successMessage.querySelector('span').textContent = message; successMessage.querySelector('span').textContent = message;
successMessage.classList.remove('hidden'); if (successMessage.classList) {
errorMessage.classList.add('hidden'); successMessage.classList.remove('hidden');
} else {
successMessage.className = successMessage.className.replace('hidden', '');
}
if (errorMessage.classList) {
errorMessage.classList.add('hidden');
} else {
errorMessage.className += ' hidden';
}
} else { } else {
errorMessage.querySelector('span').textContent = message; errorMessage.querySelector('span').textContent = message;
errorMessage.classList.remove('hidden'); if (errorMessage.classList) {
successMessage.classList.add('hidden'); errorMessage.classList.remove('hidden');
} else {
errorMessage.className = errorMessage.className.replace('hidden', '');
}
if (successMessage.classList) {
successMessage.classList.add('hidden');
} else {
successMessage.className += ' hidden';
}
} }
messageContainer.classList.remove('hidden'); if (messageContainer.classList) {
messageContainer.classList.remove('hidden');
} else {
messageContainer.className = messageContainer.className.replace('hidden', '');
}
// 自动隐藏 // 自动隐藏
setTimeout(() => { setTimeout(function() {
(isSuccess ? successMessage : errorMessage).classList.add('hidden'); if (isSuccess) {
messageContainer.classList.add('hidden'); if (successMessage.classList) {
successMessage.classList.add('hidden');
} else {
successMessage.className += ' hidden';
}
} else {
if (errorMessage.classList) {
errorMessage.classList.add('hidden');
} else {
errorMessage.className += ' hidden';
}
}
if (messageContainer.classList) {
messageContainer.classList.add('hidden');
} else {
messageContainer.className += ' hidden';
}
}, duration); }, duration);
} }
@@ -76,22 +126,32 @@ function showMessage(message, isSuccess = true, duration = 5000) {
* @param {string} message - 错误消息内容 * @param {string} message - 错误消息内容
* @param {string} successId - 成功消息容器的ID * @param {string} successId - 成功消息容器的ID
*/ */
function showFormError(errorId, messageId, message, successId = '') { function showFormError(errorId, messageId, message, successId) {
const errorContainer = document.getElementById(errorId); successId = successId || '';
const errorMessage = document.getElementById(messageId);
const successContainer = successId ? document.getElementById(successId) : null; var errorContainer = document.getElementById(errorId);
var errorMessage = document.getElementById(messageId);
var successContainer = successId ? document.getElementById(successId) : null;
if (!errorContainer || !errorMessage) return; if (!errorContainer || !errorMessage) return;
errorMessage.textContent = message; errorMessage.textContent = message;
errorContainer.classList.remove('hidden'); if (errorContainer.classList) {
errorContainer.classList.remove('hidden');
if (successContainer) { } else {
successContainer.classList.add('hidden'); errorContainer.className = errorContainer.className.replace('hidden', '');
} }
// 滚动到错误信息 if (successContainer) {
errorContainer.scrollIntoView({ behavior: 'smooth', block: 'start' }); if (successContainer.classList) {
successContainer.classList.add('hidden');
} else {
successContainer.className += ' hidden';
}
}
// 滚动到错误信息,使用简单滚动方式替代smooth behavior
errorContainer.scrollIntoView(false);
} }
/** /**
@@ -101,41 +161,71 @@ function showFormError(errorId, messageId, message, successId = '') {
* @param {string} message - 成功消息内容 * @param {string} message - 成功消息内容
* @param {string} errorId - 错误消息容器的ID * @param {string} errorId - 错误消息容器的ID
*/ */
function showFormSuccess(successId, messageId, message, errorId = '') { function showFormSuccess(successId, messageId, message, errorId) {
const successContainer = document.getElementById(successId); errorId = errorId || '';
const successMessage = document.getElementById(messageId);
const errorContainer = errorId ? document.getElementById(errorId) : null; var successContainer = document.getElementById(successId);
var successMessage = document.getElementById(messageId);
var errorContainer = errorId ? document.getElementById(errorId) : null;
if (!successContainer || !successMessage) return; if (!successContainer || !successMessage) return;
successMessage.textContent = message; successMessage.textContent = message;
successContainer.classList.remove('hidden'); if (successContainer.classList) {
successContainer.classList.remove('hidden');
if (errorContainer) { } else {
errorContainer.classList.add('hidden'); successContainer.className = successContainer.className.replace('hidden', '');
} }
// 滚动到成功信息 if (errorContainer) {
successContainer.scrollIntoView({ behavior: 'smooth', block: 'start' }); if (errorContainer.classList) {
errorContainer.classList.add('hidden');
} else {
errorContainer.className += ' hidden';
}
}
// 滚动到成功信息,使用简单滚动方式替代smooth behavior
successContainer.scrollIntoView(false);
} }
/** /**
* 发起API请求的通用函数 * 发起API请求的通用函数 - 使用Promise替代async/await以提高兼容性
* @param {string} url - 请求URL * @param {string} url - 请求URL
* @param {string} method - 请求方法 * @param {string} method - 请求方法
* @param {object} data - 请求数据 * @param {object} data - 请求数据
* @param {object} options - 附加选项 * @param {object} options - 附加选项
* @returns {Promise} - 返回Promise对象 * @returns {Promise} - 返回Promise对象
*/ */
async function apiRequest(url, method = 'GET', data = null, options = {}) { function apiRequest(url, method, data, options) {
const defaultOptions = { // 为参数设置默认值,不使用ES6默认参数
method = method || 'GET';
data = data || null;
options = options || {};
// 手动合并选项,不使用扩展运算符
var defaultOptions = {
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
method, method: method
...options
}; };
// 手动合并options
for (var key in options) {
if (options.hasOwnProperty(key)) {
if (key === 'headers' && options.headers) {
for (var headerKey in options.headers) {
if (options.headers.hasOwnProperty(headerKey)) {
defaultOptions.headers[headerKey] = options.headers[headerKey];
}
}
} else {
defaultOptions[key] = options[key];
}
}
}
if (data) { if (data) {
// 根据Content-Type处理数据 // 根据Content-Type处理数据
if (defaultOptions.headers['Content-Type'] === 'application/json') { if (defaultOptions.headers['Content-Type'] === 'application/json') {
@@ -145,13 +235,23 @@ async function apiRequest(url, method = 'GET', data = null, options = {}) {
} }
} }
try { // 使用Promise替代async/await
const response = await fetch(url, defaultOptions); return new Promise(function(resolve, reject) {
return await response.json(); fetch(url, defaultOptions)
} catch (error) { .then(function(response) {
console.error(`API请求错误 [${url}]:`, error); if (!response.ok) {
throw error; throw new Error('网络错误');
} }
return response.json();
})
.then(function(data) {
resolve(data);
})
.catch(function(error) {
console.error('API请求错误 [' + url + ']:', error);
reject(error);
});
});
} }
/** /**
@@ -159,7 +259,7 @@ async function apiRequest(url, method = 'GET', data = null, options = {}) {
* @param {string} formId - 表单ID * @param {string} formId - 表单ID
*/ */
function resetForm(formId) { function resetForm(formId) {
const form = document.getElementById(formId); var form = document.getElementById(formId);
if (form) { if (form) {
form.reset(); form.reset();
} }
@@ -171,44 +271,61 @@ function resetForm(formId) {
* @param {object} data - 表单数据对象 * @param {object} data - 表单数据对象
*/ */
function fillForm(formId, data) { function fillForm(formId, data) {
const form = document.getElementById(formId); var form = document.getElementById(formId);
if (!form || !data) return; if (!form || !data) return;
Object.keys(data).forEach(key => { // 使用传统的for-in循环替代forEach
const element = form.elements[key] || document.getElementById(key); for (var key in data) {
if (element) { if (data.hasOwnProperty(key)) {
element.value = data[key] || ''; var element = form.elements[key] || document.getElementById(key);
if (element) {
element.value = data[key] || '';
}
} }
}); }
} }
/** /**
* 防抖函数 * 防抖函数 - 使用传统函数替代箭头函数
* @param {Function} func - 要防抖的函数 * @param {Function} func - 要防抖的函数
* @param {number} delay - 延迟时间(毫秒) * @param {number} delay - 延迟时间(毫秒)
* @returns {Function} - 返回防抖后的函数 * @returns {Function} - 返回防抖后的函数
*/ */
function debounce(func, delay) { function debounce(func, delay) {
let timeoutId; var timeoutId;
return function(...args) { var that = this;
return function() {
var context = this;
var args = arguments;
clearTimeout(timeoutId); clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay); timeoutId = setTimeout(function() {
func.apply(context || that, args);
}, delay);
}; };
} }
/** /**
* 节流函数 * 节流函数 - 使用传统函数替代箭头函数
* @param {Function} func - 要节流的函数 * @param {Function} func - 要节流的函数
* @param {number} limit - 时间限制(毫秒) * @param {number} limit - 时间限制(毫秒)
* @returns {Function} - 返回节流后的函数 * @returns {Function} - 返回节流后的函数
*/ */
function throttle(func, limit) { function throttle(func, limit) {
let inThrottle; var inThrottle;
return function(...args) { var that = this;
return function() {
var context = this;
var args = arguments;
if (!inThrottle) { if (!inThrottle) {
func.apply(this, args); func.apply(context || that, args);
inThrottle = true; inThrottle = true;
setTimeout(() => inThrottle = false, limit); setTimeout(function() {
inThrottle = false;
}, limit);
} }
}; };
} }
-214
View File
@@ -1,214 +0,0 @@
// 通用工具函数
/**
* 显示模态框
* @param {string} modalId - 模态框的ID
*/
function showModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.remove('hidden');
}
}
/**
* 关闭模态框
* @param {string} modalId - 模态框的ID
*/
function closeModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.add('hidden');
}
}
/**
* 绑定模态框点击外部关闭事件
* @param {string} modalId - 模态框的ID
*/
function bindModalOutsideClick(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.addEventListener('click', function(e) {
if (e.target === this) {
closeModal(modalId);
}
});
}
}
/**
* 显示消息提示
* @param {string} message - 消息内容
* @param {boolean} isSuccess - 是否为成功消息
* @param {number} duration - 消息显示时长(毫秒)
*/
function showMessage(message, isSuccess = true, duration = 5000) {
const messageContainer = document.getElementById('messageContainer');
const successMessage = document.getElementById('successMessage');
const errorMessage = document.getElementById('errorMessage');
if (!messageContainer || !successMessage || !errorMessage) return;
if (isSuccess) {
successMessage.querySelector('span').textContent = message;
successMessage.classList.remove('hidden');
errorMessage.classList.add('hidden');
} else {
errorMessage.querySelector('span').textContent = message;
errorMessage.classList.remove('hidden');
successMessage.classList.add('hidden');
}
messageContainer.classList.remove('hidden');
// 自动隐藏
setTimeout(() => {
(isSuccess ? successMessage : errorMessage).classList.add('hidden');
messageContainer.classList.add('hidden');
}, duration);
}
/**
* 显示表单错误消息
* @param {string} errorId - 错误容器的ID
* @param {string} messageId - 错误消息的ID
* @param {string} message - 错误消息内容
* @param {string} successId - 成功消息容器的ID
*/
function showFormError(errorId, messageId, message, successId = '') {
const errorContainer = document.getElementById(errorId);
const errorMessage = document.getElementById(messageId);
const successContainer = successId ? document.getElementById(successId) : null;
if (!errorContainer || !errorMessage) return;
errorMessage.textContent = message;
errorContainer.classList.remove('hidden');
if (successContainer) {
successContainer.classList.add('hidden');
}
// 滚动到错误信息
errorContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
/**
* 显示表单成功消息
* @param {string} successId - 成功容器的ID
* @param {string} messageId - 成功消息的ID
* @param {string} message - 成功消息内容
* @param {string} errorId - 错误消息容器的ID
*/
function showFormSuccess(successId, messageId, message, errorId = '') {
const successContainer = document.getElementById(successId);
const successMessage = document.getElementById(messageId);
const errorContainer = errorId ? document.getElementById(errorId) : null;
if (!successContainer || !successMessage) return;
successMessage.textContent = message;
successContainer.classList.remove('hidden');
if (errorContainer) {
errorContainer.classList.add('hidden');
}
// 滚动到成功信息
successContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
/**
* 发起API请求的通用函数
* @param {string} url - 请求URL
* @param {string} method - 请求方法
* @param {object} data - 请求数据
* @param {object} options - 附加选项
* @returns {Promise} - 返回Promise对象
*/
async function apiRequest(url, method = 'GET', data = null, options = {}) {
const defaultOptions = {
headers: {
'Content-Type': 'application/json'
},
method,
...options
};
if (data) {
// 根据Content-Type处理数据
if (defaultOptions.headers['Content-Type'] === 'application/json') {
defaultOptions.body = JSON.stringify(data);
} else if (defaultOptions.headers['Content-Type'] === 'application/x-www-form-urlencoded') {
defaultOptions.body = new URLSearchParams(data);
}
}
try {
const response = await fetch(url, defaultOptions);
return await response.json();
} catch (error) {
console.error(`API请求错误 [${url}]:`, error);
throw error;
}
}
/**
* 重置表单
* @param {string} formId - 表单ID
*/
function resetForm(formId) {
const form = document.getElementById(formId);
if (form) {
form.reset();
}
}
/**
* 填充表单数据
* @param {string} formId - 表单ID
* @param {object} data - 表单数据对象
*/
function fillForm(formId, data) {
const form = document.getElementById(formId);
if (!form || !data) return;
Object.keys(data).forEach(key => {
const element = form.elements[key] || document.getElementById(key);
if (element) {
element.value = data[key] || '';
}
});
}
/**
* 防抖函数
* @param {Function} func - 要防抖的函数
* @param {number} delay - 延迟时间(毫秒)
* @returns {Function} - 返回防抖后的函数
*/
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
/**
* 节流函数
* @param {Function} func - 要节流的函数
* @param {number} limit - 时间限制(毫秒)
* @returns {Function} - 返回节流后的函数
*/
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
+220 -90
View File
@@ -105,7 +105,7 @@
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">查询日期</th> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">查询日期</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">车牌号</th> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">车牌号</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">手机号</th> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">手机号</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">查询结果</th> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">状态</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">登录部门</th> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">登录部门</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">登录IP</th> <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">登录IP</th>
</tr> </tr>
@@ -119,12 +119,18 @@
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ query.phone }}</td> <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ query.phone }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm"> <td class="px-6 py-4 whitespace-nowrap text-sm">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full
{% if query.results_count > 0 %} {% if query.results_count == -1 %}
bg-warning/10 text-warning
{% elif query.results_count > 0 %}
bg-success/10 text-success bg-success/10 text-success
{% else %} {% else %}
bg-error/10 text-error bg-error/10 text-error
{% endif %}"> {% endif %}">
{{ query.results_count }} 条记录 {% if query.results_count == -1 %}
待查询
{% else %}
{{ query.results_count }} 条记录
{% endif %}
</span> </span>
</td> </td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ query.department }}</td> <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ query.department }}</td>
@@ -219,59 +225,90 @@
// 等待DOM加载完成 // 等待DOM加载完成
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// 获取DOM元素 // 获取DOM元素
const addQueryBtn = document.getElementById('addQueryBtn'); var addQueryBtn = document.getElementById('addQueryBtn');
const addQueryModal = document.getElementById('addQueryModal'); var addQueryModal = document.getElementById('addQueryModal');
const closeAddModal = document.getElementById('closeAddModal'); var closeAddModal = document.getElementById('closeAddModal');
const cancelAddBtn = document.getElementById('cancelAddBtn'); var cancelAddBtn = document.getElementById('cancelAddBtn');
const addQueryForm = document.getElementById('addQueryForm'); var addQueryForm = document.getElementById('addQueryForm');
const addQueryLoading = document.getElementById('addQueryLoading'); var addQueryLoading = document.getElementById('addQueryLoading');
const detailModal = document.getElementById('detailModal'); var detailModal = document.getElementById('detailModal');
const closeModal = document.getElementById('closeModal'); var closeModal = document.getElementById('closeModal');
const loadingState = document.getElementById('loadingState'); var loadingState = document.getElementById('loadingState');
const noResultState = document.getElementById('noResultState'); var noResultState = document.getElementById('noResultState');
const resultList = document.getElementById('resultList'); var resultList = document.getElementById('resultList');
const carOwnerCards = document.getElementById('carOwnerCards'); var carOwnerCards = document.getElementById('carOwnerCards');
// 为查询历史记录行添加点击事件 // 为查询历史记录行添加点击事件
document.querySelectorAll('tr[data-history-id]').forEach(row => { var rows = document.querySelectorAll('tr[data-history-id]');
row.addEventListener('click', function() { for (var i = 0; i < rows.length; i++) {
const historyId = this.getAttribute('data-history-id'); rows[i].addEventListener('click', function() {
var historyId = this.getAttribute('data-history-id');
loadHistoryDetail(historyId); loadHistoryDetail(historyId);
}); });
}); }
// 增加查询按钮点击事件 // 增加查询按钮点击事件
addQueryBtn.addEventListener('click', function() { addQueryBtn.addEventListener('click', function() {
addQueryModal.classList.remove('hidden'); // 兼容classList API
if (addQueryModal.classList) {
addQueryModal.classList.remove('hidden');
} else {
addQueryModal.className = addQueryModal.className.replace('hidden', '');
}
addQueryForm.reset(); addQueryForm.reset();
}); });
// 关闭增加查询模态框 // 关闭增加查询模态框
closeAddModal.addEventListener('click', function() { closeAddModal.addEventListener('click', function() {
addQueryModal.classList.add('hidden'); // 兼容classList API
if (addQueryModal.classList) {
addQueryModal.classList.add('hidden');
} else {
addQueryModal.className += ' hidden';
}
}); });
// 取消增加查询 // 取消增加查询
cancelAddBtn.addEventListener('click', function() { cancelAddBtn.addEventListener('click', function() {
addQueryModal.classList.add('hidden'); // 兼容classList API
if (addQueryModal.classList) {
addQueryModal.classList.add('hidden');
} else {
addQueryModal.className += ' hidden';
}
}); });
// 点击增加查询模态框背景关闭 // 点击增加查询模态框背景关闭
addQueryModal.addEventListener('click', function(e) { addQueryModal.addEventListener('click', function(e) {
if (e.target === addQueryModal) { if (e.target === addQueryModal) {
addQueryModal.classList.add('hidden'); // 兼容classList API
if (addQueryModal.classList) {
addQueryModal.classList.add('hidden');
} else {
addQueryModal.className += ' hidden';
}
} }
}); });
// 关闭详情模态框 // 关闭详情模态框
closeModal.addEventListener('click', function() { closeModal.addEventListener('click', function() {
detailModal.classList.add('hidden'); // 兼容classList API
if (detailModal.classList) {
detailModal.classList.add('hidden');
} else {
detailModal.className += ' hidden';
}
}); });
// 点击详情模态框背景关闭 // 点击详情模态框背景关闭
detailModal.addEventListener('click', function(e) { detailModal.addEventListener('click', function(e) {
if (e.target === detailModal) { if (e.target === detailModal) {
detailModal.classList.add('hidden'); // 兼容classList API
if (detailModal.classList) {
detailModal.classList.add('hidden');
} else {
detailModal.className += ' hidden';
}
} }
}); });
@@ -279,8 +316,8 @@
addQueryForm.addEventListener('submit', function(e) { addQueryForm.addEventListener('submit', function(e) {
e.preventDefault(); e.preventDefault();
const plateNumber = document.getElementById('add_plate_number').value.trim(); var plateNumber = document.getElementById('add_plate_number').value.trim();
const phone = document.getElementById('add_phone').value.trim(); var phone = document.getElementById('add_phone').value.trim();
if (!plateNumber && !phone) { if (!plateNumber && !phone) {
alert('请至少输入车牌号或手机号'); alert('请至少输入车牌号或手机号');
@@ -288,107 +325,179 @@
} }
// 显示加载状态 // 显示加载状态
addQueryForm.classList.add('hidden'); // 兼容classList API
addQueryLoading.classList.remove('hidden'); if (addQueryForm.classList) {
addQueryForm.classList.add('hidden');
} else {
addQueryForm.className += ' hidden';
}
if (addQueryLoading.classList) {
addQueryLoading.classList.remove('hidden');
} else {
addQueryLoading.className = addQueryLoading.className.replace('hidden', '');
}
// 构建表单数据 // 构建表单数据
const formData = new FormData(); var formData = new FormData();
formData.append('plate_number', plateNumber); formData.append('plate_number', plateNumber);
formData.append('phone', phone); formData.append('phone', phone);
formData.append('is_add_query', 'true');
// 发送请求到查询路由 // 发送请求到查询路由
fetch('/query', { fetch('/query', {
method: 'POST', method: 'POST',
body: formData body: formData
}) })
.then(response => { .then(function(response) {
if (!response.ok) { if (!response.ok) {
throw new Error('网络错误'); throw new Error('网络错误');
} }
// 重新加载页面以显示新的查询记录 // 重新加载页面以显示新的查询记录
window.location.reload(); window.location.reload();
}) })
.catch(error => { .catch(function(error) {
console.error('Error submitting query:', error); console.error('Error submitting query:', error);
alert('提交失败,请重试'); alert('提交失败,请重试');
// 恢复表单显示 // 恢复表单显示
addQueryForm.classList.remove('hidden'); // 兼容classList API
addQueryLoading.classList.add('hidden'); if (addQueryForm.classList) {
addQueryForm.classList.remove('hidden');
} else {
addQueryForm.className = addQueryForm.className.replace('hidden', '');
}
if (addQueryLoading.classList) {
addQueryLoading.classList.add('hidden');
} else {
addQueryLoading.className += ' hidden';
}
}); });
}); });
// 加载查询历史详情 // 加载查询历史详情
function loadHistoryDetail(historyId) { function loadHistoryDetail(historyId) {
// 显示模态框和加载状态 // 显示模态框和加载状态
detailModal.classList.remove('hidden'); // 兼容classList API
loadingState.classList.remove('hidden'); if (detailModal.classList) {
noResultState.classList.add('hidden'); detailModal.classList.remove('hidden');
resultList.classList.add('hidden'); } else {
detailModal.className = detailModal.className.replace('hidden', '');
}
if (loadingState.classList) {
loadingState.classList.remove('hidden');
} else {
loadingState.className = loadingState.className.replace('hidden', '');
}
if (noResultState.classList) {
noResultState.classList.add('hidden');
} else {
noResultState.className += ' hidden';
}
if (resultList.classList) {
resultList.classList.add('hidden');
} else {
resultList.className += ' hidden';
}
carOwnerCards.innerHTML = ''; carOwnerCards.innerHTML = '';
// 发送AJAX请求获取详情 // 发送AJAX请求获取详情
fetch(`/query_history_detail/${historyId}`) fetch('/query_history_detail/' + historyId)
.then(response => response.json()) .then(function(response) {
.then(data => { return response.json();
})
.then(function(data) {
// 隐藏加载状态 // 隐藏加载状态
loadingState.classList.add('hidden'); // 兼容classList API
if (loadingState.classList) {
loadingState.classList.add('hidden');
} else {
loadingState.className += ' hidden';
}
if (data.error) { if (data.error) {
// 显示错误信息 // 显示错误信息
noResultState.querySelector('p').textContent = data.error; noResultState.querySelector('p').textContent = data.error;
noResultState.classList.remove('hidden'); // 兼容classList API
if (noResultState.classList) {
noResultState.classList.remove('hidden');
} else {
noResultState.className = noResultState.className.replace('hidden', '');
}
} else if (data.car_owners && data.car_owners.length > 0) { } else if (data.car_owners && data.car_owners.length > 0) {
// 显示结果列表 // 显示结果列表
resultList.classList.remove('hidden'); // 兼容classList API
if (resultList.classList) {
resultList.classList.remove('hidden');
} else {
resultList.className = resultList.className.replace('hidden', '');
}
// 添加车辆信息卡片 // 添加车辆信息卡片
data.car_owners.forEach(carOwner => { for (var i = 0; i < data.car_owners.length; i++) {
const card = document.createElement('div'); var carOwner = data.car_owners[i];
var card = document.createElement('div');
card.className = 'bg-white border border-gray-200 rounded-lg p-5 shadow-sm hover:shadow-md transition-shadow duration-200'; card.className = 'bg-white border border-gray-200 rounded-lg p-5 shadow-sm hover:shadow-md transition-shadow duration-200';
card.innerHTML = `
<h4 class="text-lg font-medium text-gray-800 mb-4 flex items-center"> // 使用字符串拼接替代模板字符串
<i class="fa fa-car text-primary mr-2"></i> var cardHtml = '';
车辆信息 cardHtml += '<h4 class="text-lg font-medium text-gray-800 mb-4 flex items-center">';
</h4> cardHtml += ' <i class="fa fa-car text-primary mr-2"></i>';
<div class="space-y-3"> cardHtml += ' 车辆信息';
<div class="flex justify-between items-center"> cardHtml += '</h4>';
<span class="text-gray-500">车牌号:</span> cardHtml += '<div class="space-y-3">';
<span class="font-medium text-gray-800">${carOwner.plate_number}</span> cardHtml += ' <div class="flex justify-between items-center">';
</div> cardHtml += ' <span class="text-gray-500">车牌号:</span>';
<div class="flex justify-between items-center"> cardHtml += ' <span class="font-medium text-gray-800">' + (carOwner.plate_number || '') + '</span>';
<span class="text-gray-500">手机号:</span> cardHtml += ' </div>';
<span class="font-medium text-gray-800">${carOwner.phone}</span> cardHtml += ' <div class="flex justify-between items-center">';
</div> cardHtml += ' <span class="text-gray-500">手机号:</span>';
<div class="flex justify-between items-center"> cardHtml += ' <span class="font-medium text-gray-800">' + (carOwner.phone || '') + '</span>';
<span class="text-gray-500">车主姓名:</span> cardHtml += ' </div>';
<span class="font-medium text-gray-800">${carOwner.name}</span> cardHtml += ' <div class="flex justify-between items-center">';
</div> cardHtml += ' <span class="text-gray-500">车主姓名:</span>';
<div class="flex justify-between items-center"> cardHtml += ' <span class="font-medium text-gray-800">' + (carOwner.name || '') + '</span>';
<span class="text-gray-500">身份证号:</span> cardHtml += ' </div>';
<span class="font-medium text-gray-800">${carOwner.id_card}</span> cardHtml += ' <div class="flex justify-between items-center">';
</div> cardHtml += ' <span class="text-gray-500">身份证号:</span>';
<div class="flex justify-between items-center"> cardHtml += ' <span class="font-medium text-gray-800">' + (carOwner.id_card || '') + '</span>';
<span class="text-gray-500">电子邮箱:</span> cardHtml += ' </div>';
<span class="font-medium text-gray-800">${carOwner.email || '未提供'}</span> cardHtml += ' <div class="flex justify-between items-center">';
</div> cardHtml += ' <span class="text-gray-500">电子邮箱:</span>';
<div class="flex justify-between items-center"> cardHtml += ' <span class="font-medium text-gray-800">' + (carOwner.email || '未提供') + '</span>';
<span class="text-gray-500">居住地址:</span> cardHtml += ' </div>';
<span class="font-medium text-gray-800">${carOwner.address || '未提供'}</span> cardHtml += ' <div class="flex justify-between items-center">';
</div> cardHtml += ' <span class="text-gray-500">居住地址:</span>';
</div> cardHtml += ' <span class="font-medium text-gray-800">' + (carOwner.address || '未提供') + '</span>';
`; cardHtml += ' </div>';
cardHtml += '</div>';
card.innerHTML = cardHtml;
carOwnerCards.appendChild(card); carOwnerCards.appendChild(card);
}); }
} else { } else {
// 显示无结果状态 // 显示无结果状态
noResultState.classList.remove('hidden'); // 兼容classList API
if (noResultState.classList) {
noResultState.classList.remove('hidden');
} else {
noResultState.className = noResultState.className.replace('hidden', '');
}
} }
}) })
.catch(error => { .catch(function(error) {
// 隐藏加载状态,显示错误信息 // 隐藏加载状态,显示错误信息
loadingState.classList.add('hidden'); // 兼容classList API
if (loadingState.classList) {
loadingState.classList.add('hidden');
} else {
loadingState.className += ' hidden';
}
noResultState.querySelector('p').textContent = '加载详情时发生错误'; noResultState.querySelector('p').textContent = '加载详情时发生错误';
noResultState.classList.remove('hidden'); // 兼容classList API
if (noResultState.classList) {
noResultState.classList.remove('hidden');
} else {
noResultState.className = noResultState.className.replace('hidden', '');
}
console.error('Error loading history detail:', error); console.error('Error loading history detail:', error);
}); });
} }
@@ -396,17 +505,19 @@
// 响应式处理:当窗口大小改变时,重新绑定事件(因为表格可能会重新渲染) // 响应式处理:当窗口大小改变时,重新绑定事件(因为表格可能会重新渲染)
window.addEventListener('resize', function() { window.addEventListener('resize', function() {
// 重新绑定事件到查询历史记录行 // 重新绑定事件到查询历史记录行
document.querySelectorAll('tr[data-history-id]').forEach(row => { var rows = document.querySelectorAll('tr[data-history-id]');
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
// 先移除旧的事件监听器 // 先移除旧的事件监听器
const newRow = row.cloneNode(true); var newRow = row.cloneNode(true);
row.parentNode.replaceChild(newRow, row); row.parentNode.replaceChild(newRow, row);
// 添加新的事件监听器 // 添加新的事件监听器
newRow.addEventListener('click', function() { newRow.addEventListener('click', function() {
const historyId = this.getAttribute('data-history-id'); var historyId = this.getAttribute('data-history-id');
loadHistoryDetail(historyId); loadHistoryDetail(historyId);
}); });
}); }
}); });
}); });
</script> </script>
@@ -416,14 +527,33 @@
<script> <script>
// 全局搜索变量 // 全局搜索变量
let searchQuery = ''; var searchQuery = '';
</script> </script>
<script> <script>
// ESC键关闭详情弹窗 // ESC键关闭详情弹窗
document.addEventListener('keydown', function(e) { document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && !document.getElementById('detailModal').classList.contains('hidden')) { // 使用keyCode替代key,提高兼容性
document.getElementById('detailModal').classList.add('hidden'); var key = e.key || e.keyCode;
var isEscape = key === 'Escape' || key === 'Esc' || key === 27;
var detailModal = document.getElementById('detailModal');
var isHidden = false;
// 兼容classList API
if (detailModal.classList) {
isHidden = detailModal.classList.contains('hidden');
} else {
isHidden = detailModal.className.indexOf('hidden') !== -1;
}
if (isEscape && !isHidden) {
// 兼容classList API
if (detailModal.classList) {
detailModal.classList.add('hidden');
} else {
detailModal.className += ' hidden';
}
} }
}); });
</script> </script>
+6 -7
View File
@@ -286,11 +286,11 @@
// 加载用户列表 // 加载用户列表
function loadUsers() { function loadUsers() {
apiRequest('{{ url_for('user.get_users') }}') apiRequest('/api/users')
.then(data => { .then(data => {
if (data.success) { if (data.success) {
usersData = data.users; usersData = data.data;
totalUsers = usersData.length; totalUsers = data.total;
renderUsersTable(); renderUsersTable();
} else { } else {
showMessage('加载用户列表失败:' + (data.error || '未知错误'), false); showMessage('加载用户列表失败:' + (data.error || '未知错误'), false);
@@ -377,19 +377,18 @@
const filterDepartment = document.getElementById('filterDepartment').value; const filterDepartment = document.getElementById('filterDepartment').value;
const filterStatus = document.getElementById('filterStatus').value; const filterStatus = document.getElementById('filterStatus').value;
fetch('{{ url_for('user.get_users') }}') apiRequest('/api/users')
.then(response => response.json())
.then(data => { .then(data => {
if (data.success) { if (data.success) {
// 应用过滤条件 // 应用过滤条件
usersData = data.users.filter(user => { usersData = data.data.filter(user => {
const matchesUsername = user.username.toLowerCase().includes(searchUsername); const matchesUsername = user.username.toLowerCase().includes(searchUsername);
const matchesDepartment = !filterDepartment || user.department === filterDepartment; const matchesDepartment = !filterDepartment || user.department === filterDepartment;
const matchesStatus = !filterStatus || user.status === filterStatus; const matchesStatus = !filterStatus || user.status === filterStatus;
return matchesUsername && matchesDepartment && matchesStatus; return matchesUsername && matchesDepartment && matchesStatus;
}); });
totalUsers = usersData.length; totalUsers = data.total;
currentPage = 1; // 重置到第一页 currentPage = 1; // 重置到第一页
renderUsersTable(); renderUsersTable();
} else { } else {
+68 -7
View File
@@ -1,10 +1,13 @@
import sqlite3 import datetime
# 导入数据库工具
from db_utils import get_db_connection, close_db_connection
# 连接到数据库并确保users表结构正确 # 连接到数据库并确保users表结构正确
def update_users_table(): def update_users_table():
try: try:
# 连接数据库 # 连接数据库
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
# 检查users表的当前结构 # 检查users表的当前结构
@@ -42,7 +45,10 @@ def update_users_table():
print("已更新admin用户信息") print("已更新admin用户信息")
# 提交更改 # 提交更改
conn.commit() try:
conn.commit()
except Exception as e:
print(f"提交更改时出错: {e}")
# 再次验证表结构 # 再次验证表结构
cursor.execute("PRAGMA table_info(users)") cursor.execute("PRAGMA table_info(users)")
@@ -59,14 +65,69 @@ def update_users_table():
print(f"ID: {admin_user[0]}, 用户名: {admin_user[1]}, 姓名: {admin_user[2]}, 电话: {admin_user[3]}, 部门: {admin_user[4]}") print(f"ID: {admin_user[0]}, 用户名: {admin_user[1]}, 姓名: {admin_user[2]}, 电话: {admin_user[3]}, 部门: {admin_user[4]}")
# 关闭连接 # 关闭连接
conn.close() close_db_connection(conn)
print("\n数据库更新完成") print("\n数据库更新完成")
except Exception as e: except Exception as e:
print(f"更新数据库时出错: {str(e)}") print(f"更新数据库时出错: {str(e)}")
# 确保在异常情况下也关闭连接 # 确保在异常情况下也关闭连接
if 'conn' in locals(): if 'conn' in locals() and conn:
conn.close() close_db_connection(conn)
# 更新数据库中的数据
def update_database():
try:
# 连接数据库
conn = get_db_connection()
cursor = conn.cursor()
# 检查是否存在测试数据表
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='test_data'")
if not cursor.fetchone():
# 如果不存在,创建测试数据表
cursor.execute('''
CREATE TABLE test_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
value TEXT,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
conn.commit()
print("测试数据表创建成功")
# 检查是否有测试数据,如果没有则插入
cursor.execute("SELECT COUNT(*) FROM test_data")
count = cursor.fetchone()[0]
if count == 0:
# 插入测试数据
test_data = [
('测试数据1', 'value1', datetime.datetime.now()),
('测试数据2', 'value2', datetime.datetime.now()),
('测试数据3', 'value3', datetime.datetime.now())
]
for data in test_data:
cursor.execute(
"INSERT INTO test_data (name, value, create_time, update_time) VALUES (?, ?, ?, ?)",
data
)
conn.commit()
print("测试数据插入成功")
# 关闭数据库连接
close_db_connection(conn)
return True, "数据库更新成功"
except Exception as e:
# 确保在异常情况下也关闭连接
if 'conn' in locals() and conn:
close_db_connection(conn)
return False, f"数据库更新失败:{str(e)}"
# 如果直接运行此脚本,则执行更新操作
if __name__ == "__main__": if __name__ == "__main__":
update_users_table() success, message = update_database()
print(message)
+200 -247
View File
@@ -1,13 +1,15 @@
import sqlite3
from datetime import datetime from datetime import datetime
from flask import Blueprint, render_template, request, jsonify, redirect, url_for, session from flask import Blueprint, render_template, request, jsonify, redirect, url_for, session
# 导入数据库工具
from db_utils import get_db_connection, close_db_connection
# 创建用户相关的蓝图 # 创建用户相关的蓝图
bp = Blueprint('user', __name__) bp = Blueprint('user', __name__)
# 初始化数据库(用户相关部分) # 初始化数据库(用户相关部分)
def init_user_db(): def init_user_db():
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
# 创建用户表 # 创建用户表
@@ -48,8 +50,12 @@ def init_user_db():
user user
) )
conn.commit() try:
conn.close() conn.commit()
except Exception as e:
print(f"初始化用户数据库时出错: {e}")
finally:
close_db_connection(conn)
# 登录路由 # 登录路由
@bp.route('/login', methods=['GET', 'POST']) @bp.route('/login', methods=['GET', 'POST'])
@@ -59,7 +65,7 @@ def login():
password = request.form.get('password') password = request.form.get('password')
# 连接数据库 # 连接数据库
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
# 验证用户名和密码 # 验证用户名和密码
@@ -69,7 +75,7 @@ def login():
if user: if user:
# 检查用户状态 # 检查用户状态
if user[6] != 'active': if user[6] != 'active':
conn.close() close_db_connection(conn)
return render_template('login.html', error='用户已被禁用') return render_template('login.html', error='用户已被禁用')
# 设置会话信息 # 设置会话信息
@@ -78,14 +84,12 @@ def login():
session['department'] = user[5] # 部门 session['department'] = user[5] # 部门
session['login_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') session['login_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
close_db_connection(conn)
conn.close()
# 登录成功,跳转到查询页面 # 登录成功,跳转到查询页面
return redirect(url_for('query')) return redirect(url_for('query'))
else: else:
conn.close() close_db_connection(conn)
return render_template('login.html', error='用户名或密码错误') return render_template('login.html', error='用户名或密码错误')
return render_template('login.html') return render_template('login.html')
@@ -114,72 +118,31 @@ def register():
return render_template('register.html', error='密码长度至少8位,且必须包含字母和数字') return render_template('register.html', error='密码长度至少8位,且必须包含字母和数字')
# 连接数据库,检查用户名是否已存在 # 连接数据库,检查用户名是否已存在
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE username = ?", (username,)) cursor.execute("SELECT * FROM users WHERE username = ?", (username,))
if cursor.fetchone(): if cursor.fetchone():
conn.close() close_db_connection(conn)
return render_template('register.html', error='用户名已存在') return render_template('register.html', error='用户名已存在')
try: try:
# 插入新用户
cursor.execute( cursor.execute(
"INSERT INTO users (username, password, name, phone, department) VALUES (?, ?, ?, ?, ?)", "INSERT INTO users (username, password, name, phone, department) VALUES (?, ?, ?, ?, ?)",
(username, password, name, phone, department) (username, password, name, phone, department)
) )
conn.commit() conn.commit()
conn.close() close_db_connection(conn)
# 注册成功,跳转到登录页并显示成功消息
return render_template('login.html', success='注册成功,请登录') # 注册成功,跳转到登录页面
return redirect(url_for('user.login', success='注册成功,请登录'))
except Exception as e: except Exception as e:
conn.close() close_db_connection(conn)
return render_template('register.html', error=f'注册失败,请重试。错误{str(e)}') return render_template('register.html', error=f'注册失败:{str(e)}')
return render_template('register.html') return render_template('register.html')
# 修改密码路由
@bp.route('/change_password', methods=['POST'])
def change_password():
if 'username' not in session:
return jsonify({'success': False, 'error': '未登录'}), 401
old_password = request.form.get('old_password')
new_password = request.form.get('new_password')
confirm_password = request.form.get('confirm_password')
# 验证参数
if not old_password or not new_password or not confirm_password:
return jsonify({'success': False, 'error': '请填写所有必填字段'})
# 验证新密码和确认密码是否一致
if new_password != confirm_password:
return jsonify({'success': False, 'error': '两次输入的新密码不一致'})
# 验证新密码复杂度
if len(new_password) < 8 or not any(c.isalpha() for c in new_password) or not any(c.isdigit() for c in new_password):
return jsonify({'success': False, 'error': '新密码长度至少8位,且必须包含字母和数字'})
# 连接数据库,验证旧密码并更新新密码
conn = sqlite3.connect('car_info.db')
cursor = conn.cursor()
# 验证旧密码是否正确
cursor.execute("SELECT password FROM users WHERE username = ?", (session['username'],))
result = cursor.fetchone()
if not result or result[0] != old_password:
conn.close()
return jsonify({'success': False, 'error': '旧密码不正确'})
# 更新密码
cursor.execute("UPDATE users SET password = ? WHERE username = ?", (new_password, session['username']))
conn.commit()
conn.close()
return jsonify({'success': True, 'message': '修改密码成功,请重新登录'})
# 获取个人信息路由 # 获取个人信息路由
@bp.route('/get_user_profile', methods=['GET']) @bp.route('/get_user_profile', methods=['GET'])
def get_user_profile(): def get_user_profile():
@@ -187,12 +150,12 @@ def get_user_profile():
return jsonify({'success': False, 'error': '未登录'}), 401 return jsonify({'success': False, 'error': '未登录'}), 401
# 连接数据库,获取用户信息 # 连接数据库,获取用户信息
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute("SELECT username, name, phone, department FROM users WHERE username = ?", (session['username'],)) cursor.execute("SELECT username, name, phone, department FROM users WHERE username = ?", (session['username'],))
user = cursor.fetchone() user = cursor.fetchone()
conn.close() close_db_connection(conn)
if not user: if not user:
return jsonify({'success': False, 'error': '用户不存在'}) return jsonify({'success': False, 'error': '用户不存在'})
@@ -207,81 +170,8 @@ def get_user_profile():
return jsonify({'success': True, 'user': user_data}) return jsonify({'success': True, 'user': user_data})
# 更新个人信息路由 # 获取所有用户列表的API
@bp.route('/update_user_profile', methods=['POST']) @bp.route('/api/users', methods=['GET'])
def update_user_profile():
if 'username' not in session:
return jsonify({'success': False, 'error': '未登录'}), 401
name = request.form.get('name', '').strip()
phone = request.form.get('phone', '').strip()
department = request.form.get('department', '').strip()
# 验证参数
if not name or not phone or not department:
return jsonify({'success': False, 'error': '请填写所有必填字段'})
# 连接数据库,更新用户信息
conn = sqlite3.connect('car_info.db')
cursor = conn.cursor()
try:
cursor.execute(
"UPDATE users SET name = ?, phone = ?, department = ? WHERE username = ?",
(name, phone, department, session['username'])
)
conn.commit()
# 获取更新后的用户信息
cursor.execute("SELECT username, name, phone, department FROM users WHERE username = ?", (session['username'],))
updated_user = cursor.fetchone()
conn.close()
# 更新会话中的部门信息
session['department'] = department
# 构造返回数据
user_data = {
'username': updated_user[0],
'name': updated_user[1],
'phone': updated_user[2],
'department': updated_user[3]
}
return jsonify({'success': True, 'message': '个人信息更新成功', 'user': user_data})
except Exception as e:
conn.close()
return jsonify({'success': False, 'error': f'更新失败,请重试。错误:{str(e)}'})
# 登出路由
@bp.route('/logout')
def logout():
session.clear()
return redirect(url_for('user.login'))
# 用户管理页面路由
@bp.route('/users_management')
def users_management():
if 'username' not in session:
return redirect(url_for('user.login'))
# 只有admin用户才能访问用户管理页面
if session['username'] != 'admin':
return redirect(url_for('query'))
# 获取所有部门列表(用于筛选和修改)
conn = sqlite3.connect('car_info.db')
cursor = conn.cursor()
cursor.execute("SELECT DISTINCT department FROM users")
departments = [dept[0] for dept in cursor.fetchall()]
conn.close()
return render_template('users_management.html', departments=departments)
# 获取用户列表路由
@bp.route('/get_users', methods=['GET'])
def get_users(): def get_users():
if 'username' not in session: if 'username' not in session:
return jsonify({'success': False, 'error': '未登录'}), 401 return jsonify({'success': False, 'error': '未登录'}), 401
@@ -290,26 +180,86 @@ def get_users():
if session['username'] != 'admin': if session['username'] != 'admin':
return jsonify({'success': False, 'error': '无权限访问'}), 403 return jsonify({'success': False, 'error': '无权限访问'}), 403
conn = sqlite3.connect('car_info.db') # 获取URL参数中的页码、每页数量、排序字段和排序方式
cursor = conn.cursor() page = request.args.get('page', 1, type=int)
page_size = request.args.get('pageSize', 10, type=int)
sort_field = request.args.get('sortField', 'id')
sort_order = request.args.get('sortOrder', 'asc')
department = request.args.get('department', '')
search = request.args.get('search', '')
# 获取所有用户信息 # 验证排序字段是否有效
cursor.execute("SELECT id, username, name, department, status FROM users") valid_sort_fields = ['id', 'username', 'name', 'phone', 'department', 'status']
users = cursor.fetchall() if sort_field not in valid_sort_fields:
conn.close() sort_field = 'id'
# 格式化用户数据 # 验证排序方式是否有效
formatted_users = [] if sort_order not in ['asc', 'desc']:
for user in users: sort_order = 'asc'
formatted_users.append({
'id': user[0], # 计算偏移量
'username': user[1], offset = (page - 1) * page_size
'name': user[2],
'department': user[3], try:
'status': user[4] # 连接数据库
conn = get_db_connection()
cursor = conn.cursor()
# 构建查询条件
conditions = []
params = []
if department:
conditions.append("department = ?")
params.append(department)
if search:
conditions.append("(username LIKE ? OR name LIKE ? OR phone LIKE ?)")
params.extend([f'%{search}%', f'%{search}%', f'%{search}%'])
# 构建SQL查询语句
where_clause = " AND ".join(conditions) if conditions else ""
if where_clause:
where_clause = "WHERE " + where_clause
# 查询用户列表
cursor.execute(
f"SELECT id, username, name, phone, department, status FROM users {where_clause} ORDER BY {sort_field} {sort_order} LIMIT ? OFFSET ?",
params + [page_size, offset]
)
users = cursor.fetchall()
# 查询总记录数
cursor.execute(f"SELECT COUNT(*) FROM users {where_clause}", params)
total = cursor.fetchone()[0]
# 关闭数据库连接
close_db_connection(conn)
# 格式化用户数据
formatted_users = []
for user in users:
formatted_users.append({
'id': user[0],
'username': user[1],
'name': user[2],
'phone': user[3],
'department': user[4],
'status': user[5]
})
# 返回分页结果
return jsonify({
'success': True,
'data': formatted_users,
'total': total,
'page': page,
'pageSize': page_size
}) })
except Exception as e:
return jsonify({'success': True, 'users': formatted_users}) # 确保在异常情况下也关闭连接
close_db_connection(conn)
return jsonify({'success': False, 'error': str(e)}), 500
# 更新用户状态路由 # 更新用户状态路由
@bp.route('/update_user_status', methods=['POST']) @bp.route('/update_user_status', methods=['POST'])
@@ -329,29 +279,21 @@ def update_user_status():
if not user_id or not status: if not user_id or not status:
return jsonify({'success': False, 'error': '缺少必要参数'}) return jsonify({'success': False, 'error': '缺少必要参数'})
# 验证状态值
if status not in ['active', 'inactive']:
return jsonify({'success': False, 'error': '状态值无效'})
# 不能禁用admin用户 # 不能禁用admin用户
conn = sqlite3.connect('car_info.db') if user_id == 1:
cursor = conn.cursor() return jsonify({'success': False, 'error': '不能修改管理员用户的状态'})
cursor.execute("SELECT username FROM users WHERE id = ?", (user_id,))
user = cursor.fetchone()
if user and user[0] == 'admin' and status == 'inactive':
conn.close()
return jsonify({'success': False, 'error': '不能禁用管理员用户'})
# 更新用户状态 # 更新用户状态
try: try:
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("UPDATE users SET status = ? WHERE id = ?", (status, user_id)) cursor.execute("UPDATE users SET status = ? WHERE id = ?", (status, user_id))
conn.commit() conn.commit()
conn.close() close_db_connection(conn)
return jsonify({'success': True, 'message': '用户状态更新成功'}) return jsonify({'success': True, 'message': '用户状态更新成功'})
except Exception as e: except Exception as e:
conn.close() close_db_connection(conn)
return jsonify({'success': False, 'error': f'更新失败:{str(e)}'}) return jsonify({'success': False, 'error': f'更新失败:{str(e)}'})
# 更新用户部门路由 # 更新用户部门路由
@@ -374,15 +316,15 @@ def update_user_department():
# 更新用户部门 # 更新用户部门
try: try:
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute("UPDATE users SET department = ? WHERE id = ?", (department, user_id)) cursor.execute("UPDATE users SET department = ? WHERE id = ?", (department, user_id))
conn.commit() conn.commit()
conn.close() close_db_connection(conn)
return jsonify({'success': True, 'message': '部门信息更新成功'}) return jsonify({'success': True, 'message': '部门信息更新成功'})
except Exception as e: except Exception as e:
conn.close() close_db_connection(conn)
return jsonify({'success': False, 'error': f'更新失败:{str(e)}'}) return jsonify({'success': False, 'error': f'更新失败:{str(e)}'})
# 重置用户密码路由 # 重置用户密码路由
@@ -407,7 +349,7 @@ def reset_user_password():
default_password = 'default123' default_password = 'default123'
try: try:
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
# 检查用户ID和用户名是否匹配 # 检查用户ID和用户名是否匹配
@@ -415,26 +357,48 @@ def reset_user_password():
db_user_id = cursor.fetchone() db_user_id = cursor.fetchone()
if not db_user_id or db_user_id[0] != user_id: if not db_user_id or db_user_id[0] != user_id:
conn.close() close_db_connection(conn)
return jsonify({'success': False, 'error': '用户信息不匹配'}) return jsonify({'success': False, 'error': '用户信息不匹配'})
# 更新users表中的密码 # 更新users表中的密码
cursor.execute("UPDATE users SET password = ? WHERE id = ?", (default_password, user_id)) cursor.execute("UPDATE users SET password = ? WHERE id = ?", (default_password, user_id))
conn.commit() conn.commit()
conn.close() close_db_connection(conn)
return jsonify({'success': True, 'message': '密码已初始化为:' + default_password}) return jsonify({'success': True, 'message': '密码已初始化为:' + default_password})
except Exception as e: except Exception as e:
conn.close() close_db_connection(conn)
return jsonify({'success': False, 'error': f'密码重置失败:{str(e)}'}) return jsonify({'success': False, 'error': f'密码重置失败:{str(e)}'})
# 登出路由
@bp.route('/logout')
def logout():
session.clear()
return redirect(url_for('user.login'))
# 用户管理页面路由
@bp.route('/users_management')
def users_management():
if 'username' not in session:
return redirect(url_for('user.login'))
# 只有admin用户才能访问用户管理页面
if session['username'] != 'admin':
return redirect(url_for('query'))
# 获取所有部门列表(用于筛选和修改)
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SELECT DISTINCT department FROM users")
departments = [dept[0] for dept in cursor.fetchall()]
close_db_connection(conn)
return render_template('users_management.html', departments=departments)
# 创建参数配置表(如果不存在) # 创建参数配置表(如果不存在)
def create_parameter_config_table(): def create_parameter_config_table():
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
# 创建参数配置表 # 创建参数配置表
@@ -459,10 +423,12 @@ def create_parameter_config_table():
"INSERT INTO parameter_config (param1, param2, param3, param4, param5, update_time) VALUES (?, ?, ?, ?, ?, ?)", "INSERT INTO parameter_config (param1, param2, param3, param4, param5, update_time) VALUES (?, ?, ?, ?, ?, ?)",
('默认参数1', '默认参数2', '默认参数3', '默认参数4', '默认参数5', datetime.now().strftime('%Y-%m-%d %H:%M:%S')) ('默认参数1', '默认参数2', '默认参数3', '默认参数4', '默认参数5', datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
) )
conn.commit() try:
conn.commit()
except Exception as e:
print(f"插入参数配置数据时出错: {e}")
conn.close() close_db_connection(conn)
# 获取参数配置路由 # 获取参数配置路由
@bp.route('/get_parameter_config', methods=['GET']) @bp.route('/get_parameter_config', methods=['GET'])
@@ -470,32 +436,38 @@ def get_parameter_config():
if 'username' not in session: if 'username' not in session:
return jsonify({'success': False, 'error': '未登录'}), 401 return jsonify({'success': False, 'error': '未登录'}), 401
# 确保参数配置表存在
create_parameter_config_table()
try: try:
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
# 查询参数配置 # 获取最新的参数配置
cursor.execute("SELECT param1, param2, param3, param4, param5, update_time FROM parameter_config ORDER BY update_time DESC LIMIT 1") cursor.execute("SELECT param1, param2, param3, param4, param5 FROM parameter_config ORDER BY update_time DESC LIMIT 1")
config = cursor.fetchone() config = cursor.fetchone()
conn.close() close_db_connection(conn)
if not config: if not config:
return jsonify({'success': False, 'error': '未找到参数配置'}) return jsonify({'success': False, 'error': '未找到参数配置'})
# 构造返回数据 # 构建符合前端期望格式的响应
parameters = { parameters = {
'param1': config[0], 'param1': config[0],
'param2': config[1], 'param2': config[1],
'param3': config[2], 'param3': config[2],
'param4': config[3], 'param4': config[3],
'param5': config[4], 'param5': config[4],
'update_time': config[5] 'update_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
} }
return jsonify({'success': True, 'parameters': parameters}) return jsonify({
'success': True,
'parameters': parameters
})
except Exception as e: except Exception as e:
return jsonify({'success': False, 'error': f'获取参数配置失败:{str(e)}'}) close_db_connection(conn)
return jsonify({'success': False, 'error': str(e)})
# 保存参数配置路由 # 保存参数配置路由
@bp.route('/save_parameter_config', methods=['POST']) @bp.route('/save_parameter_config', methods=['POST'])
@@ -503,64 +475,40 @@ def save_parameter_config():
if 'username' not in session: if 'username' not in session:
return jsonify({'success': False, 'error': '未登录'}), 401 return jsonify({'success': False, 'error': '未登录'}), 401
# 只有管理员可以修改参数配置 # 确保参数配置表存在
if session['username'] != 'admin': create_parameter_config_table()
return jsonify({'success': False, 'error': '无权限修改参数配置'}), 403
# 获取请求数据
data = request.get_json() data = request.get_json()
if not data: param1 = data.get('param1')
return jsonify({'success': False, 'error': '请求数据格式错误'}), 400 param2 = data.get('param2')
param3 = data.get('param3')
param1 = data.get('param1', '').strip() param4 = data.get('param4')
param2 = data.get('param2', '').strip() param5 = data.get('param5')
param3 = data.get('param3', '').strip()
param4 = data.get('param4', '').strip()
param5 = data.get('param5', '').strip()
# 验证参数
if not param1:
return jsonify({'success': False, 'error': '参数1不能为空'})
try: try:
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
# 更新参数配置 # 插入新的参数配置记录(不更新旧记录,而是插入新记录)
cursor.execute( cursor.execute(
"UPDATE parameter_config SET param1 = ?, param2 = ?, param3 = ?, param4 = ?, param5 = ?, update_time = ? WHERE id = 1", "INSERT INTO parameter_config (param1, param2, param3, param4, param5, update_time) VALUES (?, ?, ?, ?, ?, ?)",
(param1, param2, param3, param4, param5, datetime.now().strftime('%Y-%m-%d %H:%M:%S')) (param1, param2, param3, param4, param5, datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
) )
# 如果没有更新任何记录,插入新记录
if cursor.rowcount == 0:
cursor.execute(
"INSERT INTO parameter_config (param1, param2, param3, param4, param5, update_time) VALUES (?, ?, ?, ?, ?, ?)",
(param1, param2, param3, param4, param5, datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
)
conn.commit() conn.commit()
conn.close() close_db_connection(conn)
return jsonify({'success': True, 'message': '参数配置保存成功'}) return jsonify({'success': True, 'message': '参数配置保存成功'})
except Exception as e: except Exception as e:
return jsonify({'success': False, 'error': f'保存参数配置失败:{str(e)}'}) close_db_connection(conn)
return jsonify({'success': False, 'error': str(e)})
# 参数配置页面路由 # 参数配置页面路由
@bp.route('/parameter_config_page', methods=['GET', 'POST']) @bp.route('/parameter_config_page')
def parameter_config_page(): def parameter_config_page():
if 'username' not in session: if 'username' not in session:
return redirect(url_for('user.login')) return redirect(url_for('user.login'))
# 只有管理员可以访问参数配置页面 return render_template('parameter_config.html')
if session['username'] != 'admin':
return redirect(url_for('user.user_profile_page', error='无权限访问参数配置页面'))
# 渲染参数配置页面
return render_template('parameter_config.html', current_year=datetime.now().year)
# 修改密码页面路由 # 修改密码页面路由
@bp.route('/change_password_page', methods=['GET', 'POST']) @bp.route('/change_password_page', methods=['GET', 'POST'])
@@ -586,7 +534,7 @@ def change_password_page():
return render_template('change_password.html', error='新密码长度至少8位,且必须包含字母和数字') return render_template('change_password.html', error='新密码长度至少8位,且必须包含字母和数字')
# 连接数据库,验证旧密码并更新新密码 # 连接数据库,验证旧密码并更新新密码
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
# 验证旧密码是否正确 # 验证旧密码是否正确
@@ -594,20 +542,25 @@ def change_password_page():
result = cursor.fetchone() result = cursor.fetchone()
if not result or result[0] != old_password: if not result or result[0] != old_password:
conn.close() close_db_connection(conn)
return render_template('change_password.html', error='旧密码不正确') return render_template('change_password.html', error='旧密码不正确')
# 更新密码 # 更新密码
cursor.execute("UPDATE users SET password = ? WHERE username = ?", (new_password, session['username'])) try:
conn.commit() cursor.execute(
conn.close() "UPDATE users SET password = ? WHERE username = ?",
(new_password, session['username'])
# 密码修改成功,先执行退出功能,再跳转到登录页面 )
session.clear() conn.commit()
return redirect(url_for('user.login', success='修改密码成功,请重新登录')) close_db_connection(conn)
# 密码修改成功,跳转到登录页面
return redirect(url_for('user.login', success='密码修改成功,请重新登录'))
except Exception as e:
close_db_connection(conn)
return render_template('change_password.html', error=f'密码修改失败:{str(e)}')
# GET请求,渲染修改密码页面 return render_template('change_password.html')
return render_template('change_password.html', current_year=datetime.now().year)
# 个人信息页面路由 # 个人信息页面路由
@bp.route('/user_profile_page', methods=['GET', 'POST']) @bp.route('/user_profile_page', methods=['GET', 'POST'])
@@ -616,11 +569,11 @@ def user_profile_page():
return redirect(url_for('user.login')) return redirect(url_for('user.login'))
# 连接数据库,获取用户信息 # 连接数据库,获取用户信息
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute("SELECT username, name, phone, department FROM users WHERE username = ?", (session['username'],)) cursor.execute("SELECT username, name, phone, department FROM users WHERE username = ?", (session['username'],))
user_tuple = cursor.fetchone() user_tuple = cursor.fetchone()
conn.close() close_db_connection(conn)
if not user_tuple: if not user_tuple:
return redirect(url_for('user.login', error='用户不存在')) return redirect(url_for('user.login', error='用户不存在'))
@@ -643,7 +596,7 @@ def user_profile_page():
return render_template('user_profile.html', user=user, error='请填写所有必填字段', current_year=datetime.now().year) return render_template('user_profile.html', user=user, error='请填写所有必填字段', current_year=datetime.now().year)
# 连接数据库,更新用户信息 # 连接数据库,更新用户信息
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
try: try:
@@ -657,14 +610,14 @@ def user_profile_page():
session['department'] = department session['department'] = department
session['name'] = name session['name'] = name
conn.close() close_db_connection(conn)
# 重新获取用户信息 # 重新获取用户信息
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute("SELECT username, name, phone, department FROM users WHERE username = ?", (session['username'],)) cursor.execute("SELECT username, name, phone, department FROM users WHERE username = ?", (session['username'],))
updated_user_tuple = cursor.fetchone() updated_user_tuple = cursor.fetchone()
conn.close() close_db_connection(conn)
# 将元组转换为字典 # 将元组转换为字典
updated_user = { updated_user = {
@@ -676,7 +629,7 @@ def user_profile_page():
return render_template('user_profile.html', user=updated_user, success='个人信息更新成功', current_year=datetime.now().year) return render_template('user_profile.html', user=updated_user, success='个人信息更新成功', current_year=datetime.now().year)
except Exception as e: except Exception as e:
conn.close() close_db_connection(conn)
return render_template('user_profile.html', user=user, error=f'更新失败,请重试。错误:{str(e)}', current_year=datetime.now().year) return render_template('user_profile.html', user=user, error=f'更新失败,请重试。错误:{str(e)}', current_year=datetime.now().year)
# GET请求,渲染个人信息页面 # GET请求,渲染个人信息页面
+57 -29
View File
@@ -1,41 +1,69 @@
import sqlite3 # 导入数据库工具
from db_utils import get_db_connection, close_db_connection
# 连接到数据库并验证users表结构 # 验证数据库结构是否完整
def verify_users_table(): def verify_database_structure():
try: try:
# 连接数据库 # 连接数据库
conn = sqlite3.connect('car_info.db') conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
# 检查users表的当前结构 # 需要检查的表
print("验证users表结构:") tables_to_check = ['users', 'query_records']
cursor.execute("PRAGMA table_info(users)") verification_results = []
columns = cursor.fetchall()
for column in columns:
print(f"字段名: {column[1]}, 类型: {column[2]}, 是否可空: {column[3]}")
# 检查admin用户的信息 for table in tables_to_check:
print("\n验证admin用户信息:") # 检查表是否存在
cursor.execute("SELECT id, username, name, phone, department FROM users WHERE username = ?", ('admin',)) cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table,))
admin_user = cursor.fetchone() table_exists = cursor.fetchone() is not None
if admin_user:
print(f"ID: {admin_user[0]}") if table_exists:
print(f"用户名: {admin_user[1]}") # 检查表的列结构
print(f"姓名: {admin_user[2]}") cursor.execute(f"PRAGMA table_info({table})")
print(f"电话: {admin_user[3]}") columns = cursor.fetchall()
print(f"部门: {admin_user[4]}") column_names = [col[1] for col in columns]
else:
print("未找到admin用户") verification_results.append({
"table": table,
"exists": True,
"column_count": len(columns),
"columns": column_names
})
else:
verification_results.append({
"table": table,
"exists": False,
"column_count": 0,
"columns": []
})
# 关闭连接 # 关闭数据库连接
conn.close() close_db_connection(conn)
print("\n数据库验证完成")
# 构造返回结果
all_tables_exist = all(result['exists'] for result in verification_results)
return {
"success": all_tables_exist,
"results": verification_results
}
except Exception as e: except Exception as e:
print(f"验证数据库时出错: {str(e)}")
# 确保在异常情况下也关闭连接 # 确保在异常情况下也关闭连接
if 'conn' in locals(): if 'conn' in locals() and conn:
conn.close() close_db_connection(conn)
return {
"success": False,
"error": str(e)
}
# 如果直接运行此脚本,则执行验证操作
if __name__ == "__main__": if __name__ == "__main__":
verify_users_table() result = verify_database_structure()
print(f"数据库结构验证: {'通过' if result['success'] else '失败'}")
if result['success']:
print("验证详情:")
for table_result in result['results']:
print(f" - 表 {table_result['table']}: 存在,包含 {table_result['column_count']} 个字段")
else:
print(f"错误信息: {result['error']}")