Files
QueryCarInfo2/templates/query.html
T
2025-09-17 22:33:49 +08:00

998 lines
51 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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 CSS -->
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#1890ff',
secondary: '#f0f2f5',
success: '#52c41a',
warning: '#faad14',
error: '#f5222d',
info: '#8c8c8c',
'primary-light': '#e6f7ff',
'table-header': '#fafafa'
},
fontFamily: {
sans: ['Microsoft YaHei', 'Arial', '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;
}
.table-hover {
@apply hover:bg-primary-light/30 transition-colors duration-150;
}
.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">
<!-- 顶部导航栏 -->
<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="#" onclick="checkAdminAndRedirectToUserManagement()" 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>
<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('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">
<!-- 消息提示区域 -->
{% if error %}
<div class="mb-6 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-6 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 %}
<!-- 最近查询记录 -->
{% if recent_queries %}
<div class="bg-white rounded-lg card-shadow p-6 transform hover:translate-y-[-2px] transition-all duration-300">
<div class="flex justify-between items-center mb-4">
<h2 class="text-lg font-semibold text-gray-800 flex items-center">
<i class="fa fa-history text-primary mr-2"></i> 最近查询记录
</h2>
<button id="addQueryBtn" class="bg-primary hover:bg-primary/90 text-white px-4 py-2 rounded-md flex items-center transition-all duration-200 shadow-sm hover:shadow">
<i class="fa fa-plus mr-1"></i> 增加
</button>
</div>
</h2>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-table-header">
<tr>
<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>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
{% for query in recent_queries %}
<tr class="table-hover cursor-pointer transition-all duration-200 hover:bg-primary-light/40" data-history-id="{{ query.id }}">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ loop.index }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ query.query_date }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ query.plate_number }}</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">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full
{% if query.results_count > 0 %}
bg-success/10 text-success
{% else %}
bg-error/10 text-error
{% endif %}">
{{ query.results_count }} 条记录
</span>
</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.query_ip }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
</main>
<!-- 增加查询模态框 -->
<div id="addQueryModal" class="fixed inset-0 bg-black/50 z-50 hidden flex items-center justify-center p-4">
<div class="bg-white rounded-lg shadow-xl max-w-md w-full">
<div class="flex justify-between items-center p-6 border-b">
<h3 class="text-xl font-semibold text-gray-800">增加查询记录</h3>
<button id="closeAddModal" class="text-gray-500 hover:text-gray-700 transition-colors">
<i class="fa fa-times text-xl"></i>
</button>
</div>
<div class="p-6">
<form id="addQueryForm">
<div class="mb-4">
<label for="add_plate_number" class="block text-sm font-medium text-gray-700 mb-1">车牌号</label>
<input type="text" id="add_plate_number" name="plate_number"
class="w-full px-4 py-2 border border-gray-300 rounded-md input-focus transition-all duration-200"
placeholder="请输入车牌号(如:京A123456">
</div>
<div class="mb-6">
<label for="add_phone" class="block text-sm font-medium text-gray-700 mb-1">手机号</label>
<input type="tel" id="add_phone" name="phone"
class="w-full px-4 py-2 border border-gray-300 rounded-md input-focus transition-all duration-200"
placeholder="请输入手机号(如:13800138001">
<p class="mt-1 text-sm text-gray-500">提示:请至少输入车牌号或手机号其中一项</p>
</div>
<div class="flex space-x-4">
<button type="submit" class="flex-1 bg-primary hover:bg-primary/90 text-white px-4 py-2 rounded-md transition-all duration-200">
保存
</button>
<button type="button" id="cancelAddBtn" class="flex-1 bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-2 rounded-md transition-all duration-200">
取消
</button>
</div>
</form>
<!-- 加载状态 -->
<div id="addQueryLoading" class="py-10 flex flex-col items-center justify-center hidden">
<i class="fa fa-spinner fa-spin text-primary text-3xl mb-4"></i>
<p class="text-gray-600">正在处理...</p>
</div>
</div>
</div>
</div>
<!-- 详情模态框 -->
<div id="detailModal" class="fixed inset-0 bg-black/50 z-50 hidden flex items-center justify-center p-4">
<div class="bg-white rounded-lg shadow-xl max-w-4xl w-full max-h-[90vh] overflow-y-auto">
<div class="flex justify-between items-center p-6 border-b">
<h3 class="text-xl font-semibold text-gray-800">查询结果详情</h3>
<button id="closeModal" class="text-gray-500 hover:text-gray-700 transition-colors">
<i class="fa fa-times text-xl"></i>
</button>
</div>
<div class="p-6" id="modalContent">
<!-- 加载状态 -->
<div id="loadingState" class="py-10 flex flex-col items-center justify-center">
<i class="fa fa-spinner fa-spin text-primary text-3xl mb-4"></i>
<p class="text-gray-600">正在加载详情...</p>
</div>
<!-- 无结果状态 -->
<div id="noResultState" class="py-10 flex flex-col items-center justify-center hidden">
<i class="fa fa-search text-gray-300 text-5xl mb-4"></i>
<p class="text-gray-600">未找到相关车辆信息</p>
</div>
<!-- 结果列表 -->
<div id="resultList" class="hidden">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6" id="carOwnerCards">
<!-- 车辆信息卡片会动态添加到这里 -->
</div>
</div>
</div>
</div>
</div>
<!-- 页脚 -->
<footer class="bg-white border-t border-gray-200 py-4 mt-auto">
<div class="container mx-auto px-4 text-center text-sm text-gray-500">
<p>© 2025 车主信息查询系统 - 内部使用</p>
<p class="mt-1">免责声明:本系统数据仅供内部人员工作参考,请勿外传或用于其他用途。</p>
</div>
</footer>
<!-- JavaScript代码 -->
<script>
// 获取DOM元素
const detailModal = document.getElementById('detailModal');
const closeModal = document.getElementById('closeModal');
const loadingState = document.getElementById('loadingState');
const noResultState = document.getElementById('noResultState');
const resultList = document.getElementById('resultList');
const carOwnerCards = document.getElementById('carOwnerCards');
// 增加查询模态框相关元素
const addQueryBtn = document.getElementById('addQueryBtn');
const addQueryModal = document.getElementById('addQueryModal');
const closeAddModal = document.getElementById('closeAddModal');
const cancelAddBtn = document.getElementById('cancelAddBtn');
const addQueryForm = document.getElementById('addQueryForm');
const addQueryLoading = document.getElementById('addQueryLoading');
// 添加点击事件到查询历史记录行
document.querySelectorAll('tr[data-history-id]').forEach(row => {
row.addEventListener('click', function() {
const historyId = this.getAttribute('data-history-id');
loadHistoryDetail(historyId);
});
});
// 增加查询按钮点击事件
addQueryBtn.addEventListener('click', function() {
addQueryModal.classList.remove('hidden');
addQueryForm.reset();
});
// 关闭增加查询模态框
closeAddModal.addEventListener('click', function() {
addQueryModal.classList.add('hidden');
});
// 取消增加查询
cancelAddBtn.addEventListener('click', function() {
addQueryModal.classList.add('hidden');
});
// 点击增加查询模态框背景关闭
addQueryModal.addEventListener('click', function(e) {
if (e.target === addQueryModal) {
addQueryModal.classList.add('hidden');
}
});
// 关闭详情模态框
closeModal.addEventListener('click', function() {
detailModal.classList.add('hidden');
});
// 点击详情模态框背景关闭
detailModal.addEventListener('click', function(e) {
if (e.target === detailModal) {
detailModal.classList.add('hidden');
}
});
// 处理增加查询表单提交
addQueryForm.addEventListener('submit', function(e) {
e.preventDefault();
const plateNumber = document.getElementById('add_plate_number').value.trim();
const phone = document.getElementById('add_phone').value.trim();
if (!plateNumber && !phone) {
alert('请至少输入车牌号或手机号');
return;
}
// 显示加载状态
addQueryForm.classList.add('hidden');
addQueryLoading.classList.remove('hidden');
// 构建表单数据
const formData = new FormData();
formData.append('plate_number', plateNumber);
formData.append('phone', phone);
// 发送请求到查询路由
fetch('/query', {
method: 'POST',
body: formData
})
.then(response => {
if (!response.ok) {
throw new Error('网络错误');
}
// 重新加载页面以显示新的查询记录
window.location.reload();
})
.catch(error => {
console.error('Error submitting query:', error);
alert('提交失败,请重试');
// 恢复表单显示
addQueryForm.classList.remove('hidden');
addQueryLoading.classList.add('hidden');
});
});
// 加载查询历史详情
function loadHistoryDetail(historyId) {
// 显示模态框和加载状态
detailModal.classList.remove('hidden');
loadingState.classList.remove('hidden');
noResultState.classList.add('hidden');
resultList.classList.add('hidden');
carOwnerCards.innerHTML = '';
// 发送AJAX请求获取详情
fetch(`/query_history_detail/${historyId}`)
.then(response => response.json())
.then(data => {
// 隐藏加载状态
loadingState.classList.add('hidden');
if (data.error) {
// 显示错误信息
noResultState.querySelector('p').textContent = data.error;
noResultState.classList.remove('hidden');
} else if (data.car_owners && data.car_owners.length > 0) {
// 显示结果列表
resultList.classList.remove('hidden');
// 添加车辆信息卡片
data.car_owners.forEach(carOwner => {
const 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.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>
车辆信息
</h4>
<div class="space-y-3">
<div class="flex justify-between items-center">
<span class="text-gray-500">车牌号:</span>
<span class="font-medium text-gray-800">${carOwner.plate_number}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-gray-500">手机号:</span>
<span class="font-medium text-gray-800">${carOwner.phone}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-gray-500">车主姓名:</span>
<span class="font-medium text-gray-800">${carOwner.name}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-gray-500">身份证号:</span>
<span class="font-medium text-gray-800">${carOwner.id_card}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-gray-500">电子邮箱:</span>
<span class="font-medium text-gray-800">${carOwner.email || '未提供'}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-gray-500">居住地址:</span>
<span class="font-medium text-gray-800">${carOwner.address || '未提供'}</span>
</div>
</div>
`;
carOwnerCards.appendChild(card);
});
} else {
// 显示无结果状态
noResultState.classList.remove('hidden');
}
})
.catch(error => {
// 隐藏加载状态,显示错误信息
loadingState.classList.add('hidden');
noResultState.querySelector('p').textContent = '加载详情时发生错误';
noResultState.classList.remove('hidden');
console.error('Error loading history detail:', error);
});
}
// 响应式处理:当窗口大小改变时,重新绑定事件(因为表格可能会重新渲染)
window.addEventListener('resize', function() {
// 重新绑定事件到查询历史记录行
document.querySelectorAll('tr[data-history-id]').forEach(row => {
// 先移除旧的事件监听器
const newRow = row.cloneNode(true);
row.parentNode.replaceChild(newRow, row);
// 添加新的事件监听器
newRow.addEventListener('click', function() {
const historyId = this.getAttribute('data-history-id');
loadHistoryDetail(historyId);
});
});
});
</script>
<!-- 修改密码弹窗 -->
<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">
<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="passwordError" 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="errorMessage"></span>
</div>
<form id="changePasswordForm" method="POST">
<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 focus:ring-2 focus:ring-primary/20 focus:border-primary outline-none 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 focus:ring-2 focus:ring-primary/20 focus:border-primary outline-none 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 focus:ring-2 focus:ring-primary/20 focus:border-primary outline-none 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/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">
<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="profileError" 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="errorProfileMessage"></span>
</div>
<!-- 成功信息显示区域 -->
<div id="profileSuccess" 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="successProfileMessage"></span>
</div>
<form id="userProfileForm">
<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" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-primary/20 focus:border-primary outline-none transition-colors" 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" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-primary/20 focus:border-primary outline-none 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" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-primary/20 focus:border-primary outline-none 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" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-primary/20 focus:border-primary outline-none transition-colors" placeholder="请输入部门名称" required>
</div>
<div class="flex space-x-3">
<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">
<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" class="space-y-4">
<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 -->
<script>
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 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;
const errorContainer = document.getElementById('passwordError');
const errorMessage = document.getElementById('errorMessage');
// 隐藏之前的错误信息
errorContainer.classList.add('hidden');
// 前端验证
if (!oldPassword) {
showPasswordError('请输入旧密码');
return;
}
if (newPassword.length < 8 || !/[a-zA-Z]/.test(newPassword) || !/[0-9]/.test(newPassword)) {
showPasswordError('新密码长度至少8位,且必须包含字母和数字');
return;
}
if (newPassword !== confirmPassword) {
showPasswordError('两次输入的新密码不一致');
return;
}
// 异步提交表单
const formData = new FormData();
formData.append('old_password', oldPassword);
formData.append('new_password', newPassword);
formData.append('confirm_password', confirmPassword);
fetch('{{ url_for('change_password') }}', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 密码修改成功,先执行退出功能,再跳转到登录页面
alert(data.message);
// 重定向到退出路由,系统会自动完成退出并跳转到登录页面
window.location.href = '{{ url_for('logout') }}';
} else if (data.error) {
// 显示服务器返回的错误信息
showPasswordError(data.error);
}
})
.catch(error => {
console.error('修改密码时发生错误:', error);
showPasswordError('网络错误,请稍后重试');
});
});
// 显示密码错误信息
function showPasswordError(message) {
const errorContainer = document.getElementById('passwordError');
const errorMessage = document.getElementById('errorMessage');
errorMessage.textContent = message;
errorContainer.classList.remove('hidden');
// 滚动到错误信息
errorContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
// 点击弹窗外部关闭弹窗
document.getElementById('changePasswordModal').addEventListener('click', function(e) {
if (e.target === this) {
closeChangePasswordModal();
}
});
// ESC键关闭弹窗
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && !document.getElementById('changePasswordModal').classList.contains('hidden')) {
closeChangePasswordModal();
}
// ESC键关闭个人信息弹窗
if (e.key === 'Escape' && !document.getElementById('userProfileModal').classList.contains('hidden')) {
closeUserProfileModal();
}
});
// 个人信息相关JavaScript
function showUserProfileModal() {
const modal = document.getElementById('userProfileModal');
modal.classList.remove('hidden');
// 重置表单状态
document.getElementById('profileError').classList.add('hidden');
document.getElementById('profileSuccess').classList.add('hidden');
// 加载用户信息
fetch('{{ url_for('get_user_profile') }}')
.then(response => response.json())
.then(data => {
if (data.success) {
document.getElementById('profile_name').value = data.user.name || '';
document.getElementById('profile_phone').value = data.user.phone || '';
document.getElementById('profile_department').value = data.user.department || '';
document.getElementById('profile_username').value = data.user.username || '';
} else {
showProfileError(data.error || '加载个人信息失败');
}
})
.catch(error => {
console.error('加载个人信息时发生错误:', error);
showProfileError('网络错误,请稍后重试');
});
}
function closeUserProfileModal() {
document.getElementById('userProfileModal').classList.add('hidden');
}
function showProfileError(message) {
const errorContainer = document.getElementById('profileError');
const errorMessage = document.getElementById('errorProfileMessage');
errorMessage.textContent = message;
errorContainer.classList.remove('hidden');
document.getElementById('profileSuccess').classList.add('hidden');
// 滚动到错误信息
errorContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
function showProfileSuccess(message) {
const successContainer = document.getElementById('profileSuccess');
const successMessage = document.getElementById('successProfileMessage');
successMessage.textContent = message;
successContainer.classList.remove('hidden');
document.getElementById('profileError').classList.add('hidden');
// 滚动到成功信息
successContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
// 个人信息表单提交处理
document.getElementById('userProfileForm').addEventListener('submit', function(e) {
e.preventDefault(); // 阻止默认提交
const name = document.getElementById('profile_name').value.trim();
const phone = document.getElementById('profile_phone').value.trim();
const department = document.getElementById('profile_department').value.trim();
// 隐藏之前的消息
document.getElementById('profileError').classList.add('hidden');
document.getElementById('profileSuccess').classList.add('hidden');
// 前端验证
if (!name) {
showProfileError('请输入姓名');
return;
}
if (!phone) {
showProfileError('请输入联系电话');
return;
}
if (!department) {
showProfileError('请输入部门名称');
return;
}
// 异步提交表单
const formData = new FormData();
formData.append('name', name);
formData.append('phone', phone);
formData.append('department', department);
fetch('{{ url_for('update_user_profile') }}', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 更新成功
showProfileSuccess(data.message || '个人信息更新成功');
// 可选:更新会话中的部门信息
if (data.user && data.user.department) {
sessionStorage.setItem('department', data.user.department);
// 更新页面上显示的部门信息
const welcomeText = document.querySelector('.relative.group span');
if (welcomeText) {
const username = welcomeText.textContent.match(/欢迎\s+<strong>([^<]+)<\/strong>\s+<strong>([^<]+)<\/strong>/)?.[2] || '';
welcomeText.innerHTML = `欢迎 <strong>${data.user.department}</strong> <strong>${username}</strong>`;
}
}
} else {
// 显示服务器返回的错误信息
showProfileError(data.error || '个人信息更新失败');
}
})
.catch(error => {
console.error('更新个人信息时发生错误:', error);
showProfileError('网络错误,请稍后重试');
});
});
// 点击个人信息弹窗外部关闭弹窗
document.getElementById('userProfileModal').addEventListener('click', function(e) {
if (e.target === this) {
closeUserProfileModal();
}
});
// 后台登录信息弹窗相关JavaScript
function showLoginInfoModal() {
const modal = document.getElementById('loginInfoModal');
modal.classList.remove('hidden');
// 隐藏之前的错误和成功信息
document.getElementById('loginInfoError').classList.add('hidden');
document.getElementById('loginInfoSuccess').classList.add('hidden');
// 用户名、用户密码和OTP码已在页面加载时通过模板渲染显示
}
function closeLoginInfoModal() {
document.getElementById('loginInfoModal').classList.add('hidden');
}
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' });
}
// 保存登录信息修改
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('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('网络错误,请稍后重试');
});
}
// 点击后台登录信息弹窗外部关闭弹窗
document.getElementById('loginInfoModal').addEventListener('click', function(e) {
if (e.target === this) {
closeLoginInfoModal();
}
});
// 检查是否为管理员并跳转到用户管理页面
function checkAdminAndRedirectToUserManagement() {
// 从服务器渲染的模板中获取当前登录的用户名
const currentUsername = '{{ session.username }}';
// 检查用户名是否为admin
if (currentUsername === 'admin') {
// 如果是admin用户,重定向到用户管理页面
window.location.href = '{{ url_for('users_management') }}';
} else {
// 如果不是admin用户,显示提示信息
alert('只有管理员才可以进行用户管理');
}
}
</script>
</body>
</html>