Files
qiye/mobile/main.js
MeSHard ca6f1086b0 1
2025-11-14 17:34:39 +08:00

543 lines
22 KiB
JavaScript
Raw Permalink 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.

// 危化品车辆监控系统(简化版)- 主要JavaScript文件
// 模拟数据
const vehicleStats = {
total: 156,
todayApplications: 23,
activeOrders: 89,
todayNew: 5,
approved: 18,
pending: 5,
transporting: 67,
loading: 22
};
const alertsData = [
{ id: 1, vehicleId: '京A12345', type: '违停', location: '化工园区A区停车场', time: '2024-01-15 14:25', description: '京A12345违停在化工园区A区停车场2024-01-15 14:25触发', status: 'unread' },
{ id: 2, vehicleId: '京B67890', type: '超速', location: 'G6高速K125+300', time: '2024-01-15 13:40', description: '京B67890超速在G6高速K125+3002024-01-15 13:40触发', status: 'unread' },
{ id: 3, vehicleId: '京C24680', type: '违停', location: '危险品仓库门口', time: '2024-01-15 12:15', description: '京C24680违停在危险品仓库门口2024-01-15 12:15触发', status: 'unread' },
{ id: 4, vehicleId: '京D13579', type: '超速', location: 'S12省道K45+200', time: '2024-01-15 11:30', description: '京D13579超速在S12省道K45+2002024-01-15 11:30触发', status: 'read' },
{ id: 5, vehicleId: '京E86420', type: '违停', location: '化学品储罐区', time: '2024-01-15 10:45', description: '京E86420违停在化学品储罐区2024-01-15 10:45触发', status: 'read' },
{ id: 6, vehicleId: '京F97531', type: '超速', location: '化工园区主干道', time: '2024-01-15 09:20', description: '京F97531超速在化工园区主干道2024-01-15 09:20触发', status: 'read' }
];
const trajectoryData = {
'京A12345': [
{ time: '08:30', location: '化工园区A区', status: '出发', type: 'normal' },
{ time: '09:15', location: '高速入口', status: '停留15分钟', type: 'stop' },
{ time: '10:45', location: '服务区', status: '停留30分钟', type: 'stop' },
{ time: '11:00', location: 'G6高速K125', status: '超速报警', type: 'alert' },
{ time: '11:30', location: '检查站', status: '停留20分钟', type: 'stop' },
{ time: '12:00', location: '危险品仓库', status: '到达', type: 'normal' }
],
'京B67890': [
{ time: '09:00', location: '化工厂', status: '出发', type: 'normal' },
{ time: '09:45', location: '化工园区B区', status: '装卸货物', type: 'stop' },
{ time: '10:30', location: '高速入口', status: '正常行驶', type: 'normal' },
{ time: '11:15', location: '服务区', status: '停留10分钟', type: 'stop' },
{ time: '12:30', location: '储运中心', status: '到达', type: 'normal' }
],
'京C24680': [
{ time: '07:30', location: '储运中心', status: '出发', type: 'normal' },
{ time: '08:15', location: '检查站', status: '检查通过', type: 'normal' },
{ time: '09:00', location: '高速入口', status: '正常行驶', type: 'normal' },
{ time: '10:20', location: '化工园区', status: '违停报警', type: 'alert' },
{ time: '11:00', location: '化学品仓库', status: '到达', type: 'normal' }
]
};
// 全局变量
let currentAlertFilter = 'all';
let recentSearches = ['京A12345', '京B67890', '京C24680'];
// 导航函数
function navigateToHome() {
window.location.href = 'index.html';
}
function navigateToMonitor() {
window.location.href = 'monitor.html';
}
function navigateToTracking() {
window.location.href = 'tracking.html';
}
function goBack() {
window.history.back();
}
// 首页功能
function loadRecentAlerts() {
const container = document.getElementById('recent-alerts');
if (!container) return;
const recentAlerts = alertsData.slice(0, 4);
container.innerHTML = recentAlerts.map(alert => {
const statusColor = alert.status === 'unread' ? 'text-red-600' : 'text-gray-500';
const statusDot = alert.status === 'unread' ? 'bg-red-500' : 'bg-gray-300';
const statusText = alert.status === 'unread' ? '未查看' : '已查看';
return `
<div class="bg-white rounded-lg shadow-sm p-3 ${alert.status === 'unread' ? 'alert-unread' : 'alert-read'}" onclick="toggleAlertStatus(${alert.id})">
<div class="flex items-start space-x-3">
<div class="w-2 h-2 ${statusDot} rounded-full mt-2"></div>
<div class="flex-1">
<div class="flex items-center justify-between mb-1">
<span class="text-sm font-medium ${statusColor}">${alert.vehicleId}</span>
<span class="text-xs ${statusColor}">${statusText}</span>
</div>
<p class="text-sm text-gray-700 mb-1">${alert.description}</p>
<div class="flex items-center justify-between text-xs text-gray-400">
<span>${alert.location}</span>
<span>${alert.time.split(' ')[1]}</span>
</div>
</div>
</div>
</div>
`;
}).join('');
}
function animateStatistics() {
const stats = [
{ id: 'total-vehicles', value: vehicleStats.total },
{ id: 'today-applications', value: vehicleStats.todayApplications },
{ id: 'active-orders', value: vehicleStats.activeOrders }
];
stats.forEach(stat => {
const element = document.getElementById(stat.id);
if (element) {
anime({
targets: { value: 0 },
value: stat.value,
duration: 2000,
easing: 'easeOutQuart',
update: function(anim) {
element.textContent = Math.round(anim.animatables[0].target.value);
}
});
}
});
}
function showVehicleDetail(type) {
const modal = document.getElementById('stats-modal');
const title = document.getElementById('stats-modal-title');
const content = document.getElementById('stats-modal-content');
let titleText, contentHTML;
switch (type) {
case 'total':
titleText = '园区车辆详情';
contentHTML = `
<div class="space-y-4">
<div class="flex justify-between items-center p-3 bg-blue-50 rounded-lg">
<span class="text-sm text-gray-700">总车辆数</span>
<span class="text-lg font-bold text-blue-600">${vehicleStats.total}</span>
</div>
<div class="grid grid-cols-2 gap-3">
<div class="text-center p-3 bg-green-50 rounded-lg">
<p class="text-lg font-bold text-green-600">${vehicleStats.todayNew}</p>
<p class="text-xs text-gray-500">今日新增</p>
</div>
<div class="text-center p-3 bg-gray-50 rounded-lg">
<p class="text-lg font-bold text-gray-600">${vehicleStats.total - vehicleStats.todayNew}</p>
<p class="text-xs text-gray-500">原有车辆</p>
</div>
</div>
</div>
`;
break;
case 'applications':
titleText = '入园申请详情';
contentHTML = `
<div class="space-y-4">
<div class="flex justify-between items-center p-3 bg-green-50 rounded-lg">
<span class="text-sm text-gray-700">今日申请总数</span>
<span class="text-lg font-bold text-green-600">${vehicleStats.todayApplications}</span>
</div>
<div class="grid grid-cols-2 gap-3">
<div class="text-center p-3 bg-blue-50 rounded-lg">
<p class="text-lg font-bold text-blue-600">${vehicleStats.approved}</p>
<p class="text-xs text-gray-500">已批准</p>
</div>
<div class="text-center p-3 bg-yellow-50 rounded-lg">
<p class="text-lg font-bold text-yellow-600">${vehicleStats.pending}</p>
<p class="text-xs text-gray-500">待审批</p>
</div>
</div>
</div>
`;
break;
case 'orders':
titleText = '运单车辆详情';
contentHTML = `
<div class="space-y-4">
<div class="flex justify-between items-center p-3 bg-orange-50 rounded-lg">
<span class="text-sm text-gray-700">运单车辆总数</span>
<span class="text-lg font-bold text-orange-600">${vehicleStats.activeOrders}</span>
</div>
<div class="grid grid-cols-2 gap-3">
<div class="text-center p-3 bg-green-50 rounded-lg">
<p class="text-lg font-bold text-green-600">${vehicleStats.transporting}</p>
<p class="text-xs text-gray-500">运输中</p>
</div>
<div class="text-center p-3 bg-gray-50 rounded-lg">
<p class="text-lg font-bold text-gray-600">${vehicleStats.loading}</p>
<p class="text-xs text-gray-500">待装卸</p>
</div>
</div>
</div>
`;
break;
}
title.textContent = titleText;
content.innerHTML = contentHTML;
modal.classList.remove('hidden');
// 添加显示动画
anime({
targets: modal.querySelector('.absolute'),
translateY: [100, 0],
duration: 300,
easing: 'easeOutQuart'
});
}
function closeStatsModal() {
const modal = document.getElementById('stats-modal');
modal.classList.add('hidden');
}
// 报警管理页面功能
function loadAllAlerts() {
const container = document.getElementById('alert-list');
if (!container) return;
const filteredAlerts = filterAlertsByStatus(alertsData, currentAlertFilter);
if (filteredAlerts.length === 0) {
container.innerHTML = '';
document.getElementById('empty-state').classList.remove('hidden');
return;
}
document.getElementById('empty-state').classList.add('hidden');
container.innerHTML = filteredAlerts.map(alert => {
const statusColor = alert.status === 'unread' ? 'text-red-600' : 'text-gray-500';
const statusDot = alert.status === 'unread' ? 'bg-red-500' : 'bg-gray-300';
const statusText = alert.status === 'unread' ? '未查看' : '已查看';
const borderClass = alert.status === 'unread' ? 'alert-unread' : 'alert-read';
return `
<div class="alert-card ${borderClass} bg-white rounded-xl shadow-sm p-4" onclick="toggleAlertStatus(${alert.id})">
<div class="flex items-start space-x-3">
<div class="w-3 h-3 ${statusDot} rounded-full mt-2"></div>
<div class="flex-1">
<div class="flex items-center justify-between mb-2">
<h3 class="text-sm font-semibold ${statusColor}">${alert.vehicleId}</h3>
<span class="text-xs px-2 py-1 rounded ${alert.status === 'unread' ? 'bg-red-100 text-red-600' : 'bg-gray-100 text-gray-600'}">${statusText}</span>
</div>
<p class="text-sm text-gray-700 mb-2">${alert.description}</p>
<div class="flex items-center justify-between text-xs text-gray-400">
<span class="flex items-center">
<svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/>
</svg>
${alert.location}
</span>
<span>${alert.time}</span>
</div>
</div>
</div>
</div>
`;
}).join('');
// 添加进入动画
anime({
targets: '.alert-card',
translateY: [20, 0],
opacity: [0, 1],
delay: anime.stagger(100),
duration: 600,
easing: 'easeOutQuart'
});
}
function filterAlerts(status) {
currentAlertFilter = status;
// 更新筛选标签状态
document.querySelectorAll('.filter-tab').forEach(tab => {
tab.classList.remove('active');
tab.classList.add('text-gray-600');
});
event.target.classList.remove('text-gray-600');
event.target.classList.add('active');
loadAllAlerts();
}
function filterAlertsByStatus(alerts, status) {
if (status === 'all') return alerts;
return alerts.filter(alert => alert.status === status);
}
function toggleAlertStatus(alertId) {
const alert = alertsData.find(a => a.id === alertId);
if (alert) {
alert.status = alert.status === 'unread' ? 'read' : 'unread';
loadAllAlerts();
updateAlertStats();
// 显示状态更新提示
const statusText = alert.status === 'read' ? '已标记为已查看' : '已标记为未查看';
showToast(statusText);
}
}
function updateAlertStats() {
const totalAlerts = alertsData.length;
const unreadAlerts = alertsData.filter(a => a.status === 'unread').length;
const readAlerts = totalAlerts - unreadAlerts;
const elements = {
'total-alerts': totalAlerts,
'unread-alerts': unreadAlerts,
'read-alerts': readAlerts,
'count-all': totalAlerts,
'count-unread': unreadAlerts,
'count-read': readAlerts,
'unread-count': `${unreadAlerts}条未查看`
};
Object.entries(elements).forEach(([id, value]) => {
const element = document.getElementById(id);
if (element) {
if (typeof value === 'string' && value.includes('条未查看')) {
element.textContent = value;
} else {
element.textContent = value;
}
}
});
}
// 轨迹查询页面功能
function handleInputChange() {
const input = document.getElementById('license-plate-input');
const value = input.value.trim();
if (value.length > 0) {
document.getElementById('search-suggestions').style.display = 'none';
} else {
document.getElementById('search-suggestions').style.display = 'flex';
}
}
function quickSearch(platePrefix) {
document.getElementById('license-plate-input').value = platePrefix;
handleInputChange();
}
function searchTrajectory() {
const plateNumber = document.getElementById('license-plate-input').value.trim();
if (!plateNumber) {
alert('请输入车牌号');
return;
}
// 模拟查询过程
showToast('正在查询轨迹...');
setTimeout(() => {
const trajectory = trajectoryData[plateNumber];
if (trajectory) {
displaySearchResults(plateNumber, trajectory);
addToRecentSearches(plateNumber);
} else {
showToast('未找到该车辆的轨迹记录');
}
}, 1000);
}
function displaySearchResults(plateNumber, trajectory) {
document.getElementById('recent-searches-section').classList.add('hidden');
document.getElementById('results-section').classList.remove('hidden');
// 显示搜索结果
const resultsContainer = document.getElementById('search-results');
resultsContainer.innerHTML = `
<div class="result-card p-4 bg-gray-50 rounded-lg">
<div class="flex items-center justify-between mb-3">
<h3 class="text-lg font-semibold text-gray-900">${plateNumber}</h3>
<span class="px-2 py-1 bg-green-100 text-green-800 rounded text-xs">有轨迹记录</span>
</div>
<div class="grid grid-cols-2 gap-4 text-sm">
<div>
<span class="text-gray-500">轨迹点数:</span>
<span class="text-gray-900 ml-1">${trajectory.length}</span>
</div>
<div>
<span class="text-gray-500">报警次数:</span>
<span class="text-red-600 ml-1">${trajectory.filter(t => t.type === 'alert').length}</span>
</div>
<div>
<span class="text-gray-500">开始时间:</span>
<span class="text-gray-900 ml-1">${trajectory[0].time}</span>
</div>
<div>
<span class="text-gray-500">结束时间:</span>
<span class="text-gray-900 ml-1">${trajectory[trajectory.length - 1].time}</span>
</div>
</div>
<button onclick="showTrajectoryDetails('${plateNumber}')" class="w-full mt-4 bg-blue-600 text-white py-2 px-4 rounded-lg text-sm font-medium">
查看详细轨迹
</button>
</div>
`;
}
function showTrajectoryDetails(plateNumber) {
const trajectory = trajectoryData[plateNumber];
const detailsContainer = document.getElementById('trajectory-details');
const timelineContainer = document.getElementById('trajectory-timeline');
timelineContainer.innerHTML = trajectory.map((point, index) => {
const iconColor = point.type === 'alert' ? 'text-red-600' : point.type === 'stop' ? 'text-yellow-600' : 'text-green-600';
const iconBg = point.type === 'alert' ? 'bg-red-100' : point.type === 'stop' ? 'bg-yellow-100' : 'bg-green-100';
return `
<div class="timeline-item flex items-start space-x-4">
<div class="w-8 h-8 ${iconBg} rounded-full flex items-center justify-center flex-shrink-0">
<svg class="w-4 h-4 ${iconColor}" fill="currentColor" viewBox="0 0 20 20">
${point.type === 'alert' ?
'<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>' :
point.type === 'stop' ?
'<path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>' :
'<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>'
}
</svg>
</div>
<div class="flex-1 pb-4">
<div class="flex items-center justify-between mb-1">
<h4 class="text-sm font-medium text-gray-900">${point.location}</h4>
<span class="text-xs text-gray-500">${point.time}</span>
</div>
<p class="text-xs ${point.type === 'alert' ? 'text-red-600' : 'text-gray-600'}">${point.status}</p>
</div>
</div>
`;
}).join('');
detailsContainer.classList.remove('hidden');
// 添加显示动画
anime({
targets: detailsContainer,
opacity: [0, 1],
translateY: [20, 0],
duration: 500,
easing: 'easeOutQuart'
});
}
function clearResults() {
document.getElementById('results-section').classList.add('hidden');
document.getElementById('trajectory-details').classList.add('hidden');
document.getElementById('recent-searches-section').classList.remove('hidden');
document.getElementById('license-plate-input').value = '';
handleInputChange();
}
function addToRecentSearches(plateNumber) {
// 移除已存在的相同车牌号
recentSearches = recentSearches.filter(plate => plate !== plateNumber);
// 添加到开头
recentSearches.unshift(plateNumber);
// 保持最多3条记录
recentSearches = recentSearches.slice(0, 3);
}
function loadRecentSearches() {
// 这个功能在简化版中用于显示预设的最近查询
// 实际应用中会使用本地存储来保存用户的查询历史
}
// 通用工具函数
function showToast(message) {
// 创建toast元素
const toast = document.createElement('div');
toast.className = 'fixed top-20 left-1/2 transform -translate-x-1/2 bg-gray-800 text-white px-4 py-2 rounded-lg text-sm z-50';
toast.textContent = message;
document.body.appendChild(toast);
// 显示动画
anime({
targets: toast,
translateY: [-20, 0],
opacity: [0, 1],
duration: 300,
easing: 'easeOutQuart',
complete: () => {
setTimeout(() => {
anime({
targets: toast,
translateY: [0, -20],
opacity: [1, 0],
duration: 300,
easing: 'easeInQuart',
complete: () => {
document.body.removeChild(toast);
}
});
}, 2000);
}
});
}
// 页面初始化
document.addEventListener('DOMContentLoaded', function() {
// 根据当前页面初始化相应功能
const currentPage = window.location.pathname.split('/').pop();
switch (currentPage) {
case 'index.html':
case '':
loadRecentAlerts();
animateStatistics();
break;
case 'monitor.html':
loadAllAlerts();
updateAlertStats();
break;
case 'tracking.html':
loadRecentSearches();
break;
}
});
// 导出函数供HTML调用
window.navigateToHome = navigateToHome;
window.navigateToMonitor = navigateToMonitor;
window.navigateToTracking = navigateToTracking;
window.goBack = goBack;
window.showVehicleDetail = showVehicleDetail;
window.closeStatsModal = closeStatsModal;
window.filterAlerts = filterAlerts;
window.toggleAlertStatus = toggleAlertStatus;
window.handleInputChange = handleInputChange;
window.quickSearch = quickSearch;
window.searchTrajectory = searchTrajectory;
window.showTrajectoryDetails = showTrajectoryDetails;
window.clearResults = clearResults;