This commit is contained in:
2025-09-18 17:17:35 +08:00
parent 8de14edf9d
commit 01503d6862
11 changed files with 1378 additions and 1504 deletions
BIN
View File
Binary file not shown.
+31
View File
@@ -0,0 +1,31 @@
import sqlite3
# 连接数据库
conn = sqlite3.connect('car_info.db')
cursor = conn.cursor()
# 查询所有表
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
tables = cursor.fetchall()
print('数据库中的表:', tables)
# 查询参数配置表
cursor.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name='parameter_config'")
result = cursor.fetchone()
print('参数配置表SQL:', result)
# 如果参数配置表存在,查询其结构
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()
+179
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>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Font Awesome -->
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<!-- 配置Tailwind自定义颜色和字体 -->
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#2563eb',
secondary: '#f3f4f6',
'primary-light': '#dbeafe',
'primary-dark': '#1d4ed8',
'table-header': '#f9fafb',
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
}
}
}
</script>
<!-- 自定义工具类 -->
<style type="text/tailwindcss">
@layer utilities {
.content-auto {
content-visibility: auto;
}
.card-shadow {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
}
.input-focus {
@apply focus:border-primary focus:ring-2 focus:ring-primary/20 focus:outline-none;
}
.animate-fade-in {
animation: fadeIn 0.3s ease-out forwards;
}
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
</head>
<body class="bg-secondary min-h-screen font-sans">
{% include 'header.html' %}
<!-- 主要内容区域 -->
<main class="container mx-auto px-4 pt-24 pb-12">
<!-- 返回按钮 -->
<div class="mb-6 flex justify-end">
<a href="{{ url_for('query') }}" class="inline-flex items-center text-primary hover:text-primary-dark transition-colors">
<i class="fa fa-arrow-left mr-2"></i> 返回主页面
</a>
</div>
<!-- 修改密码表单 -->
<div class="max-w-md mx-auto bg-white rounded-lg shadow-xl p-8 animate-fade-in">
<div class="text-center mb-6">
<div class="w-16 h-16 bg-primary-light rounded-full flex items-center justify-center mx-auto mb-4">
<i class="fa fa-key text-primary text-2xl"></i>
</div>
<h2 class="text-2xl font-bold text-gray-800">修改密码</h2>
<p class="text-gray-500 mt-2">请填写以下信息以修改您的密码</p>
</div>
<!-- 错误信息显示区域 -->
{% if error %}
<div class="mb-4 bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-md flex items-start animate-fade-in">
<i class="fa fa-exclamation-circle mt-1 mr-3"></i>
<span>{{ error }}</span>
</div>
{% endif %}
<!-- 表单 -->
<form id="changePasswordForm" method="POST" class="space-y-4">
<div class="mb-4">
<label for="old_password" class="block text-gray-700 text-sm font-medium mb-2">旧密码</label>
<div class="relative">
<input type="password" id="old_password" name="old_password" class="w-full px-4 py-2 border border-gray-300 rounded-md input-focus transition-colors" placeholder="请输入旧密码" required>
<button type="button" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700 transition-colors" onclick="togglePasswordVisibility('old_password', this)">
<i class="fa fa-eye"></i>
</button>
</div>
</div>
<div class="mb-4">
<label for="new_password" class="block text-gray-700 text-sm font-medium mb-2">新密码</label>
<div class="relative">
<input type="password" id="new_password" name="new_password" class="w-full px-4 py-2 border border-gray-300 rounded-md input-focus transition-colors" placeholder="请输入新密码" required>
<button type="button" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700 transition-colors" onclick="togglePasswordVisibility('new_password', this)">
<i class="fa fa-eye"></i>
</button>
</div>
<p class="text-xs text-gray-500 mt-1">密码长度至少8位,包含字母和数字</p>
</div>
<div class="mb-6">
<label for="confirm_password" class="block text-gray-700 text-sm font-medium mb-2">确认新密码</label>
<div class="relative">
<input type="password" id="confirm_password" name="confirm_password" class="w-full px-4 py-2 border border-gray-300 rounded-md input-focus transition-colors" placeholder="请再次输入新密码" required>
<button type="button" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700 transition-colors" onclick="togglePasswordVisibility('confirm_password', this)">
<i class="fa fa-eye"></i>
</button>
</div>
</div>
<div class="flex space-x-3">
<button type="submit" class="flex-1 bg-primary hover:bg-primary-dark text-white py-2 px-4 rounded-md transition-colors shadow-sm hover:shadow">
确认修改
</button>
<button type="button" onclick="window.location.href='{{ url_for('query') }}'" class="flex-1 border border-gray-300 text-gray-700 hover:bg-gray-50 py-2 px-4 rounded-md transition-colors">
取消
</button>
</div>
</form>
</div>
</main>
{% include 'footer.html' %}
<!-- JavaScript -->
<script>
// 密码可见性切换
function togglePasswordVisibility(inputId, button) {
const input = document.getElementById(inputId);
const icon = button.querySelector('i');
if (input.type === 'password') {
input.type = 'text';
icon.classList.remove('fa-eye');
icon.classList.add('fa-eye-slash');
} else {
input.type = 'password';
icon.classList.remove('fa-eye-slash');
icon.classList.add('fa-eye');
}
}
// 表单提交处理
document.getElementById('changePasswordForm').addEventListener('submit', function(e) {
e.preventDefault(); // 阻止默认提交
const newPassword = document.getElementById('new_password').value;
const confirmPassword = document.getElementById('confirm_password').value;
const oldPassword = document.getElementById('old_password').value;
// 前端验证
if (!oldPassword) {
alert('请输入旧密码');
return;
}
if (newPassword.length < 8 || !/[a-zA-Z]/.test(newPassword) || !/[0-9]/.test(newPassword)) {
alert('新密码长度至少8位,且必须包含字母和数字');
return;
}
if (newPassword !== confirmPassword) {
alert('两次输入的新密码不一致');
return;
}
// 提交表单
this.submit();
});
</script>
</body>
</html>
+7
View File
@@ -0,0 +1,7 @@
<!-- 页脚 -->
<footer class="bg-white py-4 border-t border-gray-200">
<div class="container mx-auto px-4 text-center text-gray-500 text-sm">
<p>© {{ current_year }} 车主信息查询系统 - 版权所有</p>
<p>免责声明:本系统为内部测试使用,仅供学习和研究。</p>
</div>
</footer>
+42
View File
@@ -0,0 +1,42 @@
<!-- 顶部导航栏 -->
<header class="bg-white shadow-sm fixed top-0 left-0 right-0 z-10">
<div class="container mx-auto px-4 py-3 flex justify-between items-center">
<div class="flex items-center space-x-2">
<i class="fa fa-car text-primary text-2xl"></i>
<h1 class="text-xl font-bold text-gray-800">车主信息查询系统</h1>
</div>
<div class="flex items-center space-x-4">
<!-- 用户下拉菜单 -->
<div class="relative group">
<div class="flex items-center cursor-pointer hover:text-primary transition-colors">
<span class="text-gray-600">欢迎 <strong>{{ session.department }}</strong> <strong>{{ session.name }}</strong></span>
<i class="fa fa-chevron-down ml-2 text-xs transition-transform duration-300 group-hover:rotate-180"></i>
</div>
<!-- 下拉菜单 -->
<div class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-300 transform origin-top-right scale-95 group-hover:scale-100 z-20">
<div class="py-1">
<a href="{{ url_for('user.user_profile_page') }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-primary hover:text-white transition-colors">
<i class="fa fa-user mr-2"></i> 个人信息
</a>
<a href="{{ url_for('user.change_password_page') }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-primary hover:text-white transition-colors">
<i class="fa fa-lock mr-2"></i> 修改密码
</a>
<a href="{{ url_for('user.parameter_config_page') }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-primary hover:text-white transition-colors">
<i class="fa fa-cog mr-2"></i> 参数配置
</a>
{% if session.username == 'admin' %}
<a href="{{ url_for('user.users_management') }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-primary hover:text-white transition-colors">
<i class="fa fa-users mr-2"></i> 用户管理
</a>
{% endif %}
<div class="border-t border-gray-200 my-1"></div>
<a href="{{ url_for('user.logout') }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-primary hover:text-white transition-colors">
<i class="fa fa-sign-out mr-2"></i> 退出
</a>
</div>
</div>
</div>
</div>
</div>
</header>
+214
View File
@@ -0,0 +1,214 @@
// 通用工具函数
/**
* 显示模态框
* @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);
}
};
}
+271
View File
@@ -0,0 +1,271 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>参数配置 - 车主信息查询系统</title>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Font Awesome -->
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<!-- 配置Tailwind自定义颜色和字体 -->
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#2563eb',
secondary: '#f3f4f6',
'primary-light': '#dbeafe',
'primary-dark': '#1d4ed8',
'table-header': '#f9fafb',
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
}
}
}
</script>
<!-- 自定义工具类 -->
<style type="text/tailwindcss">
@layer utilities {
.content-auto {
content-visibility: auto;
}
.card-shadow {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
}
.input-focus {
@apply focus:border-primary focus:ring-2 focus:ring-primary/20 focus:outline-none;
}
.animate-fade-in {
animation: fadeIn 0.3s ease-out forwards;
}
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
</head>
<body class="bg-secondary min-h-screen font-sans">
{% include 'header.html' %}
<!-- 主要内容区域 -->
<main class="container mx-auto px-4 pt-24 pb-12">
<!-- 返回按钮 -->
<div class="mb-6 flex justify-end">
<a href="{{ url_for('query') }}" class="inline-flex items-center text-primary hover:text-primary-dark transition-colors">
<i class="fa fa-arrow-left mr-2"></i> 返回主页面
</a>
</div>
<!-- 个人信息表单 -->
<div class="max-w-md mx-auto bg-white rounded-lg shadow-xl p-8 animate-fade-in">
<div class="text-center mb-6">
<div class="w-16 h-16 bg-primary-light rounded-full flex items-center justify-center mx-auto mb-4">
<i class="fa fa-cog text-primary text-2xl"></i>
</div>
<h2 class="text-2xl font-bold text-gray-800">参数配置</h2>
<p class="text-gray-500 mt-2">请配置系统参数</p>
</div>
<!-- 消息提示区域 -->
{% if error %}
<div class="mb-4 bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-md flex items-start animate-fade-in">
<i class="fa fa-exclamation-circle mt-1 mr-3"></i>
<span>{{ error }}</span>
</div>
{% endif %}
{% if success %}
<div class="mb-4 bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-md flex items-start animate-fade-in">
<i class="fa fa-check-circle mt-1 mr-3"></i>
<span>{{ success }}</span>
</div>
{% endif %}
<div class="mb-6 bg-blue-50 border border-blue-100 text-blue-700 px-4 py-3 rounded-md flex items-center" id="updateTimeAlert" style="display: none;">
<i class="fa fa-info-circle mr-2"></i>
<span id="updateTimeContent"></span>
</div>
<div class="space-y-4">
<div class="mb-4">
<label for="param1" class="block text-gray-700 text-sm font-medium mb-2">参数1 <span class="text-red-500">*</span></label>
<input type="text" id="param1" class="w-full px-4 py-2 border border-gray-300 rounded-md input-focus transition-colors" placeholder="请输入参数1的值">
</div>
<div class="mb-4">
<label for="param2" class="block text-gray-700 text-sm font-medium mb-2">参数2</label>
<input type="text" id="param2" class="w-full px-4 py-2 border border-gray-300 rounded-md input-focus transition-colors" placeholder="请输入参数2的值">
</div>
<div class="mb-4">
<label for="param3" class="block text-gray-700 text-sm font-medium mb-2">参数3</label>
<input type="text" id="param3" class="w-full px-4 py-2 border border-gray-300 rounded-md input-focus transition-colors" placeholder="请输入参数3的值">
</div>
<div class="mb-4">
<label for="param4" class="block text-gray-700 text-sm font-medium mb-2">参数4</label>
<input type="text" id="param4" class="w-full px-4 py-2 border border-gray-300 rounded-md input-focus transition-colors" placeholder="请输入参数4的值">
</div>
<div class="mb-6">
<label for="param5" class="block text-gray-700 text-sm font-medium mb-2">参数5</label>
<input type="text" id="param5" class="w-full px-4 py-2 border border-gray-300 rounded-md input-focus transition-colors" placeholder="请输入参数5的值">
</div>
<div class="flex space-x-3">
<button onclick="loadParameterConfig()" class="flex-1 border border-gray-300 text-gray-700 hover:bg-gray-50 py-2 px-4 rounded-md transition-colors flex items-center justify-center">
<i class="fa fa-refresh mr-2"></i> 重新加载
</button>
<button onclick="saveParameterConfig()" class="flex-1 bg-primary hover:bg-primary-dark text-white py-2 px-4 rounded-md transition-colors shadow-sm hover:shadow flex items-center justify-center">
<i class="fa fa-save mr-2"></i> 保存配置
</button>
</div>
</div>
</main>
<script>
// 页面加载时获取参数配置
document.addEventListener('DOMContentLoaded', function() {
loadParameterConfig();
});
// 加载参数配置
function loadParameterConfig() {
fetch('/get_parameter_config')
.then(response => response.json())
.then(data => {
if (data.success && data.parameters) {
const parameters = data.parameters;
document.getElementById('param1').value = parameters.param1 || '';
document.getElementById('param2').value = parameters.param2 || '';
document.getElementById('param3').value = parameters.param3 || '';
document.getElementById('param4').value = parameters.param4 || '';
document.getElementById('param5').value = parameters.param5 || '';
// 显示更新时间
if (parameters.update_time) {
document.getElementById('updateTimeContent').textContent = '最后更新时间:' + parameters.update_time;
document.getElementById('updateTimeAlert').style.display = 'flex';
}
} else {
showErrorMessage('获取参数配置失败:' + (data.error || ''));
}
})
.catch(error => {
showErrorMessage('获取参数配置失败,请稍后重试');
console.error('获取参数配置失败', error);
});
}
// 保存参数配置
function saveParameterConfig() {
// 表单验证
if (!document.getElementById('param1').value.trim()) {
showErrorMessage('参数1不能为空');
return;
}
const formData = {
param1: document.getElementById('param1').value.trim(),
param2: document.getElementById('param2').value.trim(),
param3: document.getElementById('param3').value.trim(),
param4: document.getElementById('param4').value.trim(),
param5: document.getElementById('param5').value.trim()
};
fetch('/save_parameter_config', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
})
.then(response => response.json())
.then(data => {
if (data.success) {
showSuccessMessage('参数配置保存成功');
// 重新加载参数配置
loadParameterConfig();
} else {
showErrorMessage('保存参数配置失败:' + (data.error || ''));
}
})
.catch(error => {
let errorMessage = '保存参数配置失败,请稍后重试';
if (error.responseJSON && error.responseJSON.error) {
errorMessage = error.responseJSON.error;
}
showErrorMessage(errorMessage);
console.error('保存参数配置失败', error);
});
}
// 显示错误消息
function showErrorMessage(message) {
// 创建错误消息元素
const errorDiv = document.createElement('div');
errorDiv.className = 'mb-4 bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-md flex items-start animate-fade-in';
errorDiv.innerHTML = `
<i class="fa fa-exclamation-circle mt-1 mr-3"></i>
<span>${message}</span>
`;
// 插入到表单前
const container = document.querySelector('.space-y-4');
container.insertBefore(errorDiv, container.firstChild);
// 3秒后自动移除
setTimeout(() => {
errorDiv.style.opacity = '0';
errorDiv.style.transform = 'translateY(-10px)';
errorDiv.style.transition = 'opacity 0.3s, transform 0.3s';
setTimeout(() => {
if (errorDiv.parentNode) {
errorDiv.parentNode.removeChild(errorDiv);
}
}, 300);
}, 3000);
}
// 显示成功消息
function showSuccessMessage(message) {
// 创建成功消息元素
const successDiv = document.createElement('div');
successDiv.className = 'mb-4 bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-md flex items-start animate-fade-in';
successDiv.innerHTML = `
<i class="fa fa-check-circle mt-1 mr-3"></i>
<span>${message}</span>
`;
// 插入到表单前
const container = document.querySelector('.space-y-4');
container.insertBefore(successDiv, container.firstChild);
// 3秒后自动移除
setTimeout(() => {
successDiv.style.opacity = '0';
successDiv.style.transform = 'translateY(-10px)';
successDiv.style.transition = 'opacity 0.3s, transform 0.3s';
setTimeout(() => {
if (successDiv.parentNode) {
successDiv.parentNode.removeChild(successDiv);
}
}, 300);
}, 3000);
}
</script>
{% include 'footer.html' %}
</body>
</html>
+198 -762
View File
File diff suppressed because it is too large Load Diff
+127
View File
@@ -0,0 +1,127 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>个人信息 - 车主信息查询系统</title>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Font Awesome -->
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<!-- 配置Tailwind自定义颜色和字体 -->
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#2563eb',
secondary: '#f3f4f6',
'primary-light': '#dbeafe',
'primary-dark': '#1d4ed8',
'table-header': '#f9fafb',
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
}
}
}
</script>
<!-- 自定义工具类 -->
<style type="text/tailwindcss">
@layer utilities {
.content-auto {
content-visibility: auto;
}
.card-shadow {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
}
.input-focus {
@apply focus:border-primary focus:ring-2 focus:ring-primary/20 focus:outline-none;
}
.animate-fade-in {
animation: fadeIn 0.3s ease-out forwards;
}
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
</head>
<body class="bg-secondary min-h-screen font-sans">
{% include 'header.html' %}
<!-- 主要内容区域 -->
<main class="container mx-auto px-4 pt-24 pb-12">
<!-- 返回按钮 -->
<div class="mb-6 flex justify-end">
<a href="{{ url_for('query') }}" class="inline-flex items-center text-primary hover:text-primary-dark transition-colors">
<i class="fa fa-arrow-left mr-2"></i> 返回主页面
</a>
</div>
<!-- 个人信息表单 -->
<div class="max-w-md mx-auto bg-white rounded-lg shadow-xl p-8 animate-fade-in">
<div class="text-center mb-6">
<div class="w-16 h-16 bg-primary-light rounded-full flex items-center justify-center mx-auto mb-4">
<i class="fa fa-user text-primary text-2xl"></i>
</div>
<h2 class="text-2xl font-bold text-gray-800">个人信息</h2>
<p class="text-gray-500 mt-2">请查看并更新您的个人信息</p>
</div>
<!-- 消息提示区域 -->
{% if error %}
<div class="mb-4 bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-md flex items-start animate-fade-in">
<i class="fa fa-exclamation-circle mt-1 mr-3"></i>
<span>{{ error }}</span>
</div>
{% endif %}
{% if success %}
<div class="mb-4 bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-md flex items-start animate-fade-in">
<i class="fa fa-check-circle mt-1 mr-3"></i>
<span>{{ success }}</span>
</div>
{% endif %}
<!-- 表单 -->
<form id="userProfileForm" method="POST" class="space-y-4">
<div class="mb-4">
<label for="profile_username" class="block text-gray-700 text-sm font-medium mb-2">用户名</label>
<input type="text" id="profile_username" value="{{ user.username }}" class="w-full px-4 py-2 border border-gray-300 rounded-md input-focus transition-colors bg-gray-50 cursor-not-allowed" disabled>
</div>
<div class="mb-4">
<label for="profile_name" class="block text-gray-700 text-sm font-medium mb-2">姓名 <span class="text-red-500">*</span></label>
<input type="text" id="profile_name" name="name" value="{{ user.name }}" class="w-full px-4 py-2 border border-gray-300 rounded-md input-focus transition-colors" placeholder="请输入姓名" required>
</div>
<div class="mb-4">
<label for="profile_phone" class="block text-gray-700 text-sm font-medium mb-2">联系电话 <span class="text-red-500">*</span></label>
<input type="tel" id="profile_phone" name="phone" value="{{ user.phone }}" class="w-full px-4 py-2 border border-gray-300 rounded-md input-focus transition-colors" placeholder="请输入联系电话" required>
</div>
<div class="mb-6">
<label for="profile_department" class="block text-gray-700 text-sm font-medium mb-2">部门名称 <span class="text-red-500">*</span></label>
<input type="text" id="profile_department" name="department" value="{{ user.department }}" class="w-full px-4 py-2 border border-gray-300 rounded-md input-focus transition-colors" placeholder="请输入部门名称" required>
</div>
<div class="flex space-x-3">
<button type="submit" class="flex-1 bg-primary hover:bg-primary-dark text-white py-2 px-4 rounded-md transition-colors shadow-sm hover:shadow">
保存修改
</button>
<button type="button" onclick="window.location.href='{{ url_for('query') }}'" class="flex-1 border border-gray-300 text-gray-700 hover:bg-gray-50 py-2 px-4 rounded-md transition-colors">
取消
</button>
</div>
</form>
</div>
</main>
{% include 'footer.html' %}
</body>
</html>
+53 -617
View File
@@ -32,6 +32,9 @@
} }
</script> </script>
<!-- 引入通用工具函数 -->
<script src="{{ url_for('static', filename='js/utils.js') }}"></script>
<!-- 自定义工具类 --> <!-- 自定义工具类 -->
<style type="text/tailwindcss"> <style type="text/tailwindcss">
@layer utilities { @layer utilities {
@@ -65,48 +68,17 @@
</style> </style>
</head> </head>
<body class="bg-secondary min-h-screen font-sans"> <body class="bg-secondary min-h-screen font-sans">
<!-- 顶部导航栏 --> {% include 'header.html' %}
<header class="bg-white shadow-sm fixed top-0 left-0 right-0 z-10">
<div class="container mx-auto px-4 py-3 flex justify-between items-center">
<div class="flex items-center space-x-2">
<i class="fa fa-car text-primary text-2xl"></i>
<h1 class="text-xl font-bold text-gray-800">车主信息查询系统</h1>
</div>
<div class="flex items-center space-x-4">
<!-- 用户下拉菜单 -->
<div class="relative group">
<div class="flex items-center cursor-pointer hover:text-primary transition-colors">
<span class="text-gray-600">欢迎 <strong>{{ session.department }}</strong> <strong>{{ session.name }}</strong></span>
<i class="fa fa-chevron-down ml-2 text-xs transition-transform duration-300 group-hover:rotate-180"></i>
</div>
<!-- 下拉菜单 -->
<div class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-300 transform origin-top-right scale-95 group-hover:scale-100 z-20">
<div class="py-1">
<a href="#" onclick="showChangePasswordModal()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-primary hover:text-white transition-colors">
<i class="fa fa-key mr-2"></i> 修改密码
</a>
<a href="{{ url_for('user.users_management') }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-primary hover:text-white transition-colors bg-primary/10">
<i class="fa fa-users mr-2"></i> 用户管理
</a>
<a href="#" onclick="showUserProfileModal()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-primary hover:text-white transition-colors">
<i class="fa fa-user mr-2"></i> 个人信息
</a>
<a href="#" onclick="showLoginInfoModal()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-primary hover:text-white transition-colors">
<i class="fa fa-sign-in mr-2"></i> 后台登录信息
</a>
<div class="border-t border-gray-200 my-1"></div>
<a href="{{ url_for('user.logout') }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-primary hover:text-white transition-colors">
<i class="fa fa-sign-out mr-2"></i> 退出
</a>
</div>
</div>
</div>
</div>
</div>
</header>
<!-- 主内容区 --> <!-- 主内容区 -->
<main class="container mx-auto px-4 pt-24 pb-12"> <main class="container mx-auto px-4 pt-24 pb-12">
<!-- 返回按钮 -->
<div class="mb-6 flex justify-end">
<a href="{{ url_for('query') }}" class="inline-flex items-center text-primary hover:text-primary-dark transition-colors">
<i class="fa fa-arrow-left mr-2"></i> 返回主页面
</a>
</div>
<!-- 页面标题 --> <!-- 页面标题 -->
<div class="mb-6"> <div class="mb-6">
<h2 class="text-2xl font-bold text-gray-800">用户管理</h2> <h2 class="text-2xl font-bold text-gray-800">用户管理</h2>
@@ -197,6 +169,8 @@
</div> </div>
</main> </main>
{% include 'footer.html' %}
<!-- 修改部门信息弹窗 --> <!-- 修改部门信息弹窗 -->
<div id="editDepartmentModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> <div id="editDepartmentModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4 overflow-hidden transform transition-all animate-fade-in"> <div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4 overflow-hidden transform transition-all animate-fade-in">
@@ -271,177 +245,6 @@
</div> </div>
</div> </div>
<!-- 修改密码弹窗 -->
<div id="changePasswordModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4 overflow-hidden transform transition-all animate-fade-in">
<div class="bg-primary text-white p-4 flex justify-between items-center">
<h3 class="text-lg font-medium">修改密码</h3>
<button onclick="closeChangePasswordModal()" class="text-white hover:text-gray-200 transition-colors">
<i class="fa fa-times"></i>
</button>
</div>
<div class="p-6">
<!-- 错误信息显示区域 -->
<div id="changePasswordError" class="mb-4 bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-md flex items-start hidden">
<i class="fa fa-exclamation-circle mt-1 mr-3"></i>
<span id="errorChangePasswordMessage"></span>
</div>
<!-- 成功信息显示区域 -->
<div id="changePasswordSuccess" class="mb-4 bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-md flex items-start hidden">
<i class="fa fa-check-circle mt-1 mr-3"></i>
<span id="successChangePasswordMessage"></span>
</div>
<form id="changePasswordForm">
<div class="mb-4">
<label for="old_password" class="block text-sm font-medium text-gray-700 mb-1">当前密码</label>
<input type="password" id="old_password" class="w-full px-3 py-2 border border-gray-300 rounded-md input-focus">
</div>
<div class="mb-4">
<label for="new_password" class="block text-sm font-medium text-gray-700 mb-1">新密码</label>
<input type="password" id="new_password" class="w-full px-3 py-2 border border-gray-300 rounded-md input-focus">
</div>
<div class="mb-6">
<label for="confirm_password" class="block text-sm font-medium text-gray-700 mb-1">确认新密码</label>
<input type="password" id="confirm_password" class="w-full px-3 py-2 border border-gray-300 rounded-md input-focus">
</div>
<div class="flex space-x-4">
<button type="submit" class="flex-1 bg-primary hover:bg-primary/90 text-white py-2 px-4 rounded-md transition-colors shadow-sm hover:shadow">
保存修改
</button>
<button type="button" onclick="closeChangePasswordModal()" class="flex-1 border border-gray-300 text-gray-700 hover:bg-gray-50 py-2 px-4 rounded-md transition-colors">
取消
</button>
</div>
</form>
</div>
</div>
</div>
<!-- 个人信息弹窗 -->
<div id="userProfileModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4 overflow-hidden transform transition-all animate-fade-in">
<div class="bg-primary text-white p-4 flex justify-between items-center">
<h3 class="text-lg font-medium">个人信息</h3>
<button onclick="closeUserProfileModal()" class="text-white hover:text-gray-200 transition-colors">
<i class="fa fa-times"></i>
</button>
</div>
<div class="p-6">
<!-- 错误信息显示区域 -->
<div id="userProfileError" class="mb-4 bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-md flex items-start hidden">
<i class="fa fa-exclamation-circle mt-1 mr-3"></i>
<span id="errorUserProfileMessage"></span>
</div>
<!-- 成功信息显示区域 -->
<div id="userProfileSuccess" class="mb-4 bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-md flex items-start hidden">
<i class="fa fa-check-circle mt-1 mr-3"></i>
<span id="successUserProfileMessage"></span>
</div>
<form id="userProfileForm">
<div class="mb-4">
<label for="profile_username" class="block text-sm font-medium text-gray-700 mb-1">用户名</label>
<input type="text" id="profile_username" readonly class="w-full px-3 py-2 border border-gray-300 rounded-md bg-gray-50 cursor-not-allowed">
</div>
<div class="mb-4">
<label for="profile_name" class="block text-sm font-medium text-gray-700 mb-1">姓名</label>
<input type="text" id="profile_name" class="w-full px-3 py-2 border border-gray-300 rounded-md input-focus">
</div>
<div class="mb-4">
<label for="profile_phone" class="block text-sm font-medium text-gray-700 mb-1">电话</label>
<input type="text" id="profile_phone" class="w-full px-3 py-2 border border-gray-300 rounded-md input-focus">
</div>
<div class="mb-6">
<label for="profile_department" class="block text-sm font-medium text-gray-700 mb-1">部门</label>
<input type="text" id="profile_department" readonly class="w-full px-3 py-2 border border-gray-300 rounded-md bg-gray-50 cursor-not-allowed">
</div>
<div class="flex space-x-4">
<button type="submit" class="flex-1 bg-primary hover:bg-primary/90 text-white py-2 px-4 rounded-md transition-colors shadow-sm hover:shadow">
保存修改
</button>
<button type="button" onclick="closeUserProfileModal()" class="flex-1 border border-gray-300 text-gray-700 hover:bg-gray-50 py-2 px-4 rounded-md transition-colors">
取消
</button>
</div>
</form>
</div>
</div>
</div>
<!-- 后台登录信息弹窗 -->
<div id="loginInfoModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4 overflow-hidden transform transition-all animate-fade-in">
<div class="bg-primary text-white p-4 flex justify-between items-center">
<h3 class="text-lg font-medium">后台登录信息</h3>
<button onclick="closeLoginInfoModal()" class="text-white hover:text-gray-200 transition-colors">
<i class="fa fa-times"></i>
</button>
</div>
<div class="p-6">
<!-- 错误信息显示区域 -->
<div id="loginInfoError" class="mb-4 bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-md flex items-start hidden">
<i class="fa fa-exclamation-circle mt-1 mr-3"></i>
<span id="errorLoginInfoMessage"></span>
</div>
<!-- 成功信息显示区域 -->
<div id="loginInfoSuccess" class="mb-4 bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-md flex items-start hidden">
<i class="fa fa-check-circle mt-1 mr-3"></i>
<span id="successLoginInfoMessage"></span>
</div>
<form id="loginInfoForm">
<div class="bg-gray-50 p-4 rounded-md">
<div class="grid grid-cols-1 gap-2 text-sm">
<div class="text-gray-500 mb-1">登录用户名:</div>
<input type="text" id="loginUsername" value="{{ session.username }}"
{% if session.username != 'admin' %}readonly{% endif %}
class="w-full px-3 py-2 border rounded-md text-gray-800 input-focus"
{% if session.username != 'admin' %}style="background-color: #f3f4f6; cursor: not-allowed;"{% endif %}>
</div>
</div>
<div class="bg-gray-50 p-4 rounded-md">
<div class="grid grid-cols-1 gap-2 text-sm">
<div class="text-gray-500 mb-1">用户密码:</div>
<input type="password" id="loginPassword" value="" placeholder="{% if session.username == 'admin' %}输入新密码或保持为空不修改{% else %}只读,无法修改{% endif %}"
{% if session.username != 'admin' %}readonly{% endif %}
class="w-full px-3 py-2 border rounded-md text-gray-800 input-focus"
{% if session.username != 'admin' %}style="background-color: #f3f4f6; cursor: not-allowed;"{% endif %}>
</div>
</div>
<div class="bg-gray-50 p-4 rounded-md">
<div class="grid grid-cols-1 gap-2 text-sm">
<div class="text-gray-500 mb-1">OTP码:</div>
<input type="text" id="loginOtpCode" value="{{ session.otp_code if 'otp_code' in session else '' }}"
class="w-full px-3 py-2 border rounded-md text-gray-800 input-focus">
</div>
</div>
</form>
<div class="mt-6 flex space-x-4">
<button type="button" onclick="saveLoginInfo()" class="flex-1 bg-primary hover:bg-primary/90 text-white py-2 px-4 rounded-md transition-colors shadow-sm hover:shadow">
保存修改
</button>
<button type="button" onclick="closeLoginInfoModal()" class="flex-1 border border-gray-300 text-gray-700 hover:bg-gray-50 py-2 px-4 rounded-md transition-colors">
取消
</button>
</div>
</div>
</div>
</div>
<!-- 用户管理相关JavaScript --> <!-- 用户管理相关JavaScript -->
<script> <script>
// 当前页码和每页显示数量 // 当前页码和每页显示数量
@@ -454,18 +257,6 @@
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
loadUsers(); loadUsers();
// 绑定修改密码表单提交事件
document.getElementById('changePasswordForm').addEventListener('submit', function(e) {
e.preventDefault();
changePassword();
});
// 绑定个人信息表单提交事件
document.getElementById('userProfileForm').addEventListener('submit', function(e) {
e.preventDefault();
updateUserProfile();
});
// 绑定搜索按钮点击事件 // 绑定搜索按钮点击事件
document.getElementById('searchUsername').addEventListener('keypress', function(e) { document.getElementById('searchUsername').addEventListener('keypress', function(e) {
if (e.key === 'Enter') { if (e.key === 'Enter') {
@@ -487,24 +278,27 @@
renderUsersTable(); renderUsersTable();
} }
}); });
// 绑定模态框点击外部关闭事件
bindModalOutsideClick('editDepartmentModal');
bindModalOutsideClick('resetPasswordModal');
}); });
// 加载用户列表 // 加载用户列表
function loadUsers() { function loadUsers() {
fetch('{{ url_for('user.get_users') }}') apiRequest('{{ url_for('user.get_users') }}')
.then(response => response.json())
.then(data => { .then(data => {
if (data.success) { if (data.success) {
usersData = data.users; usersData = data.users;
totalUsers = usersData.length; totalUsers = usersData.length;
renderUsersTable(); renderUsersTable();
} else { } else {
showError('加载用户列表失败:' + (data.error || '未知错误')); showMessage('加载用户列表失败:' + (data.error || '未知错误'), false);
} }
}) })
.catch(error => { .catch(error => {
console.error('加载用户列表时发生错误:', error); console.error('加载用户列表时发生错误:', error);
showError('网络错误,请稍后重试'); showMessage('网络错误,请稍后重试', false);
}); });
} }
@@ -599,12 +393,12 @@
currentPage = 1; // 重置到第一页 currentPage = 1; // 重置到第一页
renderUsersTable(); renderUsersTable();
} else { } else {
showError('搜索用户失败:' + (data.error || '未知错误')); showMessage('搜索用户失败:' + (data.error || '未知错误'), false);
} }
}) })
.catch(error => { .catch(error => {
console.error('搜索用户时发生错误:', error); console.error('搜索用户时发生错误:', error);
showError('网络错误,请稍后重试'); showMessage('网络错误,请稍后重试', false);
}); });
} }
@@ -614,28 +408,25 @@
const confirmMessage = `确定要${newStatus === 'active' ? '启用' : '禁用'}用户 ${username} 吗?`; const confirmMessage = `确定要${newStatus === 'active' ? '启用' : '禁用'}用户 ${username} 吗?`;
if (confirm(confirmMessage)) { if (confirm(confirmMessage)) {
fetch('{{ url_for('user.update_user_status') }}', { apiRequest('{{ url_for('user.update_user_status') }}', 'POST', {
method: 'POST', user_id: userId,
status: newStatus
}, {
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, }
body: JSON.stringify({
user_id: userId,
status: newStatus
})
}) })
.then(response => response.json())
.then(data => { .then(data => {
if (data.success) { if (data.success) {
showSuccess(data.message || '用户状态更新成功'); showMessage(data.message || '用户状态更新成功', true);
loadUsers(); // 重新加载用户列表 loadUsers(); // 重新加载用户列表
} else { } else {
showError(data.error || '用户状态更新失败'); showMessage(data.error || '用户状态更新失败', false);
} }
}) })
.catch(error => { .catch(error => {
console.error('更新用户状态时发生错误:', error); console.error('更新用户状态时发生错误:', error);
showError('网络错误,请稍后重试'); showMessage('网络错误,请稍后重试', false);
}); });
} }
} }
@@ -656,7 +447,7 @@
} }
} }
document.getElementById('editDepartmentModal').classList.remove('hidden'); showModal('editDepartmentModal');
} }
// 关闭修改部门弹窗 // 关闭修改部门弹窗
@@ -682,18 +473,18 @@
}) })
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.success) { if (data.success) {
showSuccess(data.message || '部门信息更新成功'); showMessage(data.message || '部门信息更新成功', true);
closeEditDepartmentModal(); closeModal('editDepartmentModal');
loadUsers(); // 重新加载用户列表 loadUsers(); // 重新加载用户列表
} else { } else {
showError(data.error || '部门信息更新失败'); showMessage(data.error || '部门信息更新失败', false);
} }
}) })
.catch(error => { .catch(error => {
console.error('更新部门信息时发生错误:', error); console.error('更新部门信息时发生错误:', error);
showError('网络错误,请稍后重试'); showMessage('网络错误,请稍后重试', false);
}); });
} }
// 显示初始化密码弹窗 // 显示初始化密码弹窗
@@ -727,379 +518,24 @@
}) })
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.success) {
showSuccess(data.message || '密码初始化成功');
closeResetPasswordModal();
} else {
showError(data.error || '密码初始化失败');
}
})
.catch(error => {
console.error('初始化密码时发生错误:', error);
showError('网络错误,请稍后重试');
});
}
// 显示成功消息
function showSuccess(message) {
const messageContainer = document.getElementById('messageContainer');
const successMessage = document.getElementById('successMessage');
const errorMessage = document.getElementById('errorMessage');
successMessage.querySelector('span').textContent = message;
successMessage.classList.remove('hidden');
errorMessage.classList.add('hidden');
messageContainer.classList.remove('hidden');
// 5秒后自动隐藏
setTimeout(() => {
successMessage.classList.add('hidden');
messageContainer.classList.add('hidden');
}, 5000);
}
// 显示错误消息
function showError(message) {
const messageContainer = document.getElementById('messageContainer');
const successMessage = document.getElementById('successMessage');
const errorMessage = document.getElementById('errorMessage');
errorMessage.querySelector('span').textContent = message;
errorMessage.classList.remove('hidden');
successMessage.classList.add('hidden');
messageContainer.classList.remove('hidden');
// 5秒后自动隐藏
setTimeout(() => {
errorMessage.classList.add('hidden');
messageContainer.classList.add('hidden');
}, 5000);
}
// 修改密码相关函数
function showChangePasswordModal() {
document.getElementById('changePasswordModal').classList.remove('hidden');
document.getElementById('old_password').focus();
}
function closeChangePasswordModal() {
document.getElementById('changePasswordModal').classList.add('hidden');
// 重置表单
document.getElementById('changePasswordForm').reset();
}
function changePassword() {
// 获取表单数据
const oldPassword = document.getElementById('old_password').value;
const newPassword = document.getElementById('new_password').value;
const confirmPassword = document.getElementById('confirm_password').value;
// 隐藏之前的消息
document.getElementById('changePasswordError').classList.add('hidden');
document.getElementById('changePasswordSuccess').classList.add('hidden');
// 前端验证
if (!oldPassword || !newPassword || !confirmPassword) {
showChangePasswordError('请填写所有必填字段');
return;
}
if (newPassword !== confirmPassword) {
showChangePasswordError('两次输入的新密码不一致');
return;
}
// 异步提交修改
fetch('{{ url_for('user.change_password') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
'old_password': oldPassword,
'new_password': newPassword,
'confirm_password': confirmPassword
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 修改成功
showChangePasswordSuccess(data.message || '修改密码成功,请重新登录');
// 3秒后跳转登录页
setTimeout(() => {
window.location.href = '{{ url_for('user.login') }}';
}, 3000);
} else {
// 显示错误信息
showChangePasswordError(data.error || '修改密码失败');
}
})
.catch(error => {
console.error('修改密码时发生错误:', error);
showChangePasswordError('网络错误,请稍后重试');
});
}
function showChangePasswordError(message) {
const errorContainer = document.getElementById('changePasswordError');
const errorMessage = document.getElementById('errorChangePasswordMessage');
errorMessage.textContent = message;
errorContainer.classList.remove('hidden');
document.getElementById('changePasswordSuccess').classList.add('hidden');
// 滚动到错误信息
errorContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
function showChangePasswordSuccess(message) {
const successContainer = document.getElementById('changePasswordSuccess');
const successMessage = document.getElementById('successChangePasswordMessage');
successMessage.textContent = message;
successContainer.classList.remove('hidden');
document.getElementById('changePasswordError').classList.add('hidden');
// 滚动到成功信息
successContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
// 个人信息相关函数
function showUserProfileModal() {
// 获取个人信息
fetch('{{ url_for('user.get_user_profile') }}')
.then(response => response.json())
.then(data => {
if (data.success) { if (data.success) {
const user = data.user; showMessage(data.message || '密码初始化成功', true);
document.getElementById('profile_username').value = user.username; closeModal('resetPasswordModal');
document.getElementById('profile_name').value = user.name;
document.getElementById('profile_phone').value = user.phone;
document.getElementById('profile_department').value = user.department;
document.getElementById('userProfileModal').classList.remove('hidden');
} else { } else {
// 显示错误信息 showMessage(data.error || '密码初始化失败', false);
alert('获取个人信息失败:' + (data.error || '未知错误'));
} }
}) })
.catch(error => { .catch(error => {
console.error('获取个人信息时发生错误:', error); console.error('初始化密码时发生错误:', error);
alert('网络错误,请稍后重试'); showMessage('网络错误,请稍后重试', false);
}); });
} }
function closeUserProfileModal() {
document.getElementById('userProfileModal').classList.add('hidden');
// 隐藏之前的消息
document.getElementById('userProfileError').classList.add('hidden');
document.getElementById('userProfileSuccess').classList.add('hidden');
}
function updateUserProfile() {
// 获取表单数据
const name = document.getElementById('profile_name').value.trim();
const phone = document.getElementById('profile_phone').value.trim();
// 隐藏之前的消息
document.getElementById('userProfileError').classList.add('hidden');
document.getElementById('userProfileSuccess').classList.add('hidden');
// 前端验证
if (!name || !phone) {
showUserProfileError('请填写所有必填字段');
return;
}
// 异步提交修改
fetch('{{ url_for('user.update_user_profile') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
'name': name,
'phone': phone
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 更新成功
showUserProfileSuccess(data.message || '个人信息更新成功');
// 3秒后关闭弹窗
setTimeout(() => {
closeUserProfileModal();
// 刷新页面以更新会话信息
location.reload();
}, 1500);
} else {
// 显示错误信息
showUserProfileError(data.error || '个人信息更新失败');
}
})
.catch(error => {
console.error('更新个人信息时发生错误:', error);
showUserProfileError('网络错误,请稍后重试');
});
}
function showUserProfileError(message) {
const errorContainer = document.getElementById('userProfileError');
const errorMessage = document.getElementById('errorUserProfileMessage');
errorMessage.textContent = message;
errorContainer.classList.remove('hidden');
document.getElementById('userProfileSuccess').classList.add('hidden');
// 滚动到错误信息
errorContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
function showUserProfileSuccess(message) {
const successContainer = document.getElementById('userProfileSuccess');
const successMessage = document.getElementById('successUserProfileMessage');
successMessage.textContent = message;
successContainer.classList.remove('hidden');
document.getElementById('userProfileError').classList.add('hidden');
// 滚动到成功信息
successContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
// 后台登录信息相关函数
function showLoginInfoModal() {
document.getElementById('loginInfoModal').classList.remove('hidden');
}
function closeLoginInfoModal() {
document.getElementById('loginInfoModal').classList.add('hidden');
// 隐藏之前的消息
document.getElementById('loginInfoError').classList.add('hidden');
document.getElementById('loginInfoSuccess').classList.add('hidden');
}
function saveLoginInfo() {
// 获取表单数据
const username = document.getElementById('loginUsername').value.trim();
const password = document.getElementById('loginPassword').value;
const otpCode = document.getElementById('loginOtpCode').value.trim();
// 隐藏之前的消息
document.getElementById('loginInfoError').classList.add('hidden');
document.getElementById('loginInfoSuccess').classList.add('hidden');
// 前端验证
if (!otpCode) {
showLoginInfoError('OTP码不能为空');
return;
}
if (otpCode.length !== 6) {
showLoginInfoError('OTP码长度必须是6位');
return;
}
// 如果是admin用户修改密码,需要验证密码非空
{% if session.username == 'admin' %}
if (password === '') {
showLoginInfoError('密码不能为空');
return;
}
{% endif %}
// 构建请求数据
const data = {
username: username,
otp_code: otpCode
};
// 如果admin用户提供了新密码,添加到请求数据中
{% if session.username == 'admin' %}
if (password) {
data.password = password;
}
{% endif %}
// 异步提交修改
fetch('{{ url_for('user.update_login_info') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 更新成功
showLoginInfoSuccess(data.message || '登录信息更新成功');
// 更新session中的OTP码
{% if 'session' in globals %}
session['otp_code'] = otpCode;
{% endif %}
// 3秒后关闭弹窗
setTimeout(() => {
closeLoginInfoModal();
}, 1500);
} else {
// 显示服务器返回的错误信息
showLoginInfoError(data.error || '登录信息更新失败');
}
})
.catch(error => {
console.error('更新登录信息时发生错误:', error);
showLoginInfoError('网络错误,请稍后重试');
});
}
function showLoginInfoError(message) {
const errorContainer = document.getElementById('loginInfoError');
const errorMessage = document.getElementById('errorLoginInfoMessage');
errorMessage.textContent = message;
errorContainer.classList.remove('hidden');
document.getElementById('loginInfoSuccess').classList.add('hidden');
// 滚动到错误信息
errorContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
function showLoginInfoSuccess(message) {
const successContainer = document.getElementById('loginInfoSuccess');
const successMessage = document.getElementById('successLoginInfoMessage');
successMessage.textContent = message;
successContainer.classList.remove('hidden');
document.getElementById('loginInfoError').classList.add('hidden');
// 滚动到成功信息
successContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
// 点击弹窗外部关闭弹窗
document.getElementById('editDepartmentModal').addEventListener('click', function(e) {
if (e.target === this) {
closeEditDepartmentModal();
}
});
document.getElementById('resetPasswordModal').addEventListener('click', function(e) {
if (e.target === this) {
closeResetPasswordModal();
}
});
document.getElementById('changePasswordModal').addEventListener('click', function(e) {
if (e.target === this) {
closeChangePasswordModal();
}
});
document.getElementById('userProfileModal').addEventListener('click', function(e) {
if (e.target === this) {
closeUserProfileModal();
}
});
document.getElementById('loginInfoModal').addEventListener('click', function(e) {
if (e.target === this) {
closeLoginInfoModal();
}
});
</script> </script>
</body> </body>
</html> </html>
+256 -125
View File
@@ -23,16 +23,6 @@ def init_user_db():
) )
''') ''')
# 创建登录信息表
cursor.execute('''
CREATE TABLE IF NOT EXISTS login_info (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
otp_code TEXT
)
''')
# 检查是否存在admin用户,如果不存在则创建 # 检查是否存在admin用户,如果不存在则创建
cursor.execute("SELECT * FROM users WHERE username = 'admin'") cursor.execute("SELECT * FROM users WHERE username = 'admin'")
if not cursor.fetchone(): if not cursor.fetchone():
@@ -41,9 +31,6 @@ def init_user_db():
"INSERT INTO users (username, password, name, phone, department) VALUES (?, ?, ?, ?, ?)", "INSERT INTO users (username, password, name, phone, department) VALUES (?, ?, ?, ?, ?)",
('admin', 'admin123', '管理员', '13800138000', '管理部') ('admin', 'admin123', '管理员', '13800138000', '管理部')
) )
# 同时插入到登录信息表
cursor.execute("INSERT INTO login_info (username, password, otp_code) VALUES (?, ?, ?)",
('admin', 'admin123', ''))
# 插入一些测试数据 # 插入一些测试数据
cursor.execute("SELECT COUNT(*) FROM users") cursor.execute("SELECT COUNT(*) FROM users")
@@ -60,8 +47,6 @@ def init_user_db():
"INSERT INTO users (username, password, name, phone, department) VALUES (?, ?, ?, ?, ?)", "INSERT INTO users (username, password, name, phone, department) VALUES (?, ?, ?, ?, ?)",
user user
) )
cursor.execute("INSERT INTO login_info (username, password, otp_code) VALUES (?, ?, ?)",
(user[0], user[1], ''))
conn.commit() conn.commit()
conn.close() conn.close()
@@ -93,19 +78,7 @@ 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')
# 检查是否有login_info记录,如果没有则创建
cursor.execute("SELECT * FROM login_info WHERE username = ?", (username,))
login_info = cursor.fetchone()
if not login_info:
cursor.execute("INSERT INTO login_info (username, password, otp_code) VALUES (?, ?, ?)",
(username, password, ""))
conn.commit()
# 设置session中的OTP码为空
session['otp_code'] = ""
else:
# 设置session中的OTP码
session['otp_code'] = login_info[3] if login_info[3] else ""
conn.close() conn.close()
@@ -154,9 +127,7 @@ def register():
"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)
) )
# 同时插入到登录信息表,不自动生成OTP码
cursor.execute("INSERT INTO login_info (username, password, otp_code) VALUES (?, ?, ?)",
(username, password, ""))
conn.commit() conn.commit()
conn.close() conn.close()
# 注册成功,跳转到登录页并显示成功消息 # 注册成功,跳转到登录页并显示成功消息
@@ -203,8 +174,7 @@ def change_password():
# 更新密码 # 更新密码
cursor.execute("UPDATE users SET password = ? WHERE username = ?", (new_password, session['username'])) cursor.execute("UPDATE users SET password = ? WHERE username = ?", (new_password, session['username']))
# 同时更新login_info表中的密码
cursor.execute("UPDATE login_info SET password = ? WHERE username = ?", (new_password, session['username']))
conn.commit() conn.commit()
conn.close() conn.close()
@@ -283,91 +253,7 @@ def update_user_profile():
conn.close() conn.close()
return jsonify({'success': False, 'error': f'更新失败,请重试。错误:{str(e)}'}) return jsonify({'success': False, 'error': f'更新失败,请重试。错误:{str(e)}'})
# 获取登录信息路由
@bp.route('/get_login_info', methods=['GET'])
def get_login_info():
if 'username' not in session:
return jsonify({'success': False, 'error': '未登录'}), 401
# 获取登录时间 - 如果会话中没有存储登录时间,可以使用当前时间
login_time = session.get('login_time', datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
# 获取登录IP地址
login_ip = request.remote_addr
# 构造返回数据
return jsonify({
'success': True,
'login_time': login_time,
'login_ip': login_ip
})
# 登录信息更新路由
@bp.route('/update_login_info', methods=['POST'])
def update_login_info():
import re
if 'username' not in session:
return jsonify({'success': False, 'error': '请先登录'}), 401
data = request.get_json()
username = data.get('username')
password = data.get('password')
otp_code = data.get('otp_code')
# 验证输入
if not username or not otp_code:
return jsonify({'success': False, 'error': '用户名和OTP码不能为空'})
# 验证OTP码长度必须是6位
if len(otp_code) != 6:
return jsonify({'success': False, 'error': 'OTP码长度必须是6位'})
# 确保用户只能更新自己的信息
if username != session['username'] and session['username'] != 'admin':
return jsonify({'success': False, 'error': '无权更新其他用户的信息'})
# 连接数据库
conn = sqlite3.connect('car_info.db')
cursor = conn.cursor()
try:
# 构建更新语句
if session['username'] == 'admin':
# admin用户可以更新所有字段
if password:
# 只验证密码不为空,不限制格式
cursor.execute("UPDATE login_info SET password = ?, otp_code = ? WHERE username = ?",
(password, otp_code, username))
else:
cursor.execute("UPDATE login_info SET otp_code = ? WHERE username = ?", (otp_code, username))
else:
# 普通用户只能更新自己的OTP码
cursor.execute("UPDATE login_info SET otp_code = ? WHERE username = ?", (otp_code, session['username']))
# 检查是否有行被更新
if cursor.rowcount == 0:
# 如果没有找到记录,则插入新记录
if session['username'] == 'admin':
# admin用户可以为任何用户创建登录信息
cursor.execute("INSERT INTO login_info (username, password, otp_code) VALUES (?, ?, ?)",
(username, password or 'default_password', otp_code))
else:
# 普通用户只能创建自己的登录信息
cursor.execute("INSERT INTO login_info (username, password, otp_code) VALUES (?, ?, ?)",
(session['username'], 'default_password', otp_code))
# 提交更改
conn.commit()
# 如果更新的是当前登录用户的信息,更新session中的OTP码
if username == session['username']:
session['otp_code'] = otp_code
conn.close()
return jsonify({'success': True, 'message': '登录信息更新成功'})
except Exception as e:
conn.close()
return jsonify({'success': False, 'error': f'更新失败:{str(e)}'})
# 登出路由 # 登出路由
@bp.route('/logout') @bp.route('/logout')
@@ -535,13 +421,7 @@ def reset_user_password():
# 更新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))
# 更新login_info表中的密码
cursor.execute("UPDATE login_info SET password = ? WHERE username = ?", (default_password, username))
# 如果login_info表中没有该用户的记录,则插入
if cursor.rowcount == 0:
cursor.execute("INSERT INTO login_info (username, password, otp_code) VALUES (?, ?, ?)",
(username, default_password, ""))
conn.commit() conn.commit()
conn.close() conn.close()
@@ -549,4 +429,255 @@ def reset_user_password():
return jsonify({'success': True, 'message': '密码已初始化为:' + default_password}) return jsonify({'success': True, 'message': '密码已初始化为:' + default_password})
except Exception as e: except Exception as e:
conn.close() conn.close()
return jsonify({'success': False, 'error': f'密码重置失败:{str(e)}'}) return jsonify({'success': False, 'error': f'密码重置失败:{str(e)}'})
# 创建参数配置表(如果不存在)
def create_parameter_config_table():
conn = sqlite3.connect('car_info.db')
cursor = conn.cursor()
# 创建参数配置表
cursor.execute('''
CREATE TABLE IF NOT EXISTS parameter_config (
id INTEGER PRIMARY KEY AUTOINCREMENT,
param1 TEXT,
param2 TEXT,
param3 TEXT,
param4 TEXT,
param5 TEXT,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# 检查是否存在参数配置记录,如果不存在则插入第一条记录
cursor.execute("SELECT COUNT(*) FROM parameter_config")
count = cursor.fetchone()[0]
if count == 0:
cursor.execute(
"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'))
)
conn.commit()
conn.close()
# 获取参数配置路由
@bp.route('/get_parameter_config', methods=['GET'])
def get_parameter_config():
if 'username' not in session:
return jsonify({'success': False, 'error': '未登录'}), 401
try:
conn = sqlite3.connect('car_info.db')
cursor = conn.cursor()
# 查询参数配置
cursor.execute("SELECT param1, param2, param3, param4, param5, update_time FROM parameter_config ORDER BY update_time DESC LIMIT 1")
config = cursor.fetchone()
conn.close()
if not config:
return jsonify({'success': False, 'error': '未找到参数配置'})
# 构造返回数据
parameters = {
'param1': config[0],
'param2': config[1],
'param3': config[2],
'param4': config[3],
'param5': config[4],
'update_time': config[5]
}
return jsonify({'success': True, 'parameters': parameters})
except Exception as e:
return jsonify({'success': False, 'error': f'获取参数配置失败:{str(e)}'})
# 保存参数配置路由
@bp.route('/save_parameter_config', methods=['POST'])
def save_parameter_config():
if 'username' not in session:
return jsonify({'success': False, 'error': '未登录'}), 401
# 只有管理员可以修改参数配置
if session['username'] != 'admin':
return jsonify({'success': False, 'error': '无权限修改参数配置'}), 403
# 获取请求数据
data = request.get_json()
if not data:
return jsonify({'success': False, 'error': '请求数据格式错误'}), 400
param1 = data.get('param1', '').strip()
param2 = data.get('param2', '').strip()
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:
conn = sqlite3.connect('car_info.db')
cursor = conn.cursor()
# 更新参数配置
cursor.execute(
"UPDATE parameter_config SET param1 = ?, param2 = ?, param3 = ?, param4 = ?, param5 = ?, update_time = ? WHERE id = 1",
(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.close()
return jsonify({'success': True, 'message': '参数配置保存成功'})
except Exception as e:
return jsonify({'success': False, 'error': f'保存参数配置失败:{str(e)}'})
# 参数配置页面路由
@bp.route('/parameter_config_page', methods=['GET', 'POST'])
def parameter_config_page():
if 'username' not in session:
return redirect(url_for('user.login'))
# 只有管理员可以访问参数配置页面
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'])
def change_password_page():
if 'username' not in session:
return redirect(url_for('user.login'))
if request.method == 'POST':
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 render_template('change_password.html', error='请填写所有必填字段')
# 验证新密码和确认密码是否一致
if new_password != confirm_password:
return render_template('change_password.html', 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 render_template('change_password.html', 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 render_template('change_password.html', error='旧密码不正确')
# 更新密码
cursor.execute("UPDATE users SET password = ? WHERE username = ?", (new_password, session['username']))
conn.commit()
conn.close()
# 密码修改成功,先执行退出功能,再跳转到登录页面
session.clear()
return redirect(url_for('user.login', success='修改密码成功,请重新登录'))
# GET请求,渲染修改密码页面
return render_template('change_password.html', current_year=datetime.now().year)
# 个人信息页面路由
@bp.route('/user_profile_page', methods=['GET', 'POST'])
def user_profile_page():
if 'username' not in session:
return redirect(url_for('user.login'))
# 连接数据库,获取用户信息
conn = sqlite3.connect('car_info.db')
cursor = conn.cursor()
cursor.execute("SELECT username, name, phone, department FROM users WHERE username = ?", (session['username'],))
user_tuple = cursor.fetchone()
conn.close()
if not user_tuple:
return redirect(url_for('user.login', error='用户不存在'))
# 将元组转换为字典,以便在模板中使用属性访问
user = {
'username': user_tuple[0],
'name': user_tuple[1],
'phone': user_tuple[2],
'department': user_tuple[3]
}
if request.method == 'POST':
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 render_template('user_profile.html', user=user, error='请填写所有必填字段', current_year=datetime.now().year)
# 连接数据库,更新用户信息
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()
# 更新会话中的部门信息
session['department'] = department
session['name'] = name
conn.close()
# 重新获取用户信息
conn = sqlite3.connect('car_info.db')
cursor = conn.cursor()
cursor.execute("SELECT username, name, phone, department FROM users WHERE username = ?", (session['username'],))
updated_user_tuple = cursor.fetchone()
conn.close()
# 将元组转换为字典
updated_user = {
'username': updated_user_tuple[0],
'name': updated_user_tuple[1],
'phone': updated_user_tuple[2],
'department': updated_user_tuple[3]
}
return render_template('user_profile.html', user=updated_user, success='个人信息更新成功', current_year=datetime.now().year)
except Exception as e:
conn.close()
return render_template('user_profile.html', user=user, error=f'更新失败,请重试。错误:{str(e)}', current_year=datetime.now().year)
# GET请求,渲染个人信息页面
return render_template('user_profile.html', user=user, current_year=datetime.now().year)