20250819corp

This commit is contained in:
2025-09-19 16:47:15 +08:00
parent fc5fb355b4
commit b0e8a68204
12 changed files with 1148 additions and 809 deletions
-214
View File
@@ -1,214 +0,0 @@
// 通用工具函数
/**
* 显示模态框
* @param {string} modalId - 模态框的ID
*/
function showModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.remove('hidden');
}
}
/**
* 关闭模态框
* @param {string} modalId - 模态框的ID
*/
function closeModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.add('hidden');
}
}
/**
* 绑定模态框点击外部关闭事件
* @param {string} modalId - 模态框的ID
*/
function bindModalOutsideClick(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.addEventListener('click', function(e) {
if (e.target === this) {
closeModal(modalId);
}
});
}
}
/**
* 显示消息提示
* @param {string} message - 消息内容
* @param {boolean} isSuccess - 是否为成功消息
* @param {number} duration - 消息显示时长(毫秒)
*/
function showMessage(message, isSuccess = true, duration = 5000) {
const messageContainer = document.getElementById('messageContainer');
const successMessage = document.getElementById('successMessage');
const errorMessage = document.getElementById('errorMessage');
if (!messageContainer || !successMessage || !errorMessage) return;
if (isSuccess) {
successMessage.querySelector('span').textContent = message;
successMessage.classList.remove('hidden');
errorMessage.classList.add('hidden');
} else {
errorMessage.querySelector('span').textContent = message;
errorMessage.classList.remove('hidden');
successMessage.classList.add('hidden');
}
messageContainer.classList.remove('hidden');
// 自动隐藏
setTimeout(() => {
(isSuccess ? successMessage : errorMessage).classList.add('hidden');
messageContainer.classList.add('hidden');
}, duration);
}
/**
* 显示表单错误消息
* @param {string} errorId - 错误容器的ID
* @param {string} messageId - 错误消息的ID
* @param {string} message - 错误消息内容
* @param {string} successId - 成功消息容器的ID
*/
function showFormError(errorId, messageId, message, successId = '') {
const errorContainer = document.getElementById(errorId);
const errorMessage = document.getElementById(messageId);
const successContainer = successId ? document.getElementById(successId) : null;
if (!errorContainer || !errorMessage) return;
errorMessage.textContent = message;
errorContainer.classList.remove('hidden');
if (successContainer) {
successContainer.classList.add('hidden');
}
// 滚动到错误信息
errorContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
/**
* 显示表单成功消息
* @param {string} successId - 成功容器的ID
* @param {string} messageId - 成功消息的ID
* @param {string} message - 成功消息内容
* @param {string} errorId - 错误消息容器的ID
*/
function showFormSuccess(successId, messageId, message, errorId = '') {
const successContainer = document.getElementById(successId);
const successMessage = document.getElementById(messageId);
const errorContainer = errorId ? document.getElementById(errorId) : null;
if (!successContainer || !successMessage) return;
successMessage.textContent = message;
successContainer.classList.remove('hidden');
if (errorContainer) {
errorContainer.classList.add('hidden');
}
// 滚动到成功信息
successContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
/**
* 发起API请求的通用函数
* @param {string} url - 请求URL
* @param {string} method - 请求方法
* @param {object} data - 请求数据
* @param {object} options - 附加选项
* @returns {Promise} - 返回Promise对象
*/
async function apiRequest(url, method = 'GET', data = null, options = {}) {
const defaultOptions = {
headers: {
'Content-Type': 'application/json'
},
method,
...options
};
if (data) {
// 根据Content-Type处理数据
if (defaultOptions.headers['Content-Type'] === 'application/json') {
defaultOptions.body = JSON.stringify(data);
} else if (defaultOptions.headers['Content-Type'] === 'application/x-www-form-urlencoded') {
defaultOptions.body = new URLSearchParams(data);
}
}
try {
const response = await fetch(url, defaultOptions);
return await response.json();
} catch (error) {
console.error(`API请求错误 [${url}]:`, error);
throw error;
}
}
/**
* 重置表单
* @param {string} formId - 表单ID
*/
function resetForm(formId) {
const form = document.getElementById(formId);
if (form) {
form.reset();
}
}
/**
* 填充表单数据
* @param {string} formId - 表单ID
* @param {object} data - 表单数据对象
*/
function fillForm(formId, data) {
const form = document.getElementById(formId);
if (!form || !data) return;
Object.keys(data).forEach(key => {
const element = form.elements[key] || document.getElementById(key);
if (element) {
element.value = data[key] || '';
}
});
}
/**
* 防抖函数
* @param {Function} func - 要防抖的函数
* @param {number} delay - 延迟时间(毫秒)
* @returns {Function} - 返回防抖后的函数
*/
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
/**
* 节流函数
* @param {Function} func - 要节流的函数
* @param {number} limit - 时间限制(毫秒)
* @returns {Function} - 返回节流后的函数
*/
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
+220 -90
View File
@@ -105,7 +105,7 @@
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">查询日期</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">车牌号</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">手机号</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">查询结果</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">状态</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">登录部门</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">登录IP</th>
</tr>
@@ -119,12 +119,18 @@
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ query.phone }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full
{% if query.results_count > 0 %}
{% if query.results_count == -1 %}
bg-warning/10 text-warning
{% elif query.results_count > 0 %}
bg-success/10 text-success
{% else %}
bg-error/10 text-error
{% endif %}">
{{ query.results_count }} 条记录
{% if query.results_count == -1 %}
待查询
{% else %}
{{ query.results_count }} 条记录
{% endif %}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ query.department }}</td>
@@ -219,59 +225,90 @@
// 等待DOM加载完成
document.addEventListener('DOMContentLoaded', function() {
// 获取DOM元素
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');
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');
var addQueryBtn = document.getElementById('addQueryBtn');
var addQueryModal = document.getElementById('addQueryModal');
var closeAddModal = document.getElementById('closeAddModal');
var cancelAddBtn = document.getElementById('cancelAddBtn');
var addQueryForm = document.getElementById('addQueryForm');
var addQueryLoading = document.getElementById('addQueryLoading');
var detailModal = document.getElementById('detailModal');
var closeModal = document.getElementById('closeModal');
var loadingState = document.getElementById('loadingState');
var noResultState = document.getElementById('noResultState');
var resultList = document.getElementById('resultList');
var carOwnerCards = document.getElementById('carOwnerCards');
// 为查询历史记录行添加点击事件
document.querySelectorAll('tr[data-history-id]').forEach(row => {
row.addEventListener('click', function() {
const historyId = this.getAttribute('data-history-id');
var rows = document.querySelectorAll('tr[data-history-id]');
for (var i = 0; i < rows.length; i++) {
rows[i].addEventListener('click', function() {
var historyId = this.getAttribute('data-history-id');
loadHistoryDetail(historyId);
});
});
}
// 增加查询按钮点击事件
addQueryBtn.addEventListener('click', function() {
addQueryModal.classList.remove('hidden');
// 兼容classList API
if (addQueryModal.classList) {
addQueryModal.classList.remove('hidden');
} else {
addQueryModal.className = addQueryModal.className.replace('hidden', '');
}
addQueryForm.reset();
});
// 关闭增加查询模态框
closeAddModal.addEventListener('click', function() {
addQueryModal.classList.add('hidden');
// 兼容classList API
if (addQueryModal.classList) {
addQueryModal.classList.add('hidden');
} else {
addQueryModal.className += ' hidden';
}
});
// 取消增加查询
cancelAddBtn.addEventListener('click', function() {
addQueryModal.classList.add('hidden');
// 兼容classList API
if (addQueryModal.classList) {
addQueryModal.classList.add('hidden');
} else {
addQueryModal.className += ' hidden';
}
});
// 点击增加查询模态框背景关闭
addQueryModal.addEventListener('click', function(e) {
if (e.target === addQueryModal) {
addQueryModal.classList.add('hidden');
// 兼容classList API
if (addQueryModal.classList) {
addQueryModal.classList.add('hidden');
} else {
addQueryModal.className += ' hidden';
}
}
});
// 关闭详情模态框
closeModal.addEventListener('click', function() {
detailModal.classList.add('hidden');
// 兼容classList API
if (detailModal.classList) {
detailModal.classList.add('hidden');
} else {
detailModal.className += ' hidden';
}
});
// 点击详情模态框背景关闭
detailModal.addEventListener('click', function(e) {
if (e.target === detailModal) {
detailModal.classList.add('hidden');
// 兼容classList API
if (detailModal.classList) {
detailModal.classList.add('hidden');
} else {
detailModal.className += ' hidden';
}
}
});
@@ -279,8 +316,8 @@
addQueryForm.addEventListener('submit', function(e) {
e.preventDefault();
const plateNumber = document.getElementById('add_plate_number').value.trim();
const phone = document.getElementById('add_phone').value.trim();
var plateNumber = document.getElementById('add_plate_number').value.trim();
var phone = document.getElementById('add_phone').value.trim();
if (!plateNumber && !phone) {
alert('请至少输入车牌号或手机号');
@@ -288,107 +325,179 @@
}
// 显示加载状态
addQueryForm.classList.add('hidden');
addQueryLoading.classList.remove('hidden');
// 兼容classList API
if (addQueryForm.classList) {
addQueryForm.classList.add('hidden');
} else {
addQueryForm.className += ' hidden';
}
if (addQueryLoading.classList) {
addQueryLoading.classList.remove('hidden');
} else {
addQueryLoading.className = addQueryLoading.className.replace('hidden', '');
}
// 构建表单数据
const formData = new FormData();
var formData = new FormData();
formData.append('plate_number', plateNumber);
formData.append('phone', phone);
formData.append('is_add_query', 'true');
// 发送请求到查询路由
fetch('/query', {
method: 'POST',
body: formData
})
.then(response => {
.then(function(response) {
if (!response.ok) {
throw new Error('网络错误');
}
// 重新加载页面以显示新的查询记录
window.location.reload();
})
.catch(error => {
.catch(function(error) {
console.error('Error submitting query:', error);
alert('提交失败,请重试');
// 恢复表单显示
addQueryForm.classList.remove('hidden');
addQueryLoading.classList.add('hidden');
// 兼容classList API
if (addQueryForm.classList) {
addQueryForm.classList.remove('hidden');
} else {
addQueryForm.className = addQueryForm.className.replace('hidden', '');
}
if (addQueryLoading.classList) {
addQueryLoading.classList.add('hidden');
} else {
addQueryLoading.className += ' hidden';
}
});
});
// 加载查询历史详情
function loadHistoryDetail(historyId) {
// 显示模态框和加载状态
detailModal.classList.remove('hidden');
loadingState.classList.remove('hidden');
noResultState.classList.add('hidden');
resultList.classList.add('hidden');
// 兼容classList API
if (detailModal.classList) {
detailModal.classList.remove('hidden');
} else {
detailModal.className = detailModal.className.replace('hidden', '');
}
if (loadingState.classList) {
loadingState.classList.remove('hidden');
} else {
loadingState.className = loadingState.className.replace('hidden', '');
}
if (noResultState.classList) {
noResultState.classList.add('hidden');
} else {
noResultState.className += ' hidden';
}
if (resultList.classList) {
resultList.classList.add('hidden');
} else {
resultList.className += ' hidden';
}
carOwnerCards.innerHTML = '';
// 发送AJAX请求获取详情
fetch(`/query_history_detail/${historyId}`)
.then(response => response.json())
.then(data => {
fetch('/query_history_detail/' + historyId)
.then(function(response) {
return response.json();
})
.then(function(data) {
// 隐藏加载状态
loadingState.classList.add('hidden');
// 兼容classList API
if (loadingState.classList) {
loadingState.classList.add('hidden');
} else {
loadingState.className += ' hidden';
}
if (data.error) {
// 显示错误信息
noResultState.querySelector('p').textContent = data.error;
noResultState.classList.remove('hidden');
// 兼容classList API
if (noResultState.classList) {
noResultState.classList.remove('hidden');
} else {
noResultState.className = noResultState.className.replace('hidden', '');
}
} else if (data.car_owners && data.car_owners.length > 0) {
// 显示结果列表
resultList.classList.remove('hidden');
// 兼容classList API
if (resultList.classList) {
resultList.classList.remove('hidden');
} else {
resultList.className = resultList.className.replace('hidden', '');
}
// 添加车辆信息卡片
data.car_owners.forEach(carOwner => {
const card = document.createElement('div');
for (var i = 0; i < data.car_owners.length; i++) {
var carOwner = data.car_owners[i];
var card = document.createElement('div');
card.className = 'bg-white border border-gray-200 rounded-lg p-5 shadow-sm hover:shadow-md transition-shadow duration-200';
card.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>
`;
// 使用字符串拼接替代模板字符串
var cardHtml = '';
cardHtml += '<h4 class="text-lg font-medium text-gray-800 mb-4 flex items-center">';
cardHtml += ' <i class="fa fa-car text-primary mr-2"></i>';
cardHtml += ' 车辆信息';
cardHtml += '</h4>';
cardHtml += '<div class="space-y-3">';
cardHtml += ' <div class="flex justify-between items-center">';
cardHtml += ' <span class="text-gray-500">车牌号:</span>';
cardHtml += ' <span class="font-medium text-gray-800">' + (carOwner.plate_number || '') + '</span>';
cardHtml += ' </div>';
cardHtml += ' <div class="flex justify-between items-center">';
cardHtml += ' <span class="text-gray-500">手机号:</span>';
cardHtml += ' <span class="font-medium text-gray-800">' + (carOwner.phone || '') + '</span>';
cardHtml += ' </div>';
cardHtml += ' <div class="flex justify-between items-center">';
cardHtml += ' <span class="text-gray-500">车主姓名:</span>';
cardHtml += ' <span class="font-medium text-gray-800">' + (carOwner.name || '') + '</span>';
cardHtml += ' </div>';
cardHtml += ' <div class="flex justify-between items-center">';
cardHtml += ' <span class="text-gray-500">身份证号:</span>';
cardHtml += ' <span class="font-medium text-gray-800">' + (carOwner.id_card || '') + '</span>';
cardHtml += ' </div>';
cardHtml += ' <div class="flex justify-between items-center">';
cardHtml += ' <span class="text-gray-500">电子邮箱:</span>';
cardHtml += ' <span class="font-medium text-gray-800">' + (carOwner.email || '未提供') + '</span>';
cardHtml += ' </div>';
cardHtml += ' <div class="flex justify-between items-center">';
cardHtml += ' <span class="text-gray-500">居住地址:</span>';
cardHtml += ' <span class="font-medium text-gray-800">' + (carOwner.address || '未提供') + '</span>';
cardHtml += ' </div>';
cardHtml += '</div>';
card.innerHTML = cardHtml;
carOwnerCards.appendChild(card);
});
}
} else {
// 显示无结果状态
noResultState.classList.remove('hidden');
// 兼容classList API
if (noResultState.classList) {
noResultState.classList.remove('hidden');
} else {
noResultState.className = noResultState.className.replace('hidden', '');
}
}
})
.catch(error => {
.catch(function(error) {
// 隐藏加载状态,显示错误信息
loadingState.classList.add('hidden');
// 兼容classList API
if (loadingState.classList) {
loadingState.classList.add('hidden');
} else {
loadingState.className += ' hidden';
}
noResultState.querySelector('p').textContent = '加载详情时发生错误';
noResultState.classList.remove('hidden');
// 兼容classList API
if (noResultState.classList) {
noResultState.classList.remove('hidden');
} else {
noResultState.className = noResultState.className.replace('hidden', '');
}
console.error('Error loading history detail:', error);
});
}
@@ -396,17 +505,19 @@
// 响应式处理:当窗口大小改变时,重新绑定事件(因为表格可能会重新渲染)
window.addEventListener('resize', function() {
// 重新绑定事件到查询历史记录行
document.querySelectorAll('tr[data-history-id]').forEach(row => {
var rows = document.querySelectorAll('tr[data-history-id]');
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
// 先移除旧的事件监听器
const newRow = row.cloneNode(true);
var newRow = row.cloneNode(true);
row.parentNode.replaceChild(newRow, row);
// 添加新的事件监听器
newRow.addEventListener('click', function() {
const historyId = this.getAttribute('data-history-id');
var historyId = this.getAttribute('data-history-id');
loadHistoryDetail(historyId);
});
});
}
});
});
</script>
@@ -416,14 +527,33 @@
<script>
// 全局搜索变量
let searchQuery = '';
var searchQuery = '';
</script>
<script>
// ESC键关闭详情弹窗
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && !document.getElementById('detailModal').classList.contains('hidden')) {
document.getElementById('detailModal').classList.add('hidden');
// 使用keyCode替代key,提高兼容性
var key = e.key || e.keyCode;
var isEscape = key === 'Escape' || key === 'Esc' || key === 27;
var detailModal = document.getElementById('detailModal');
var isHidden = false;
// 兼容classList API
if (detailModal.classList) {
isHidden = detailModal.classList.contains('hidden');
} else {
isHidden = detailModal.className.indexOf('hidden') !== -1;
}
if (isEscape && !isHidden) {
// 兼容classList API
if (detailModal.classList) {
detailModal.classList.add('hidden');
} else {
detailModal.className += ' hidden';
}
}
});
</script>
+6 -7
View File
@@ -286,11 +286,11 @@
// 加载用户列表
function loadUsers() {
apiRequest('{{ url_for('user.get_users') }}')
apiRequest('/api/users')
.then(data => {
if (data.success) {
usersData = data.users;
totalUsers = usersData.length;
usersData = data.data;
totalUsers = data.total;
renderUsersTable();
} else {
showMessage('加载用户列表失败:' + (data.error || '未知错误'), false);
@@ -377,19 +377,18 @@
const filterDepartment = document.getElementById('filterDepartment').value;
const filterStatus = document.getElementById('filterStatus').value;
fetch('{{ url_for('user.get_users') }}')
.then(response => response.json())
apiRequest('/api/users')
.then(data => {
if (data.success) {
// 应用过滤条件
usersData = data.users.filter(user => {
usersData = data.data.filter(user => {
const matchesUsername = user.username.toLowerCase().includes(searchUsername);
const matchesDepartment = !filterDepartment || user.department === filterDepartment;
const matchesStatus = !filterStatus || user.status === filterStatus;
return matchesUsername && matchesDepartment && matchesStatus;
});
totalUsers = usersData.length;
totalUsers = data.total;
currentPage = 1; // 重置到第一页
renderUsersTable();
} else {