1
This commit is contained in:
64
background/design.md
Normal file
64
background/design.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# 企业设置管理系统设计规范
|
||||||
|
|
||||||
|
## 设计理念
|
||||||
|
采用现代简约的企业级设计风格,强调专业性和易用性,通过精致的视觉细节和流畅的交互动画提升用户体验。
|
||||||
|
|
||||||
|
## 色彩系统
|
||||||
|
- **主色调**: 深蓝灰 (#2D3748) - 专业稳重
|
||||||
|
- **辅助色**: 柔和蓝 (#4A90E2) - 交互元素
|
||||||
|
- **强调色**: 温暖橙 (#F56500) - 重要操作
|
||||||
|
- **背景色**: 浅灰白 (#F7FAFC) - 主背景
|
||||||
|
- **文本色**: 深灰 (#2D3748) 和中灰 (#4A5568)
|
||||||
|
|
||||||
|
## 字体系统
|
||||||
|
- **标题字体**: Noto Sans SC (粗体) - 现代感强
|
||||||
|
- **正文字体**: Noto Sans SC (常规) - 清晰易读
|
||||||
|
- **字体层级**:
|
||||||
|
- H1: 2.5rem (40px)
|
||||||
|
- H2: 2rem (32px)
|
||||||
|
- H3: 1.5rem (24px)
|
||||||
|
- 正文: 1rem (16px)
|
||||||
|
- 小字: 0.875rem (14px)
|
||||||
|
|
||||||
|
## 视觉效果
|
||||||
|
### 背景效果
|
||||||
|
- 使用液态金属位移效果作为页面背景
|
||||||
|
- 微妙的粒子动画增强视觉层次
|
||||||
|
|
||||||
|
### 交互动画
|
||||||
|
- 按钮悬停: 3D倾斜效果 + 阴影扩展
|
||||||
|
- 表单元素: 聚焦时边框发光效果
|
||||||
|
- 开关切换: 平滑的滑动动画
|
||||||
|
- 卡片悬停: 轻微上浮 + 阴影加深
|
||||||
|
|
||||||
|
### 图标系统
|
||||||
|
- 使用线性图标风格
|
||||||
|
- 统一的描边粗细 (2px)
|
||||||
|
- 悬停时填充动画
|
||||||
|
|
||||||
|
## 布局系统
|
||||||
|
- **网格系统**: 12列响应式网格
|
||||||
|
- **间距系统**: 4px基础单位的倍数
|
||||||
|
- **圆角**: 统一使用8px圆角
|
||||||
|
- **阴影**: 多层次阴影系统营造深度感
|
||||||
|
|
||||||
|
## 组件设计
|
||||||
|
### 登录表单
|
||||||
|
- 居中布局,最大宽度400px
|
||||||
|
- 半透明背景卡片
|
||||||
|
- 渐变边框装饰
|
||||||
|
|
||||||
|
### 设置面板
|
||||||
|
- 左侧导航栏 (固定宽度280px)
|
||||||
|
- 右侧内容区域 (自适应)
|
||||||
|
- 分组的设置卡片
|
||||||
|
|
||||||
|
### 开关控件
|
||||||
|
- iOS风格的滑动开关
|
||||||
|
- 颜色状态反馈
|
||||||
|
- 微动画过渡
|
||||||
|
|
||||||
|
### 输入控件
|
||||||
|
- 浮动标签设计
|
||||||
|
- 聚焦时动画效果
|
||||||
|
- 错误状态视觉反馈
|
||||||
71
background/index.html
Normal file
71
background/index.html
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>企业设置管理系统</title>
|
||||||
|
<meta http-equiv="refresh" content="0; url=login.html">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Noto Sans SC', sans-serif;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-top: 3px solid white;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
margin: 0 auto 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-subtitle {
|
||||||
|
font-size: 14px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="loading-container">
|
||||||
|
<div class="spinner"></div>
|
||||||
|
<div class="loading-text">企业设置管理系统</div>
|
||||||
|
<div class="loading-subtitle">正在加载登录页面...</div>
|
||||||
|
|
||||||
|
<div style="margin-top: 30px;">
|
||||||
|
<p style="font-size: 12px; opacity: 0.6;">
|
||||||
|
如果页面没有自动跳转,请<a href="login.html" style="color: #4A90E2; text-decoration: none;">点击这里</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 确保在3秒后强制跳转
|
||||||
|
setTimeout(function() {
|
||||||
|
window.location.href = 'login.html';
|
||||||
|
}, 3000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
56
background/interaction.md
Normal file
56
background/interaction.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# 企业设置管理系统交互设计
|
||||||
|
|
||||||
|
## 系统概述
|
||||||
|
一个专为企业管理设置的双页面Web应用,包含登录功能和企业设置管理功能。
|
||||||
|
|
||||||
|
## 页面结构
|
||||||
|
1. **登录页面** - 企业用户身份验证
|
||||||
|
2. **设置页面** - 企业配置管理
|
||||||
|
|
||||||
|
## 详细交互设计
|
||||||
|
|
||||||
|
### 登录页面交互
|
||||||
|
- **表单验证**: 实时验证企业ID和密码
|
||||||
|
- **错误提示**: 友好的错误信息展示
|
||||||
|
- **记住登录**: 可选的记住登录状态功能
|
||||||
|
- **动画反馈**: 按钮悬停和点击动画效果
|
||||||
|
|
||||||
|
### 设置页面交互
|
||||||
|
- **设置分类**:
|
||||||
|
- 交通管理设置
|
||||||
|
- 是否禁停 (开关切换)
|
||||||
|
- 是否禁行 (开关切换)
|
||||||
|
- 是否是危险源 (开关切换)
|
||||||
|
- 车辆管理设置
|
||||||
|
- 可停车辆数 (数字输入框)
|
||||||
|
- 可行车辆数 (数字输入框)
|
||||||
|
- 安全设置
|
||||||
|
- 最大限速 (滑动条 + 数字输入)
|
||||||
|
- 企业信息
|
||||||
|
- 企业产品 (多行文本框)
|
||||||
|
|
||||||
|
- **交互特性**:
|
||||||
|
- 实时预览设置效果
|
||||||
|
- 数值输入验证和限制
|
||||||
|
- 开关状态的视觉反馈
|
||||||
|
- 保存确认对话框
|
||||||
|
- 重置为默认设置选项
|
||||||
|
|
||||||
|
### 数据持久化
|
||||||
|
- 使用localStorage保存设置数据
|
||||||
|
- 登录状态管理
|
||||||
|
- 设置变更历史记录
|
||||||
|
|
||||||
|
## 用户体验流程
|
||||||
|
1. 用户访问登录页面
|
||||||
|
2. 输入企业凭证进行登录
|
||||||
|
3. 进入设置页面查看当前配置
|
||||||
|
4. 修改各项企业设置参数
|
||||||
|
5. 预览设置效果
|
||||||
|
6. 保存设置并确认
|
||||||
|
7. 可选择退出登录
|
||||||
|
|
||||||
|
## 响应式设计
|
||||||
|
- 适配桌面端和移动端
|
||||||
|
- 触摸友好的交互元素
|
||||||
|
- 合理的布局缩放
|
||||||
391
background/login.html
Normal file
391
background/login.html
Normal file
@@ -0,0 +1,391 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>企业设置管理系统 - 登录</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
font-family: 'Noto Sans SC', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.liquid-bg {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.liquid-metal {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
|
||||||
|
radial-gradient(circle at 80% 20%, rgba(255, 255, 255, 0.15) 0%, transparent 50%),
|
||||||
|
radial-gradient(circle at 40% 40%, rgba(120, 119, 198, 0.4) 0%, transparent 50%);
|
||||||
|
animation: liquidFlow 20s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes liquidFlow {
|
||||||
|
0%, 100% { transform: translate(0, 0) rotate(0deg); }
|
||||||
|
33% { transform: translate(-20px, -20px) rotate(1deg); }
|
||||||
|
66% { transform: translate(20px, -10px) rotate(-1deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-card {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
box-shadow: 0 25px 45px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
border: 2px solid transparent;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input:focus {
|
||||||
|
background: rgba(255, 255, 255, 1);
|
||||||
|
border-color: #4A90E2;
|
||||||
|
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: linear-gradient(135deg, #4A90E2 0%, #357ABD 100%);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
transform: translateY(-3px) rotateX(5deg);
|
||||||
|
box-shadow: 0 15px 35px rgba(74, 144, 226, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:active {
|
||||||
|
transform: translateY(-1px) rotateX(2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-particles {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.particle {
|
||||||
|
position: absolute;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: float 6s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes float {
|
||||||
|
0%, 100% { transform: translateY(0px) rotate(0deg); opacity: 0; }
|
||||||
|
50% { transform: translateY(-100px) rotate(180deg); opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-animation {
|
||||||
|
animation: logoFloat 3s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes logoFloat {
|
||||||
|
0%, 100% { transform: translateY(0px); }
|
||||||
|
50% { transform: translateY(-10px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-shake {
|
||||||
|
animation: shake 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shake {
|
||||||
|
0%, 100% { transform: translateX(0); }
|
||||||
|
25% { transform: translateX(-5px); }
|
||||||
|
75% { transform: translateX(5px); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="min-h-screen flex items-center justify-center p-4">
|
||||||
|
<div class="liquid-bg">
|
||||||
|
<div class="liquid-metal"></div>
|
||||||
|
<div class="floating-particles" id="particles"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="glass-card rounded-3xl p-8 w-full max-w-md transform transition-all duration-500 hover:scale-105">
|
||||||
|
<!-- Logo Section -->
|
||||||
|
<div class="text-center mb-8">
|
||||||
|
<div class="logo-animation mb-4">
|
||||||
|
<div class="w-20 h-20 mx-auto bg-gradient-to-br from-blue-400 to-blue-600 rounded-3xl flex items-center justify-center shadow-2xl">
|
||||||
|
<svg class="w-10 h-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-4m-5 0H9m0 0H5m0 0h2M7 7h10M7 11h10M7 15h10"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h1 class="text-3xl font-bold text-white mb-2">企业设置管理</h1>
|
||||||
|
<p class="text-blue-100 text-sm">安全高效的企业配置平台</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Login Form -->
|
||||||
|
<form id="loginForm" class="space-y-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="companyId" class="block text-white text-sm font-medium mb-2">企业ID</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="companyId"
|
||||||
|
name="companyId"
|
||||||
|
class="form-input w-full px-4 py-3 rounded-xl text-gray-800 placeholder-gray-500 focus:outline-none"
|
||||||
|
placeholder="请输入企业ID"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<div class="error-message text-red-300 text-xs mt-1 hidden" id="companyIdError"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password" class="block text-white text-sm font-medium mb-2">登录密码</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
class="form-input w-full px-4 py-3 rounded-xl text-gray-800 placeholder-gray-500 focus:outline-none"
|
||||||
|
placeholder="请输入登录密码"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<div class="error-message text-red-300 text-xs mt-1 hidden" id="passwordError"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<label class="flex items-center">
|
||||||
|
<input type="checkbox" id="rememberMe" class="w-4 h-4 text-blue-600 rounded focus:ring-blue-500">
|
||||||
|
<span class="ml-2 text-sm text-blue-100">记住登录状态</span>
|
||||||
|
</label>
|
||||||
|
<a href="#" class="text-sm text-blue-200 hover:text-white transition-colors">忘记密码?</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="btn-primary w-full py-3 px-6 rounded-xl text-white font-medium text-lg focus:outline-none focus:ring-4 focus:ring-blue-300"
|
||||||
|
id="loginBtn"
|
||||||
|
>
|
||||||
|
<span class="btn-text">安全登录</span>
|
||||||
|
<span class="btn-loading hidden">
|
||||||
|
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white inline" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||||
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||||
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||||
|
</svg>
|
||||||
|
验证中...
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<div class="text-center mt-8">
|
||||||
|
<p class="text-blue-200 text-xs">
|
||||||
|
© 2025 企业设置管理系统 | 安全可靠的配置管理平台
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Success Modal -->
|
||||||
|
<div id="successModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
|
||||||
|
<div class="bg-white rounded-2xl p-8 max-w-sm mx-4 transform transition-all duration-300 scale-95">
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="w-16 h-16 mx-auto bg-green-100 rounded-full flex items-center justify-center mb-4">
|
||||||
|
<svg class="w-8 h-8 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-lg font-semibold text-gray-900 mb-2">登录成功</h3>
|
||||||
|
<p class="text-gray-600 text-sm mb-6">正在进入企业设置管理平台...</p>
|
||||||
|
<div class="w-full bg-gray-200 rounded-full h-2">
|
||||||
|
<div class="bg-blue-600 h-2 rounded-full transition-all duration-1000" style="width: 0%" id="progressBar"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 创建浮动粒子效果
|
||||||
|
function createParticles() {
|
||||||
|
const particlesContainer = document.getElementById('particles');
|
||||||
|
const particleCount = 15;
|
||||||
|
|
||||||
|
for (let i = 0; i < particleCount; i++) {
|
||||||
|
const particle = document.createElement('div');
|
||||||
|
particle.className = 'particle';
|
||||||
|
|
||||||
|
const size = Math.random() * 4 + 2;
|
||||||
|
const left = Math.random() * 100;
|
||||||
|
const animationDelay = Math.random() * 6;
|
||||||
|
const animationDuration = Math.random() * 3 + 4;
|
||||||
|
|
||||||
|
particle.style.width = `${size}px`;
|
||||||
|
particle.style.height = `${size}px`;
|
||||||
|
particle.style.left = `${left}%`;
|
||||||
|
particle.style.bottom = '-10px';
|
||||||
|
particle.style.animationDelay = `${animationDelay}s`;
|
||||||
|
particle.style.animationDuration = `${animationDuration}s`;
|
||||||
|
|
||||||
|
particlesContainer.appendChild(particle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单验证
|
||||||
|
function validateForm() {
|
||||||
|
const companyId = document.getElementById('companyId');
|
||||||
|
const password = document.getElementById('password');
|
||||||
|
let isValid = true;
|
||||||
|
|
||||||
|
// 验证企业ID
|
||||||
|
if (!companyId.value.trim()) {
|
||||||
|
showError('companyIdError', '企业ID不能为空');
|
||||||
|
companyId.classList.add('error-shake');
|
||||||
|
setTimeout(() => companyId.classList.remove('error-shake'), 500);
|
||||||
|
isValid = false;
|
||||||
|
} else {
|
||||||
|
hideError('companyIdError');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证密码
|
||||||
|
if (!password.value.trim()) {
|
||||||
|
showError('passwordError', '登录密码不能为空');
|
||||||
|
password.classList.add('error-shake');
|
||||||
|
setTimeout(() => password.classList.remove('error-shake'), 500);
|
||||||
|
isValid = false;
|
||||||
|
} else {
|
||||||
|
hideError('passwordError');
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showError(elementId, message) {
|
||||||
|
const errorElement = document.getElementById(elementId);
|
||||||
|
errorElement.textContent = message;
|
||||||
|
errorElement.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideError(elementId) {
|
||||||
|
const errorElement = document.getElementById(elementId);
|
||||||
|
errorElement.classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录处理
|
||||||
|
function handleLogin(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (!validateForm()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const loginBtn = document.getElementById('loginBtn');
|
||||||
|
const btnText = loginBtn.querySelector('.btn-text');
|
||||||
|
const btnLoading = loginBtn.querySelector('.btn-loading');
|
||||||
|
|
||||||
|
// 显示加载状态
|
||||||
|
btnText.classList.add('hidden');
|
||||||
|
btnLoading.classList.remove('hidden');
|
||||||
|
loginBtn.disabled = true;
|
||||||
|
|
||||||
|
// 模拟登录验证
|
||||||
|
setTimeout(() => {
|
||||||
|
// 保存登录状态
|
||||||
|
const rememberMe = document.getElementById('rememberMe').checked;
|
||||||
|
const loginData = {
|
||||||
|
companyId: document.getElementById('companyId').value,
|
||||||
|
loginTime: new Date().toISOString(),
|
||||||
|
rememberMe: rememberMe
|
||||||
|
};
|
||||||
|
|
||||||
|
if (rememberMe) {
|
||||||
|
localStorage.setItem('companyLogin', JSON.stringify(loginData));
|
||||||
|
} else {
|
||||||
|
sessionStorage.setItem('companyLogin', JSON.stringify(loginData));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示成功模态框
|
||||||
|
showSuccessModal();
|
||||||
|
|
||||||
|
// 3秒后跳转到设置页面
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = 'settings.html';
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示成功模态框
|
||||||
|
function showSuccessModal() {
|
||||||
|
const modal = document.getElementById('successModal');
|
||||||
|
const progressBar = document.getElementById('progressBar');
|
||||||
|
|
||||||
|
modal.classList.remove('hidden');
|
||||||
|
|
||||||
|
// 动画显示模态框
|
||||||
|
anime({
|
||||||
|
targets: modal.querySelector('.bg-white'),
|
||||||
|
scale: [0.95, 1],
|
||||||
|
opacity: [0, 1],
|
||||||
|
duration: 300,
|
||||||
|
easing: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 进度条动画
|
||||||
|
anime({
|
||||||
|
targets: progressBar,
|
||||||
|
width: '100%',
|
||||||
|
duration: 3000,
|
||||||
|
easing: 'linear'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
createParticles();
|
||||||
|
|
||||||
|
// 检查是否已登录
|
||||||
|
const savedLogin = localStorage.getItem('companyLogin') || sessionStorage.getItem('companyLogin');
|
||||||
|
if (savedLogin) {
|
||||||
|
const loginData = JSON.parse(savedLogin);
|
||||||
|
document.getElementById('companyId').value = loginData.companyId || '';
|
||||||
|
document.getElementById('rememberMe').checked = loginData.rememberMe || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单提交事件
|
||||||
|
document.getElementById('loginForm').addEventListener('submit', handleLogin);
|
||||||
|
|
||||||
|
// 输入框聚焦动画
|
||||||
|
const inputs = document.querySelectorAll('.form-input');
|
||||||
|
inputs.forEach(input => {
|
||||||
|
input.addEventListener('focus', function() {
|
||||||
|
anime({
|
||||||
|
targets: this,
|
||||||
|
scale: [1, 1.02],
|
||||||
|
duration: 200,
|
||||||
|
easing: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
input.addEventListener('blur', function() {
|
||||||
|
anime({
|
||||||
|
targets: this,
|
||||||
|
scale: [1.02, 1],
|
||||||
|
duration: 200,
|
||||||
|
easing: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
383
background/main.js
Normal file
383
background/main.js
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
// 企业设置管理系统 - 主要JavaScript文件
|
||||||
|
// 包含所有核心功能和交互逻辑
|
||||||
|
|
||||||
|
class EnterpriseSettingsApp {
|
||||||
|
constructor() {
|
||||||
|
this.currentUser = null;
|
||||||
|
this.settings = {};
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.checkAuthentication();
|
||||||
|
this.initializeDefaultSettings();
|
||||||
|
this.bindGlobalEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 身份验证管理
|
||||||
|
checkAuthentication() {
|
||||||
|
const loginData = localStorage.getItem('companyLogin') || sessionStorage.getItem('companyLogin');
|
||||||
|
if (loginData) {
|
||||||
|
this.currentUser = JSON.parse(loginData);
|
||||||
|
} else if (window.location.pathname.includes('settings.html')) {
|
||||||
|
window.location.href = 'login.html';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化默认设置
|
||||||
|
initializeDefaultSettings() {
|
||||||
|
const defaultSettings = {
|
||||||
|
noParking: false,
|
||||||
|
noTraffic: false,
|
||||||
|
dangerSource: false,
|
||||||
|
parkingCapacity: 50,
|
||||||
|
trafficCapacity: 100,
|
||||||
|
speedLimit: 60,
|
||||||
|
companyProducts: '我们专注于提供高质量的企业管理解决方案,包括智能交通管理系统、企业资源规划系统、安全监控系统等。致力于为各类企业提供全方位的数字化转型服务。'
|
||||||
|
};
|
||||||
|
|
||||||
|
const savedSettings = localStorage.getItem('companySettings');
|
||||||
|
this.settings = savedSettings ? { ...defaultSettings, ...JSON.parse(savedSettings) } : defaultSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定全局事件
|
||||||
|
bindGlobalEvents() {
|
||||||
|
// 页面可见性变化
|
||||||
|
document.addEventListener('visibilitychange', () => {
|
||||||
|
if (document.hidden) {
|
||||||
|
this.onPageHidden();
|
||||||
|
} else {
|
||||||
|
this.onPageVisible();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 窗口大小变化
|
||||||
|
window.addEventListener('resize', this.debounce(() => {
|
||||||
|
this.onWindowResize();
|
||||||
|
}, 250));
|
||||||
|
|
||||||
|
// 键盘快捷键
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
this.handleKeyboardShortcuts(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面隐藏时的处理
|
||||||
|
onPageHidden() {
|
||||||
|
// 保存当前状态
|
||||||
|
if (Object.keys(this.settings).length > 0) {
|
||||||
|
localStorage.setItem('companySettings', JSON.stringify(this.settings));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面显示时的处理
|
||||||
|
onPageVisible() {
|
||||||
|
// 检查会话状态
|
||||||
|
this.checkAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 窗口大小变化处理
|
||||||
|
onWindowResize() {
|
||||||
|
// 响应式布局调整
|
||||||
|
const isMobile = window.innerWidth < 768;
|
||||||
|
document.body.classList.toggle('mobile', isMobile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 键盘快捷键处理
|
||||||
|
handleKeyboardShortcuts(e) {
|
||||||
|
// Ctrl/Cmd + S: 保存设置
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
||||||
|
e.preventDefault();
|
||||||
|
this.saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ctrl/Cmd + R: 重置设置
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 'r') {
|
||||||
|
e.preventDefault();
|
||||||
|
this.resetSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape: 退出当前操作
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
this.exitCurrentOperation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 防抖函数
|
||||||
|
debounce(func, wait) {
|
||||||
|
let timeout;
|
||||||
|
return function executedFunction(...args) {
|
||||||
|
const later = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
func(...args);
|
||||||
|
};
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(later, wait);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 节流函数
|
||||||
|
throttle(func, limit) {
|
||||||
|
let inThrottle;
|
||||||
|
return function() {
|
||||||
|
const args = arguments;
|
||||||
|
const context = this;
|
||||||
|
if (!inThrottle) {
|
||||||
|
func.apply(context, args);
|
||||||
|
inThrottle = true;
|
||||||
|
setTimeout(() => inThrottle = false, limit);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置管理
|
||||||
|
saveSettings() {
|
||||||
|
localStorage.setItem('companySettings', JSON.stringify(this.settings));
|
||||||
|
this.showNotification('设置已保存', 'success');
|
||||||
|
this.logActivity('设置保存', '用户保存了企业设置');
|
||||||
|
}
|
||||||
|
|
||||||
|
resetSettings() {
|
||||||
|
if (confirm('确定要重置所有设置为默认值吗?此操作不可撤销。')) {
|
||||||
|
localStorage.removeItem('companySettings');
|
||||||
|
this.initializeDefaultSettings();
|
||||||
|
this.updateAllUI();
|
||||||
|
this.showNotification('设置已重置为默认值', 'info');
|
||||||
|
this.logActivity('设置重置', '用户重置了所有企业设置');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 退出当前操作
|
||||||
|
exitCurrentOperation() {
|
||||||
|
// 关闭所有模态框
|
||||||
|
document.querySelectorAll('.modal, .toast').forEach(element => {
|
||||||
|
element.classList.remove('show', 'active');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 清除焦点
|
||||||
|
document.activeElement.blur();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新所有UI元素
|
||||||
|
updateAllUI() {
|
||||||
|
// 更新开关状态
|
||||||
|
Object.keys(this.settings).forEach(key => {
|
||||||
|
const element = document.getElementById(key);
|
||||||
|
if (element) {
|
||||||
|
if (element.type === 'checkbox') {
|
||||||
|
element.checked = this.settings[key];
|
||||||
|
} else if (element.type === 'range' || element.type === 'number') {
|
||||||
|
element.value = this.settings[key];
|
||||||
|
} else {
|
||||||
|
element.value = this.settings[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新显示值
|
||||||
|
this.updateDisplayValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新显示值
|
||||||
|
updateDisplayValues() {
|
||||||
|
const displays = {
|
||||||
|
parkingCapacityValue: `${this.settings.parkingCapacity} 辆`,
|
||||||
|
trafficCapacityValue: `${this.settings.trafficCapacity} 辆`,
|
||||||
|
speedLimitValue: `${this.settings.speedLimit}`,
|
||||||
|
charCount: `${this.settings.companyProducts.length} 字符`
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(displays).forEach(id => {
|
||||||
|
const element = document.getElementById(id);
|
||||||
|
if (element) {
|
||||||
|
element.textContent = displays[id];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通知系统
|
||||||
|
showNotification(message, type = 'info', duration = 3000) {
|
||||||
|
const notification = this.createNotification(message, type);
|
||||||
|
document.body.appendChild(notification);
|
||||||
|
|
||||||
|
// 显示动画
|
||||||
|
setTimeout(() => {
|
||||||
|
notification.classList.add('show');
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
// 自动隐藏
|
||||||
|
setTimeout(() => {
|
||||||
|
notification.classList.remove('show');
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(notification);
|
||||||
|
}, 300);
|
||||||
|
}, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
createNotification(message, type) {
|
||||||
|
const notification = document.createElement('div');
|
||||||
|
notification.className = `notification notification-${type}`;
|
||||||
|
|
||||||
|
const icons = {
|
||||||
|
success: '✓',
|
||||||
|
error: '✕',
|
||||||
|
warning: '⚠',
|
||||||
|
info: 'ℹ'
|
||||||
|
};
|
||||||
|
|
||||||
|
const colors = {
|
||||||
|
success: 'bg-green-500',
|
||||||
|
error: 'bg-red-500',
|
||||||
|
warning: 'bg-yellow-500',
|
||||||
|
info: 'bg-blue-500'
|
||||||
|
};
|
||||||
|
|
||||||
|
notification.innerHTML = `
|
||||||
|
<div class="flex items-center space-x-3 p-4 bg-white rounded-lg shadow-lg">
|
||||||
|
<div class="w-8 h-8 ${colors[type]} rounded-full flex items-center justify-center text-white font-bold">
|
||||||
|
${icons[type]}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="font-medium text-gray-800">${message}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 活动日志
|
||||||
|
logActivity(action, description) {
|
||||||
|
const activity = {
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
action,
|
||||||
|
description,
|
||||||
|
user: this.currentUser?.companyId || '未知用户'
|
||||||
|
};
|
||||||
|
|
||||||
|
const activities = JSON.parse(localStorage.getItem('activityLog') || '[]');
|
||||||
|
activities.unshift(activity);
|
||||||
|
|
||||||
|
// 保留最近100条记录
|
||||||
|
if (activities.length > 100) {
|
||||||
|
activities.splice(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem('activityLog', JSON.stringify(activities));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取活动日志
|
||||||
|
getActivityLog(limit = 10) {
|
||||||
|
const activities = JSON.parse(localStorage.getItem('activityLog') || '[]');
|
||||||
|
return activities.slice(0, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据验证
|
||||||
|
validateSettings() {
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
if (this.settings.parkingCapacity < 0 || this.settings.parkingCapacity > 1000) {
|
||||||
|
errors.push('停车容量必须在0-1000之间');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.settings.trafficCapacity < 0 || this.settings.trafficCapacity > 1000) {
|
||||||
|
errors.push('通行容量必须在0-1000之间');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.settings.speedLimit < 10 || this.settings.speedLimit > 120) {
|
||||||
|
errors.push('限速必须在10-120 km/h之间');
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出设置
|
||||||
|
exportSettings() {
|
||||||
|
const data = {
|
||||||
|
settings: this.settings,
|
||||||
|
exportTime: new Date().toISOString(),
|
||||||
|
version: '1.0'
|
||||||
|
};
|
||||||
|
|
||||||
|
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = `enterprise-settings-${new Date().toISOString().split('T')[0]}.json`;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
this.logActivity('设置导出', '用户导出了企业设置');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入设置
|
||||||
|
importSettings(file) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (e) => {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(e.target.result);
|
||||||
|
if (data.settings) {
|
||||||
|
this.settings = { ...this.settings, ...data.settings };
|
||||||
|
this.updateAllUI();
|
||||||
|
this.saveSettings();
|
||||||
|
this.showNotification('设置导入成功', 'success');
|
||||||
|
this.logActivity('设置导入', '用户导入了企业设置');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.showNotification('设置文件格式错误', 'error');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取系统状态
|
||||||
|
getSystemStatus() {
|
||||||
|
return {
|
||||||
|
authenticated: !!this.currentUser,
|
||||||
|
settingsCount: Object.keys(this.settings).length,
|
||||||
|
lastActivity: this.getActivityLog(1)[0]?.timestamp || null,
|
||||||
|
storageUsed: this.getStorageInfo()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取存储信息
|
||||||
|
getStorageInfo() {
|
||||||
|
let total = 0;
|
||||||
|
for (let key in localStorage) {
|
||||||
|
if (localStorage.hasOwnProperty(key)) {
|
||||||
|
total += localStorage[key].length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
used: total,
|
||||||
|
available: 5 * 1024 * 1024, // 5MB
|
||||||
|
percent: (total / (5 * 1024 * 1024)) * 100
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全局实例
|
||||||
|
let app;
|
||||||
|
|
||||||
|
// 页面加载完成后初始化
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
app = new EnterpriseSettingsApp();
|
||||||
|
|
||||||
|
// 添加全局错误处理
|
||||||
|
window.addEventListener('error', function(e) {
|
||||||
|
console.error('应用错误:', e.error);
|
||||||
|
app.showNotification('应用出现错误,请刷新页面', 'error');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加未处理的Promise错误
|
||||||
|
window.addEventListener('unhandledrejection', function(e) {
|
||||||
|
console.error('未处理的Promise错误:', e.reason);
|
||||||
|
app.showNotification('网络请求失败,请检查网络连接', 'error');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 导出到全局作用域
|
||||||
|
window.EnterpriseSettingsApp = EnterpriseSettingsApp;
|
||||||
774
background/settings.html
Normal file
774
background/settings.html
Normal file
@@ -0,0 +1,774 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>企业设置管理系统 - 设置面板</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
font-family: 'Noto Sans SC', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.liquid-bg {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.liquid-metal {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
|
||||||
|
radial-gradient(circle at 80% 20%, rgba(255, 255, 255, 0.15) 0%, transparent 50%),
|
||||||
|
radial-gradient(circle at 40% 40%, rgba(120, 119, 198, 0.4) 0%, transparent 50%);
|
||||||
|
animation: liquidFlow 20s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes liquidFlow {
|
||||||
|
0%, 100% { transform: translate(0, 0) rotate(0deg); }
|
||||||
|
33% { transform: translate(-20px, -20px) rotate(1deg); }
|
||||||
|
66% { transform: translate(20px, -10px) rotate(-1deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
border-right: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-card {
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
transform: translateX(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item.active {
|
||||||
|
background: rgba(74, 144, 226, 0.2);
|
||||||
|
border-left: 4px solid #4A90E2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 60px;
|
||||||
|
height: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #ccc;
|
||||||
|
transition: .4s;
|
||||||
|
border-radius: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: 26px;
|
||||||
|
width: 26px;
|
||||||
|
left: 4px;
|
||||||
|
bottom: 4px;
|
||||||
|
background-color: white;
|
||||||
|
transition: .4s;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider {
|
||||||
|
background-color: #4A90E2;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider:before {
|
||||||
|
transform: translateX(26px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-slider {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: #ddd;
|
||||||
|
outline: none;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-slider::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #4A90E2;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 2px 10px rgba(74, 144, 226, 0.3);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-slider::-webkit-slider-thumb:hover {
|
||||||
|
transform: scale(1.2);
|
||||||
|
box-shadow: 0 4px 15px rgba(74, 144, 226, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: linear-gradient(135deg, #4A90E2 0%, #357ABD 100%);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 25px rgba(74, 144, 226, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background: linear-gradient(135deg, #718096 0%, #4A5568 100%);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 25px rgba(113, 128, 150, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
border: 2px solid #e2e8f0;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input:focus {
|
||||||
|
border-color: #4A90E2;
|
||||||
|
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-fade {
|
||||||
|
animation: fadeInUp 0.6s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 1000;
|
||||||
|
transform: translateX(400px);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.show {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="min-h-screen flex overflow-hidden">
|
||||||
|
<!-- Background -->
|
||||||
|
<div class="liquid-bg">
|
||||||
|
<div class="liquid-metal"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<div class="sidebar w-80 h-screen flex flex-col">
|
||||||
|
<!-- Logo -->
|
||||||
|
<div class="p-6 border-b border-white/20">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<div class="w-12 h-12 bg-gradient-to-br from-blue-400 to-blue-600 rounded-xl flex items-center justify-center">
|
||||||
|
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-4m-5 0H9m0 0H5m0 0h2M7 7h10M7 11h10M7 15h10"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2 class="text-white font-bold text-lg">企业设置中心</h2>
|
||||||
|
<p class="text-blue-200 text-sm" id="companyName">企业管理系统</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Navigation -->
|
||||||
|
<nav class="flex-1 p-4">
|
||||||
|
<ul class="space-y-2">
|
||||||
|
<li class="nav-item active rounded-xl p-3 cursor-pointer" data-section="traffic">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||||
|
</svg>
|
||||||
|
<span class="text-white font-medium">交通管理设置</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item rounded-xl p-3 cursor-pointer" data-section="vehicle">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2"></path>
|
||||||
|
</svg>
|
||||||
|
<span class="text-white font-medium">车辆管理设置</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item rounded-xl p-3 cursor-pointer" data-section="safety">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path>
|
||||||
|
</svg>
|
||||||
|
<span class="text-white font-medium">安全设置</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item rounded-xl p-3 cursor-pointer" data-section="company">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-4m-5 0H9m0 0H5m0 0h2M7 7h10M7 11h10M7 15h10"></path>
|
||||||
|
</svg>
|
||||||
|
<span class="text-white font-medium">企业信息</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- User Info -->
|
||||||
|
<div class="p-6 border-t border-white/20">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<div class="w-10 h-10 bg-gradient-to-br from-green-400 to-green-600 rounded-full flex items-center justify-center">
|
||||||
|
<span class="text-white font-bold text-sm" id="userInitial">企</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<p class="text-white font-medium text-sm" id="userName">管理员</p>
|
||||||
|
<p class="text-blue-200 text-xs">在线</p>
|
||||||
|
</div>
|
||||||
|
<button id="logoutBtn" class="text-white hover:text-red-300 transition-colors">
|
||||||
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<div class="main-content flex-1 overflow-y-auto">
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="p-6 border-b border-gray-200">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h1 class="text-2xl font-bold text-gray-800" id="sectionTitle">交通管理设置</h1>
|
||||||
|
<p class="text-gray-600 text-sm mt-1" id="sectionDescription">配置企业交通管理相关参数</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex space-x-3">
|
||||||
|
<button id="resetBtn" class="btn-secondary px-6 py-2 rounded-xl text-white font-medium">
|
||||||
|
重置设置
|
||||||
|
</button>
|
||||||
|
<button id="saveBtn" class="btn-primary px-6 py-2 rounded-xl text-white font-medium">
|
||||||
|
保存设置
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Content Sections -->
|
||||||
|
<div class="p-6">
|
||||||
|
<!-- Traffic Management Section -->
|
||||||
|
<div id="traffic-section" class="section-content">
|
||||||
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
|
<!-- Parking Settings -->
|
||||||
|
<div class="glass-card rounded-2xl p-6">
|
||||||
|
<div class="flex items-center space-x-3 mb-4">
|
||||||
|
<div class="w-12 h-12 bg-red-100 rounded-xl flex items-center justify-center">
|
||||||
|
<svg class="w-6 h-6 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L18.364 5.636M5.636 18.364l12.728-12.728"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg font-semibold text-gray-800">禁停设置</h3>
|
||||||
|
<p class="text-gray-600 text-sm">是否禁止车辆停放</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-gray-700">启用禁停</span>
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" id="noParking" class="setting-toggle">
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 p-3 bg-gray-50 rounded-lg">
|
||||||
|
<p class="text-sm text-gray-600">启用后,企业区域内将禁止所有车辆停放。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Traffic Settings -->
|
||||||
|
<div class="glass-card rounded-2xl p-6">
|
||||||
|
<div class="flex items-center space-x-3 mb-4">
|
||||||
|
<div class="w-12 h-12 bg-orange-100 rounded-xl flex items-center justify-center">
|
||||||
|
<svg class="w-6 h-6 text-orange-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg font-semibold text-gray-800">禁行设置</h3>
|
||||||
|
<p class="text-gray-600 text-sm">是否禁止车辆通行</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-gray-700">启用禁行</span>
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" id="noTraffic" class="setting-toggle">
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 p-3 bg-gray-50 rounded-lg">
|
||||||
|
<p class="text-sm text-gray-600">启用后,企业区域内将禁止所有车辆通行。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Danger Source Settings -->
|
||||||
|
<div class="glass-card rounded-2xl p-6">
|
||||||
|
<div class="flex items-center space-x-3 mb-4">
|
||||||
|
<div class="w-12 h-12 bg-yellow-100 rounded-xl flex items-center justify-center">
|
||||||
|
<svg class="w-6 h-6 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg font-semibold text-gray-800">危险源设置</h3>
|
||||||
|
<p class="text-gray-600 text-sm">是否标记为危险源</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-gray-700">危险源标记</span>
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" id="dangerSource" class="setting-toggle">
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 p-3 bg-gray-50 rounded-lg">
|
||||||
|
<p class="text-sm text-gray-600">标记后,系统将对此区域进行特殊安全监控。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Vehicle Management Section -->
|
||||||
|
<div id="vehicle-section" class="section-content hidden">
|
||||||
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
|
<!-- Parking Capacity -->
|
||||||
|
<div class="glass-card rounded-2xl p-6">
|
||||||
|
<div class="flex items-center space-x-3 mb-4">
|
||||||
|
<div class="w-12 h-12 bg-blue-100 rounded-xl flex items-center justify-center">
|
||||||
|
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg font-semibold text-gray-800">可停车辆数</h3>
|
||||||
|
<p class="text-gray-600 text-sm">设置最大停车数量</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="parkingCapacity"
|
||||||
|
class="form-input w-full px-4 py-3 rounded-xl focus:outline-none"
|
||||||
|
min="0"
|
||||||
|
max="1000"
|
||||||
|
value="50"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-between text-sm text-gray-600">
|
||||||
|
<span>当前设置</span>
|
||||||
|
<span id="parkingCapacityValue" class="font-semibold text-blue-600">50 辆</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Traffic Capacity -->
|
||||||
|
<div class="glass-card rounded-2xl p-6">
|
||||||
|
<div class="flex items-center space-x-3 mb-4">
|
||||||
|
<div class="w-12 h-12 bg-green-100 rounded-xl flex items-center justify-center">
|
||||||
|
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg font-semibold text-gray-800">可行车辆数</h3>
|
||||||
|
<p class="text-gray-600 text-sm">设置最大通行数量</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="trafficCapacity"
|
||||||
|
class="form-input w-full px-4 py-3 rounded-xl focus:outline-none"
|
||||||
|
min="0"
|
||||||
|
max="1000"
|
||||||
|
value="100"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-between text-sm text-gray-600">
|
||||||
|
<span>当前设置</span>
|
||||||
|
<span id="trafficCapacityValue" class="font-semibold text-green-600">100 辆</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Safety Settings Section -->
|
||||||
|
<div id="safety-section" class="section-content hidden">
|
||||||
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
|
<!-- Speed Limit -->
|
||||||
|
<div class="glass-card rounded-2xl p-6">
|
||||||
|
<div class="flex items-center space-x-3 mb-4">
|
||||||
|
<div class="w-12 h-12 bg-red-100 rounded-xl flex items-center justify-center">
|
||||||
|
<svg class="w-6 h-6 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg font-semibold text-gray-800">最大限速</h3>
|
||||||
|
<p class="text-gray-600 text-sm">设置最高允许车速</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
id="speedLimit"
|
||||||
|
class="range-slider flex-1"
|
||||||
|
min="10"
|
||||||
|
max="120"
|
||||||
|
value="60"
|
||||||
|
>
|
||||||
|
<div class="text-2xl font-bold text-red-600 min-w-[60px]" id="speedLimitValue">60</div>
|
||||||
|
<span class="text-gray-600">km/h</span>
|
||||||
|
</div>
|
||||||
|
<div class="p-3 bg-gray-50 rounded-lg">
|
||||||
|
<p class="text-sm text-gray-600">建议根据企业区域特点设置合适的限速值。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Company Info Section -->
|
||||||
|
<div id="company-section" class="section-content hidden">
|
||||||
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
|
<!-- Company Products -->
|
||||||
|
<div class="glass-card rounded-2xl p-6 lg:col-span-2">
|
||||||
|
<div class="flex items-center space-x-3 mb-4">
|
||||||
|
<div class="w-12 h-12 bg-purple-100 rounded-xl flex items-center justify-center">
|
||||||
|
<svg class="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-4m-5 0H9m0 0H5m0 0h2M7 7h10M7 11h10M7 15h10"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg font-semibold text-gray-800">企业产品</h3>
|
||||||
|
<p class="text-gray-600 text-sm">描述企业主要产品和服务</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<textarea
|
||||||
|
id="companyProducts"
|
||||||
|
class="form-input w-full px-4 py-3 rounded-xl focus:outline-none h-32 resize-none"
|
||||||
|
placeholder="请输入企业主要产品和服务描述..."
|
||||||
|
>我们专注于提供高质量的企业管理解决方案,包括智能交通管理系统、企业资源规划系统、安全监控系统等。致力于为各类企业提供全方位的数字化转型服务。</textarea>
|
||||||
|
<div class="flex items-center justify-between text-sm text-gray-600">
|
||||||
|
<span>字符统计</span>
|
||||||
|
<span id="charCount" class="font-semibold text-purple-600">0 字符</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Toast Notification -->
|
||||||
|
<div id="toast" class="toast bg-white rounded-xl shadow-lg p-4 max-w-sm">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<div id="toastIcon" class="w-8 h-8 rounded-full flex items-center justify-center">
|
||||||
|
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p id="toastMessage" class="font-medium text-gray-800">设置已保存</p>
|
||||||
|
<p class="text-sm text-gray-600">更改已成功应用到系统</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 设置数据管理
|
||||||
|
class SettingsManager {
|
||||||
|
constructor() {
|
||||||
|
this.settings = this.loadSettings();
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.bindEvents();
|
||||||
|
this.loadUserInfo();
|
||||||
|
this.showSection('traffic');
|
||||||
|
this.updateAllValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSettings() {
|
||||||
|
const defaultSettings = {
|
||||||
|
noParking: false,
|
||||||
|
noTraffic: false,
|
||||||
|
dangerSource: false,
|
||||||
|
parkingCapacity: 50,
|
||||||
|
trafficCapacity: 100,
|
||||||
|
speedLimit: 60,
|
||||||
|
companyProducts: '我们专注于提供高质量的企业管理解决方案,包括智能交通管理系统、企业资源规划系统、安全监控系统等。致力于为各类企业提供全方位的数字化转型服务。'
|
||||||
|
};
|
||||||
|
|
||||||
|
const saved = localStorage.getItem('companySettings');
|
||||||
|
return saved ? { ...defaultSettings, ...JSON.parse(saved) } : defaultSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveSettings() {
|
||||||
|
localStorage.setItem('companySettings', JSON.stringify(this.settings));
|
||||||
|
this.showToast('设置已保存', 'success');
|
||||||
|
}
|
||||||
|
|
||||||
|
loadUserInfo() {
|
||||||
|
const loginData = localStorage.getItem('companyLogin') || sessionStorage.getItem('companyLogin');
|
||||||
|
if (loginData) {
|
||||||
|
const data = JSON.parse(loginData);
|
||||||
|
document.getElementById('companyName').textContent = data.companyId || '企业管理系统';
|
||||||
|
document.getElementById('userInitial').textContent = (data.companyId || '企业').charAt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bindEvents() {
|
||||||
|
// 导航切换
|
||||||
|
document.querySelectorAll('.nav-item').forEach(item => {
|
||||||
|
item.addEventListener('click', (e) => {
|
||||||
|
const section = e.currentTarget.dataset.section;
|
||||||
|
this.showSection(section);
|
||||||
|
this.updateNavigation(e.currentTarget);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 开关切换
|
||||||
|
document.querySelectorAll('.setting-toggle').forEach(toggle => {
|
||||||
|
toggle.addEventListener('change', (e) => {
|
||||||
|
this.settings[e.target.id] = e.target.checked;
|
||||||
|
this.animateToggle(e.target);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 数值输入
|
||||||
|
document.getElementById('parkingCapacity').addEventListener('input', (e) => {
|
||||||
|
this.settings.parkingCapacity = parseInt(e.target.value) || 0;
|
||||||
|
document.getElementById('parkingCapacityValue').textContent = `${e.target.value} 辆`;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('trafficCapacity').addEventListener('input', (e) => {
|
||||||
|
this.settings.trafficCapacity = parseInt(e.target.value) || 0;
|
||||||
|
document.getElementById('trafficCapacityValue').textContent = `${e.target.value} 辆`;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('speedLimit').addEventListener('input', (e) => {
|
||||||
|
this.settings.speedLimit = parseInt(e.target.value) || 0;
|
||||||
|
document.getElementById('speedLimitValue').textContent = e.target.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('companyProducts').addEventListener('input', (e) => {
|
||||||
|
this.settings.companyProducts = e.target.value;
|
||||||
|
document.getElementById('charCount').textContent = `${e.target.value.length} 字符`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 按钮事件
|
||||||
|
document.getElementById('saveBtn').addEventListener('click', () => {
|
||||||
|
this.saveSettings();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('resetBtn').addEventListener('click', () => {
|
||||||
|
this.resetSettings();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('logoutBtn').addEventListener('click', () => {
|
||||||
|
this.logout();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showSection(sectionName) {
|
||||||
|
// 隐藏所有区域
|
||||||
|
document.querySelectorAll('.section-content').forEach(section => {
|
||||||
|
section.classList.add('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 显示选中区域
|
||||||
|
const targetSection = document.getElementById(`${sectionName}-section`);
|
||||||
|
if (targetSection) {
|
||||||
|
targetSection.classList.remove('hidden');
|
||||||
|
targetSection.classList.add('section-fade');
|
||||||
|
|
||||||
|
// 更新标题
|
||||||
|
const titles = {
|
||||||
|
traffic: { title: '交通管理设置', desc: '配置企业交通管理相关参数' },
|
||||||
|
vehicle: { title: '车辆管理设置', desc: '管理企业区域内车辆容量配置' },
|
||||||
|
safety: { title: '安全设置', desc: '设置企业安全相关参数' },
|
||||||
|
company: { title: '企业信息', desc: '管理企业基本信息和产品描述' }
|
||||||
|
};
|
||||||
|
|
||||||
|
const info = titles[sectionName];
|
||||||
|
document.getElementById('sectionTitle').textContent = info.title;
|
||||||
|
document.getElementById('sectionDescription').textContent = info.desc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNavigation(activeItem) {
|
||||||
|
document.querySelectorAll('.nav-item').forEach(item => {
|
||||||
|
item.classList.remove('active');
|
||||||
|
});
|
||||||
|
activeItem.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAllValues() {
|
||||||
|
// 更新开关状态
|
||||||
|
document.getElementById('noParking').checked = this.settings.noParking;
|
||||||
|
document.getElementById('noTraffic').checked = this.settings.noTraffic;
|
||||||
|
document.getElementById('dangerSource').checked = this.settings.dangerSource;
|
||||||
|
|
||||||
|
// 更新数值输入
|
||||||
|
document.getElementById('parkingCapacity').value = this.settings.parkingCapacity;
|
||||||
|
document.getElementById('trafficCapacity').value = this.settings.trafficCapacity;
|
||||||
|
document.getElementById('speedLimit').value = this.settings.speedLimit;
|
||||||
|
document.getElementById('companyProducts').value = this.settings.companyProducts;
|
||||||
|
|
||||||
|
// 更新显示值
|
||||||
|
document.getElementById('parkingCapacityValue').textContent = `${this.settings.parkingCapacity} 辆`;
|
||||||
|
document.getElementById('trafficCapacityValue').textContent = `${this.settings.trafficCapacity} 辆`;
|
||||||
|
document.getElementById('speedLimitValue').textContent = this.settings.speedLimit;
|
||||||
|
document.getElementById('charCount').textContent = `${this.settings.companyProducts.length} 字符`;
|
||||||
|
}
|
||||||
|
|
||||||
|
animateToggle(toggle) {
|
||||||
|
const slider = toggle.nextElementSibling;
|
||||||
|
anime({
|
||||||
|
targets: slider,
|
||||||
|
scale: [1, 1.1, 1],
|
||||||
|
duration: 300,
|
||||||
|
easing: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
resetSettings() {
|
||||||
|
if (confirm('确定要重置所有设置为默认值吗?')) {
|
||||||
|
localStorage.removeItem('companySettings');
|
||||||
|
this.settings = this.loadSettings();
|
||||||
|
this.updateAllValues();
|
||||||
|
this.showToast('设置已重置', 'info');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
if (confirm('确定要退出登录吗?')) {
|
||||||
|
localStorage.removeItem('companyLogin');
|
||||||
|
sessionStorage.removeItem('companyLogin');
|
||||||
|
window.location.href = 'login.html';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showToast(message, type = 'success') {
|
||||||
|
const toast = document.getElementById('toast');
|
||||||
|
const toastMessage = document.getElementById('toastMessage');
|
||||||
|
const toastIcon = document.getElementById('toastIcon');
|
||||||
|
|
||||||
|
toastMessage.textContent = message;
|
||||||
|
|
||||||
|
// 设置图标和颜色
|
||||||
|
const colors = {
|
||||||
|
success: 'bg-green-500',
|
||||||
|
error: 'bg-red-500',
|
||||||
|
info: 'bg-blue-500',
|
||||||
|
warning: 'bg-yellow-500'
|
||||||
|
};
|
||||||
|
|
||||||
|
toastIcon.className = `w-8 h-8 rounded-full flex items-center justify-center ${colors[type] || colors.success}`;
|
||||||
|
|
||||||
|
// 显示toast
|
||||||
|
toast.classList.add('show');
|
||||||
|
|
||||||
|
// 3秒后隐藏
|
||||||
|
setTimeout(() => {
|
||||||
|
toast.classList.remove('show');
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化应用
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// 检查登录状态
|
||||||
|
const loginData = localStorage.getItem('companyLogin') || sessionStorage.getItem('companyLogin');
|
||||||
|
if (!loginData) {
|
||||||
|
window.location.href = 'login.html';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化设置管理器
|
||||||
|
new SettingsManager();
|
||||||
|
|
||||||
|
// 添加页面加载动画
|
||||||
|
anime({
|
||||||
|
targets: '.glass-card',
|
||||||
|
opacity: [0, 1],
|
||||||
|
translateY: [30, 0],
|
||||||
|
delay: anime.stagger(100),
|
||||||
|
duration: 600,
|
||||||
|
easing: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
220
mobile/alerts.html
Normal file
220
mobile/alerts.html
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>报警管理 - 危化品车辆管理系统</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body { font-family: 'Noto Sans SC', sans-serif; }
|
||||||
|
.dashboard-bg {
|
||||||
|
background: linear-gradient(135deg, #f9fafb 0%, #f3f4f6 100%);
|
||||||
|
background-image: radial-gradient(circle at 1px 1px, rgba(30,58,138,0.1) 1px, transparent 0);
|
||||||
|
background-size: 20px 20px;
|
||||||
|
}
|
||||||
|
.nav-item.active { color: #1e3a8a; }
|
||||||
|
.nav-item.active svg { fill: #1e3a8a; }
|
||||||
|
.alert-card { transition: all 0.3s ease; }
|
||||||
|
.alert-card:active { transform: scale(0.98); }
|
||||||
|
.alert-critical { border-left: 4px solid #ef4444; }
|
||||||
|
.alert-warning { border-left: 4px solid #f59e0b; }
|
||||||
|
.alert-info { border-left: 4px solid #3b82f6; }
|
||||||
|
.pulse-red { animation: pulse-red 1.5s infinite; }
|
||||||
|
@keyframes pulse-red {
|
||||||
|
0%, 100% { background-color: #ef4444; }
|
||||||
|
50% { background-color: #f87171; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="dashboard-bg min-h-screen">
|
||||||
|
<!-- Header -->
|
||||||
|
<header class="bg-white shadow-sm sticky top-0 z-50">
|
||||||
|
<div class="px-4 py-3">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<button onclick="goBack()" class="p-1">
|
||||||
|
<svg class="w-6 h-6 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div>
|
||||||
|
<h1 class="text-lg font-bold text-gray-900">报警管理</h1>
|
||||||
|
<p class="text-xs text-gray-500">紧急事件处理</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<div class="w-2 h-2 bg-red-500 rounded-full pulse-red"></div>
|
||||||
|
<span class="text-xs text-gray-600">1条未处理</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Alert Statistics -->
|
||||||
|
<section class="px-4 py-4">
|
||||||
|
<div class="bg-white rounded-xl shadow-sm p-4">
|
||||||
|
<h2 class="text-base font-semibold text-gray-900 mb-4">报警统计</h2>
|
||||||
|
<div class="grid grid-cols-3 gap-4">
|
||||||
|
<div class="text-center">
|
||||||
|
<p class="text-2xl font-bold text-red-600" id="pending-count">3</p>
|
||||||
|
<p class="text-xs text-gray-500">待处理</p>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<p class="text-2xl font-bold text-yellow-600" id="processing-count">2</p>
|
||||||
|
<p class="text-xs text-gray-500">处理中</p>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<p class="text-2xl font-bold text-green-600" id="resolved-count">15</p>
|
||||||
|
<p class="text-xs text-gray-500">已解决</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Filter Tabs -->
|
||||||
|
<section class="px-4 pb-4">
|
||||||
|
<div class="bg-white rounded-xl shadow-sm p-2">
|
||||||
|
<div class="grid grid-cols-4 gap-1">
|
||||||
|
<button onclick="filterAlerts('all')" class="filter-tab active px-3 py-2 rounded-lg text-xs font-medium text-center bg-blue-100 text-blue-800">
|
||||||
|
全部 <span class="ml-1 bg-blue-200 px-1 rounded" id="count-all">20</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="filterAlerts('pending')" class="filter-tab px-3 py-2 rounded-lg text-xs font-medium text-center text-gray-600">
|
||||||
|
待处理 <span class="ml-1 bg-gray-200 px-1 rounded" id="count-pending">3</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="filterAlerts('processing')" class="filter-tab px-3 py-2 rounded-lg text-xs font-medium text-center text-gray-600">
|
||||||
|
处理中 <span class="ml-1 bg-gray-200 px-1 rounded" id="count-processing">2</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="filterAlerts('resolved')" class="filter-tab px-3 py-2 rounded-lg text-xs font-medium text-center text-gray-600">
|
||||||
|
已解决 <span class="ml-1 bg-gray-200 px-1 rounded" id="count-resolved">15</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Alert List -->
|
||||||
|
<main class="px-4 pb-20">
|
||||||
|
<div id="alert-list" class="space-y-3">
|
||||||
|
<!-- Alert cards will be populated by JavaScript -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Empty State -->
|
||||||
|
<div id="empty-state" class="hidden text-center py-12">
|
||||||
|
<svg class="w-16 h-16 text-gray-300 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||||
|
</svg>
|
||||||
|
<p class="text-gray-500 text-sm">暂无报警信息</p>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Alert Detail Modal -->
|
||||||
|
<div id="alert-modal" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden">
|
||||||
|
<div class="absolute bottom-0 left-0 right-0 bg-white rounded-t-xl max-h-[80vh] overflow-y-auto">
|
||||||
|
<div class="p-4">
|
||||||
|
<div class="flex items-center justify-between mb-4">
|
||||||
|
<h3 class="text-lg font-bold text-gray-900" id="modal-title">报警详情</h3>
|
||||||
|
<button onclick="closeAlertModal()" class="p-1 text-gray-400">
|
||||||
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="alert-modal-content">
|
||||||
|
<!-- Modal content will be populated by JavaScript -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Processing Modal -->
|
||||||
|
<div id="processing-modal" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden">
|
||||||
|
<div class="absolute bottom-0 left-0 right-0 bg-white rounded-t-xl">
|
||||||
|
<div class="p-4">
|
||||||
|
<div class="flex items-center justify-between mb-4">
|
||||||
|
<h3 class="text-lg font-bold text-gray-900">处理报警</h3>
|
||||||
|
<button onclick="closeProcessingModal()" class="p-1 text-gray-400">
|
||||||
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">处理措施</label>
|
||||||
|
<div class="grid grid-cols-2 gap-2">
|
||||||
|
<button onclick="selectAction('contact')" class="action-btn p-3 border border-gray-300 rounded-lg text-sm text-left">
|
||||||
|
<div class="font-medium">联系驾驶员</div>
|
||||||
|
<div class="text-xs text-gray-500">直接通话确认情况</div>
|
||||||
|
</button>
|
||||||
|
<button onclick="selectAction('dispatch')" class="action-btn p-3 border border-gray-300 rounded-lg text-sm text-left">
|
||||||
|
<div class="font-medium">派遣救援</div>
|
||||||
|
<div class="text-xs text-gray-500">安排应急处理团队</div>
|
||||||
|
</button>
|
||||||
|
<button onclick="selectAction('reroute')" class="action-btn p-3 border border-gray-300 rounded-lg text-sm text-left">
|
||||||
|
<div class="font-medium">调整路线</div>
|
||||||
|
<div class="text-xs text-gray-500">重新规划安全路线</div>
|
||||||
|
</button>
|
||||||
|
<button onclick="selectAction('monitor')" class="action-btn p-3 border border-gray-300 rounded-lg text-sm text-left">
|
||||||
|
<div class="font-medium">持续监控</div>
|
||||||
|
<div class="text-xs text-gray-500">加强跟踪观察</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">处理备注</label>
|
||||||
|
<textarea id="processing-note" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm" placeholder="请输入处理过程和结果..."></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="flex space-x-3">
|
||||||
|
<button onclick="submitProcessing()" class="flex-1 bg-blue-600 text-white py-2 px-4 rounded-lg text-sm font-medium">
|
||||||
|
提交处理结果
|
||||||
|
</button>
|
||||||
|
<button onclick="closeProcessingModal()" class="px-4 py-2 border border-gray-300 rounded-lg text-sm text-gray-700">
|
||||||
|
取消
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bottom Navigation -->
|
||||||
|
<nav class="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 z-50">
|
||||||
|
<div class="grid grid-cols-4 h-16">
|
||||||
|
<button onclick="navigateToHome()" class="nav-item flex flex-col items-center justify-center space-y-1 text-gray-400">
|
||||||
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="text-xs">首页</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="navigateToMonitor()" class="nav-item flex flex-col items-center justify-center space-y-1 text-gray-400">
|
||||||
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="text-xs">监控</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="navigateToAlerts()" class="nav-item active flex flex-col items-center justify-center space-y-1">
|
||||||
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<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"/>
|
||||||
|
</svg>
|
||||||
|
<span class="text-xs">报警</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="navigateToTracking()" class="nav-item flex flex-col items-center justify-center space-y-1 text-gray-400">
|
||||||
|
<svg class="w-5 h-5" 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>
|
||||||
|
<span class="text-xs">轨迹</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<script src="main.js"></script>
|
||||||
|
<script>
|
||||||
|
// Initialize alerts page
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
loadAlerts();
|
||||||
|
updateAlertCounts();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
117
mobile/design.md
Normal file
117
mobile/design.md
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
# 危化品车辆管理应用 - 设计风格指南
|
||||||
|
|
||||||
|
## 设计理念
|
||||||
|
|
||||||
|
### 核心设计原则
|
||||||
|
**专业可靠**:体现工业级安全管理的专业性,建立用户信任感
|
||||||
|
**清晰高效**:信息层次清晰,操作流程简洁,适合移动端快速操作
|
||||||
|
**安全警示**:通过颜色和视觉元素强调安全重要性,突出关键信息
|
||||||
|
**现代简洁**:采用现代化设计语言,避免过度装饰,专注功能性
|
||||||
|
|
||||||
|
### 色彩系统
|
||||||
|
**主色调**:深海蓝 (#1e3a8a) - 体现专业性和信任感
|
||||||
|
**辅助色**:
|
||||||
|
- 安全绿 (#10b981) - 正常状态、成功操作
|
||||||
|
- 警告橙 (#f59e0b) - 注意事项、中等风险
|
||||||
|
- 危险红 (#ef4444) - 报警状态、高风险
|
||||||
|
- 中性灰 (#6b7280) - 辅助信息、禁用状态
|
||||||
|
|
||||||
|
**背景色系**:
|
||||||
|
- 主背景:浅灰白 (#f9fafb)
|
||||||
|
- 卡片背景:纯白 (#ffffff)
|
||||||
|
- 分割线:浅灰 (#e5e7eb)
|
||||||
|
|
||||||
|
### 字体系统
|
||||||
|
**标题字体**:Noto Sans SC Bold - 用于页面标题和重要信息
|
||||||
|
**正文字体**:Noto Sans SC Regular - 用于一般内容和描述文字
|
||||||
|
**数字字体**:等宽字体用于数据显示,确保对齐和易读性
|
||||||
|
|
||||||
|
**字体大小规范**:
|
||||||
|
- 大标题:24px (1.5rem)
|
||||||
|
- 中标题:18px (1.125rem)
|
||||||
|
- 正文:14px (0.875rem)
|
||||||
|
- 小字:12px (0.75rem)
|
||||||
|
|
||||||
|
### 视觉语言
|
||||||
|
**图标风格**:线性图标,2px描边,圆角处理,保持简洁现代感
|
||||||
|
**卡片设计**:轻微圆角 (8px),微妙阴影,突出层次感
|
||||||
|
**按钮设计**:圆角按钮,明确的视觉反馈,适合触摸操作
|
||||||
|
**状态指示**:使用颜色、图标和文字组合,确保信息传达清晰
|
||||||
|
|
||||||
|
## 视觉效果
|
||||||
|
|
||||||
|
### 动画效果库选择
|
||||||
|
**Anime.js**:用于页面切换和元素动画
|
||||||
|
- 卡片进入动画:淡入 + 上移效果
|
||||||
|
- 状态切换动画:颜色渐变和尺寸变化
|
||||||
|
- 加载动画:脉冲效果和进度指示
|
||||||
|
|
||||||
|
**ECharts.js**:用于数据可视化
|
||||||
|
- 车辆状态分布饼图
|
||||||
|
- 报警趋势折线图
|
||||||
|
- 实时数据仪表盘
|
||||||
|
|
||||||
|
### 背景效果
|
||||||
|
**主背景**:纯色背景配合微妙的几何图案
|
||||||
|
- 使用CSS生成的点阵网格作为装饰背景
|
||||||
|
- 保持低对比度,不影响内容可读性
|
||||||
|
|
||||||
|
**卡片效果**:
|
||||||
|
- 轻微阴影:box-shadow: 0 1px 3px rgba(0,0,0,0.1)
|
||||||
|
- 悬浮状态:轻微上浮效果(仅桌面端)
|
||||||
|
|
||||||
|
### 交互反馈
|
||||||
|
**触摸反馈**:
|
||||||
|
- 按钮按下时轻微缩放效果 (scale: 0.95)
|
||||||
|
- 列表项点击时背景色变化
|
||||||
|
- 开关组件的平滑过渡动画
|
||||||
|
|
||||||
|
**状态动画**:
|
||||||
|
- 实时数据更新时的脉冲效果
|
||||||
|
- 报警状态的闪烁提醒
|
||||||
|
- 加载状态的旋转动画
|
||||||
|
|
||||||
|
### 图标和图形元素
|
||||||
|
**功能图标**:
|
||||||
|
- 车辆监控:卡车图标
|
||||||
|
- 报警管理:警告三角形图标
|
||||||
|
- 轨迹回放:路径箭头图标
|
||||||
|
- 系统设置:齿轮图标
|
||||||
|
|
||||||
|
**状态图标**:
|
||||||
|
- 在线状态:绿色圆点
|
||||||
|
- 离线状态:灰色圆点
|
||||||
|
- 报警状态:红色感叹号
|
||||||
|
- 警告状态:黄色三角形
|
||||||
|
|
||||||
|
### 布局设计原则
|
||||||
|
**信息层次**:
|
||||||
|
- 重要信息使用大字体和醒目颜色
|
||||||
|
- 次要信息使用小字体和中性色
|
||||||
|
- 相关信息分组展示,使用卡片容器
|
||||||
|
|
||||||
|
**空间利用**:
|
||||||
|
- 移动端优先,确保内容在小屏幕上清晰可读
|
||||||
|
- 合理的留白,避免信息过于密集
|
||||||
|
- 关键操作按钮放置在拇指易触及区域
|
||||||
|
|
||||||
|
**响应式设计**:
|
||||||
|
- 适配不同屏幕尺寸
|
||||||
|
- 横竖屏切换时的布局调整
|
||||||
|
- 考虑刘海屏和导航栏的适配
|
||||||
|
|
||||||
|
### 特殊视觉元素
|
||||||
|
**报警通知**:
|
||||||
|
- 顶部横幅通知,使用渐变背景
|
||||||
|
- 紧急报警使用全屏遮罩和震动效果
|
||||||
|
- 声音提醒配合视觉闪烁
|
||||||
|
|
||||||
|
**数据展示**:
|
||||||
|
- 使用进度条和仪表盘展示关键指标
|
||||||
|
- 数字数据使用等宽字体,便于对比
|
||||||
|
- 趋势图使用品牌色彩,突出变化
|
||||||
|
|
||||||
|
**加载状态**:
|
||||||
|
- 骨架屏替代传统loading动画
|
||||||
|
- 分页加载时的无限滚动效果
|
||||||
|
- 数据为空时的友好提示插图
|
||||||
222
mobile/index.html
Normal file
222
mobile/index.html
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>危化品车辆监控系统</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body { font-family: 'Noto Sans SC', sans-serif; }
|
||||||
|
.dashboard-bg {
|
||||||
|
background: linear-gradient(135deg, #f9fafb 0%, #f3f4f6 100%);
|
||||||
|
background-image: radial-gradient(circle at 1px 1px, rgba(30,58,138,0.1) 1px, transparent 0);
|
||||||
|
background-size: 20px 20px;
|
||||||
|
}
|
||||||
|
.nav-item.active { color: #1e3a8a; }
|
||||||
|
.nav-item.active svg { fill: #1e3a8a; }
|
||||||
|
.stat-card { transition: all 0.3s ease; }
|
||||||
|
.stat-card:active { transform: scale(0.98); }
|
||||||
|
.alert-unread { border-left: 4px solid #ef4444; }
|
||||||
|
.alert-read { border-left: 4px solid #6b7280; }
|
||||||
|
.pulse-dot { animation: pulse 2s infinite; }
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% { opacity: 1; }
|
||||||
|
50% { opacity: 0.5; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="dashboard-bg min-h-screen">
|
||||||
|
<!-- Header -->
|
||||||
|
<header class="bg-white shadow-sm sticky top-0 z-50">
|
||||||
|
<div class="px-4 py-3">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<div class="w-8 h-8 bg-blue-800 rounded-lg flex items-center justify-center">
|
||||||
|
<svg class="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M3 4a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-1 1H4a1 1 0 01-1-1V4zM3 10a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H4a1 1 0 01-1-1v-6zM14 9a1 1 0 00-1 1v6a1 1 0 001 1h2a1 1 0 001-1v-6a1 1 0 00-1-1h-2z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h1 class="text-lg font-bold text-gray-900">危化品车辆监控</h1>
|
||||||
|
<p class="text-xs text-gray-500">园区安全管理系统</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<div class="w-2 h-2 bg-green-500 rounded-full pulse-dot"></div>
|
||||||
|
<span class="text-xs text-gray-600">在线</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="px-4 py-6 pb-20">
|
||||||
|
<!-- Vehicle Statistics -->
|
||||||
|
<section class="mb-6">
|
||||||
|
<h2 class="text-base font-semibold text-gray-900 mb-4">园区车辆概览</h2>
|
||||||
|
<div class="grid grid-cols-1 gap-4">
|
||||||
|
<!-- Total Vehicles -->
|
||||||
|
<div class="stat-card bg-white rounded-xl shadow-sm p-4" onclick="showVehicleDetail('total')">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center">
|
||||||
|
<svg class="w-6 h-6 text-blue-600" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 8h-3V4H3c-1.1 0-2 .9-2 2v11h2c0 1.66 1.34 3 3 3s3-1.34 3-3h6c0 1.66 1.34 3 3 3s3-1.34 3-3h2v-5l-3-4zM6 18.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm13.5-9l1.96 2.5H17V9.5h2.5zm-1.5 9c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-2xl font-bold text-blue-600" id="total-vehicles">156</p>
|
||||||
|
<p class="text-sm text-gray-500">园区车辆总数</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-right">
|
||||||
|
<p class="text-xs text-green-600">+5 今日新增</p>
|
||||||
|
<p class="text-xs text-gray-400">较前日</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Today's Applications -->
|
||||||
|
<div class="stat-card bg-white rounded-xl shadow-sm p-4" onclick="showVehicleDetail('applications')">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
<div class="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center">
|
||||||
|
<svg class="w-6 h-6 text-green-600" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<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"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-2xl font-bold text-green-600" id="today-applications">23</p>
|
||||||
|
<p class="text-sm text-gray-500">今日入园申请</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-right">
|
||||||
|
<p class="text-xs text-blue-600">18 已批准</p>
|
||||||
|
<p class="text-xs text-yellow-600">5 待审批</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Active Orders -->
|
||||||
|
<div class="stat-card bg-white rounded-xl shadow-sm p-4" onclick="showVehicleDetail('orders')">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
<div class="w-12 h-12 bg-orange-100 rounded-lg flex items-center justify-center">
|
||||||
|
<svg class="w-6 h-6 text-orange-600" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-2xl font-bold text-orange-600" id="active-orders">89</p>
|
||||||
|
<p class="text-sm text-gray-500">有运单车辆数</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-right">
|
||||||
|
<p class="text-xs text-green-600">67 运输中</p>
|
||||||
|
<p class="text-xs text-gray-400">22 待装卸</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Recent Alerts -->
|
||||||
|
<section class="mb-6">
|
||||||
|
<div class="flex items-center justify-between mb-4">
|
||||||
|
<h2 class="text-base font-semibold text-gray-900">最新报警</h2>
|
||||||
|
<button onclick="navigateToMonitor()" class="text-xs text-blue-600 font-medium">查看全部</button>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-3" id="recent-alerts">
|
||||||
|
<!-- Alerts will be populated by JavaScript -->
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Quick Actions -->
|
||||||
|
<section class="mb-6">
|
||||||
|
<h2 class="text-base font-semibold text-gray-900 mb-4">快速操作</h2>
|
||||||
|
<div class="grid grid-cols-2 gap-3">
|
||||||
|
<button onclick="navigateToMonitor()" class="bg-white rounded-xl shadow-sm p-4 text-left hover:shadow-md transition-shadow">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<div class="w-10 h-10 bg-red-100 rounded-lg flex items-center justify-center">
|
||||||
|
<svg class="w-6 h-6 text-red-600" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<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"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="font-medium text-gray-900">报警管理</p>
|
||||||
|
<p class="text-xs text-gray-500">处理报警事件</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button onclick="navigateToTracking()" class="bg-white rounded-xl shadow-sm p-4 text-left hover:shadow-md transition-shadow">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
|
||||||
|
<svg class="w-6 h-6 text-blue-600" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="font-medium text-gray-900">轨迹查询</p>
|
||||||
|
<p class="text-xs text-gray-500">车辆轨迹回放</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Statistics Detail Modal -->
|
||||||
|
<div id="stats-modal" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden">
|
||||||
|
<div class="absolute bottom-0 left-0 right-0 bg-white rounded-t-xl max-h-[70vh] overflow-y-auto">
|
||||||
|
<div class="p-4">
|
||||||
|
<div class="flex items-center justify-between mb-4">
|
||||||
|
<h3 class="text-lg font-bold text-gray-900" id="stats-modal-title">统计详情</h3>
|
||||||
|
<button onclick="closeStatsModal()" class="p-1 text-gray-400">
|
||||||
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="stats-modal-content">
|
||||||
|
<!-- Modal content will be populated by JavaScript -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bottom Navigation -->
|
||||||
|
<nav class="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 z-50">
|
||||||
|
<div class="grid grid-cols-3 h-16">
|
||||||
|
<button onclick="navigateToHome()" class="nav-item active flex flex-col items-center justify-center space-y-1">
|
||||||
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="text-xs">首页</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="navigateToMonitor()" class="nav-item flex flex-col items-center justify-center space-y-1 text-gray-400">
|
||||||
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="text-xs">监控</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="navigateToTracking()" class="nav-item flex flex-col items-center justify-center space-y-1 text-gray-400">
|
||||||
|
<svg class="w-5 h-5" 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>
|
||||||
|
<span class="text-xs">轨迹</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<script src="main.js"></script>
|
||||||
|
<script>
|
||||||
|
// Initialize home page
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
loadRecentAlerts();
|
||||||
|
animateStatistics();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
85
mobile/interaction.md
Normal file
85
mobile/interaction.md
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# 危化品车辆监控系统(简化版)- 交互设计
|
||||||
|
|
||||||
|
## 核心交互功能
|
||||||
|
|
||||||
|
### 1. 园区车辆概览交互
|
||||||
|
**主要交互流程:**
|
||||||
|
- 用户进入首页,直接显示三个核心统计数字
|
||||||
|
- 数字采用大字体显示,配合图标增强视觉效果
|
||||||
|
- 点击统计卡片可查看详细信息(模态窗口显示)
|
||||||
|
- 数据每30秒自动刷新,用户可手动下拉刷新
|
||||||
|
|
||||||
|
**交互细节:**
|
||||||
|
- 使用数字动画效果,从0递增到目标数值
|
||||||
|
- 统计卡片使用不同颜色区分:蓝色(总数)、绿色(申请)、橙色(运单)
|
||||||
|
- 卡片点击时轻微缩放反馈,增强交互体验
|
||||||
|
|
||||||
|
### 2. 报警信息管理交互
|
||||||
|
**主要交互流程:**
|
||||||
|
- 报警信息以列表形式展示,按时间倒序排列
|
||||||
|
- 每条报警显示车牌号、报警类型、位置、时间
|
||||||
|
- 点击报警项可切换"未查看"/"已查看"状态
|
||||||
|
- 支持按状态筛选报警(全部、未查看、已查看)
|
||||||
|
|
||||||
|
**报警类型和显示:**
|
||||||
|
- **违停报警**: 红色标识,显示"XXX车牌违停,在XX位置,XX时间触发"
|
||||||
|
- **超速报警**: 橙色标识,显示"XXX车牌超速,在XX位置,XX时间触发"
|
||||||
|
- **其他报警**: 黄色标识,根据具体情况显示
|
||||||
|
|
||||||
|
**状态管理:**
|
||||||
|
- **未查看**: 红色圆点标识,文字加粗显示
|
||||||
|
- **已查看**: 灰色圆点标识,文字正常显示
|
||||||
|
- 状态切换通过点击实现,切换后更新视觉样式
|
||||||
|
|
||||||
|
### 3. 车辆轨迹查询交互
|
||||||
|
**主要交互流程:**
|
||||||
|
- 用户在输入框中输入车牌号(支持模糊匹配)
|
||||||
|
- 点击查询按钮,系统显示该车辆的轨迹信息
|
||||||
|
- 轨迹信息包括:行驶路线、停留点、报警记录
|
||||||
|
- 支持查看最近7天的轨迹记录
|
||||||
|
|
||||||
|
**查询功能:**
|
||||||
|
- 支持车牌号前缀搜索(如输入"京A"显示所有京A车辆)
|
||||||
|
- 输入框提供历史查询记录快捷选择
|
||||||
|
- 查询结果以时间轴形式展示关键轨迹点
|
||||||
|
- 每个轨迹点显示时间、位置、状态信息
|
||||||
|
|
||||||
|
### 4. 页面导航交互
|
||||||
|
**底部导航栏:**
|
||||||
|
- **首页**: 显示园区概览和最新报警
|
||||||
|
- **监控**: 专门的报警管理页面,功能更完整
|
||||||
|
- **轨迹**: 车辆轨迹查询专用页面
|
||||||
|
|
||||||
|
**导航特性:**
|
||||||
|
- 当前页面图标高亮显示
|
||||||
|
- 页面切换使用平滑过渡动画
|
||||||
|
- 保持用户操作上下文,避免数据丢失
|
||||||
|
|
||||||
|
## 简化交互原则
|
||||||
|
|
||||||
|
### 操作简化
|
||||||
|
- 减少多步骤操作,一键完成主要功能
|
||||||
|
- 避免复杂的表单填写,使用选择代替输入
|
||||||
|
- 关键操作提供确认提示,防止误操作
|
||||||
|
|
||||||
|
### 信息显示
|
||||||
|
- 突出关键信息,次要信息可折叠显示
|
||||||
|
- 使用图标和颜色编码,减少文字阅读负担
|
||||||
|
- 重要数据使用大字体和醒目颜色
|
||||||
|
|
||||||
|
### 响应速度
|
||||||
|
- 所有操作提供即时视觉反馈
|
||||||
|
- 数据加载使用骨架屏,避免空白等待
|
||||||
|
- 优化动画时长,确保流畅体验
|
||||||
|
|
||||||
|
## 移动端适配
|
||||||
|
|
||||||
|
### 触摸交互
|
||||||
|
- 所有可点击元素最小44px×44px触摸区域
|
||||||
|
- 支持滑动查看更多信息
|
||||||
|
- 长按显示快捷操作菜单
|
||||||
|
|
||||||
|
### 屏幕适配
|
||||||
|
- 适配不同屏幕尺寸和分辨率
|
||||||
|
- 横竖屏切换保持功能可用性
|
||||||
|
- 考虑刘海屏和底部指示条适配
|
||||||
543
mobile/main.js
Normal file
543
mobile/main.js
Normal file
@@ -0,0 +1,543 @@
|
|||||||
|
// 危化品车辆监控系统(简化版)- 主要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+300,2024-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+200,2024-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;
|
||||||
135
mobile/monitor.html
Normal file
135
mobile/monitor.html
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>报警监控 - 危化品车辆监控系统</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body { font-family: 'Noto Sans SC', sans-serif; }
|
||||||
|
.dashboard-bg {
|
||||||
|
background: linear-gradient(135deg, #f9fafb 0%, #f3f4f6 100%);
|
||||||
|
background-image: radial-gradient(circle at 1px 1px, rgba(30,58,138,0.1) 1px, transparent 0);
|
||||||
|
background-size: 20px 20px;
|
||||||
|
}
|
||||||
|
.nav-item.active { color: #1e3a8a; }
|
||||||
|
.nav-item.active svg { fill: #1e3a8a; }
|
||||||
|
.alert-card { transition: all 0.3s ease; }
|
||||||
|
.alert-card:active { transform: scale(0.98); }
|
||||||
|
.alert-unread { border-left: 4px solid #ef4444; }
|
||||||
|
.alert-read { border-left: 4px solid #6b7280; }
|
||||||
|
.filter-tab.active { background: #dbeafe; color: #1e40af; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="dashboard-bg min-h-screen">
|
||||||
|
<!-- Header -->
|
||||||
|
<header class="bg-white shadow-sm sticky top-0 z-50">
|
||||||
|
<div class="px-4 py-3">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<button onclick="goBack()" class="p-1">
|
||||||
|
<svg class="w-6 h-6 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div>
|
||||||
|
<h1 class="text-lg font-bold text-gray-900">报警监控</h1>
|
||||||
|
<p class="text-xs text-gray-500">实时报警管理</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<span class="text-xs text-gray-600" id="unread-count">3条未查看</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Alert Statistics -->
|
||||||
|
<section class="px-4 py-4">
|
||||||
|
<div class="bg-white rounded-xl shadow-sm p-4">
|
||||||
|
<h2 class="text-base font-semibold text-gray-900 mb-4">报警统计</h2>
|
||||||
|
<div class="grid grid-cols-3 gap-4">
|
||||||
|
<div class="text-center">
|
||||||
|
<p class="text-2xl font-bold text-red-600" id="total-alerts">12</p>
|
||||||
|
<p class="text-xs text-gray-500">今日报警</p>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<p class="text-2xl font-bold text-orange-600" id="unread-alerts">3</p>
|
||||||
|
<p class="text-xs text-gray-500">未查看</p>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<p class="text-2xl font-bold text-green-600" id="read-alerts">9</p>
|
||||||
|
<p class="text-xs text-gray-500">已查看</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Filter Tabs -->
|
||||||
|
<section class="px-4 pb-4">
|
||||||
|
<div class="bg-white rounded-xl shadow-sm p-2">
|
||||||
|
<div class="grid grid-cols-3 gap-1">
|
||||||
|
<button onclick="filterAlerts('all')" class="filter-tab active px-3 py-2 rounded-lg text-xs font-medium text-center">
|
||||||
|
全部 <span class="ml-1 bg-blue-200 px-1 rounded" id="count-all">12</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="filterAlerts('unread')" class="filter-tab px-3 py-2 rounded-lg text-xs font-medium text-center text-gray-600">
|
||||||
|
未查看 <span class="ml-1 bg-gray-200 px-1 rounded" id="count-unread">3</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="filterAlerts('read')" class="filter-tab px-3 py-2 rounded-lg text-xs font-medium text-center text-gray-600">
|
||||||
|
已查看 <span class="ml-1 bg-gray-200 px-1 rounded" id="count-read">9</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Alert List -->
|
||||||
|
<main class="px-4 pb-20">
|
||||||
|
<div id="alert-list" class="space-y-3">
|
||||||
|
<!-- Alert cards will be populated by JavaScript -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Empty State -->
|
||||||
|
<div id="empty-state" class="hidden text-center py-12">
|
||||||
|
<svg class="w-16 h-16 text-gray-300 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||||
|
</svg>
|
||||||
|
<p class="text-gray-500 text-sm">暂无报警信息</p>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Bottom Navigation -->
|
||||||
|
<nav class="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 z-50">
|
||||||
|
<div class="grid grid-cols-3 h-16">
|
||||||
|
<button onclick="navigateToHome()" class="nav-item flex flex-col items-center justify-center space-y-1 text-gray-400">
|
||||||
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="text-xs">首页</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="navigateToMonitor()" class="nav-item active flex flex-col items-center justify-center space-y-1">
|
||||||
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="text-xs">监控</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="navigateToTracking()" class="nav-item flex flex-col items-center justify-center space-y-1 text-gray-400">
|
||||||
|
<svg class="w-5 h-5" 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>
|
||||||
|
<span class="text-xs">轨迹</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<script src="main.js"></script>
|
||||||
|
<script>
|
||||||
|
// Initialize monitor page
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
loadAllAlerts();
|
||||||
|
updateAlertStats();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
46
mobile/outline.md
Normal file
46
mobile/outline.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# 危化品车辆监控系统(简化版)- 项目概述
|
||||||
|
|
||||||
|
## 项目目标
|
||||||
|
创建一个简洁高效的移动端危化品车辆监控系统,专注于核心监控功能,提供园区车辆概览、报警信息展示和轨迹查询功能。
|
||||||
|
|
||||||
|
## 核心功能模块
|
||||||
|
|
||||||
|
### 1. 园区车辆概览
|
||||||
|
- **园区车辆总数**: 实时显示园区内车辆总数
|
||||||
|
- **当日入园申请车辆数**: 显示今日申请入园的车辆数量
|
||||||
|
- **有运单车辆数**: 显示正在执行运输任务的车辆数量
|
||||||
|
|
||||||
|
### 2. 报警信息管理
|
||||||
|
- **报警列表**: 显示所有报警事件
|
||||||
|
- **报警详情**: 显示报警车辆、类型、位置、时间
|
||||||
|
- **状态管理**: 未查看/已查看状态切换
|
||||||
|
- **报警类型**: 违停、超速等常见报警
|
||||||
|
|
||||||
|
### 3. 车辆轨迹查询
|
||||||
|
- **车牌号输入**: 支持手动输入车牌号查询
|
||||||
|
- **轨迹展示**: 显示车辆历史轨迹信息
|
||||||
|
- **简化查询**: 快速查询最近轨迹记录
|
||||||
|
|
||||||
|
## 页面结构规划
|
||||||
|
|
||||||
|
### 主页面 (index.html)
|
||||||
|
- 顶部:应用标题和在线状态
|
||||||
|
- 中部:三个核心统计卡片(园区车辆总数、入园申请、运单车辆)
|
||||||
|
- 下部:最新报警信息列表
|
||||||
|
- 底部:导航栏(首页、监控、轨迹)
|
||||||
|
|
||||||
|
### 车辆监控页面 (monitor.html)
|
||||||
|
- 车辆统计概览
|
||||||
|
- 报警信息列表
|
||||||
|
- 报警状态切换功能
|
||||||
|
|
||||||
|
### 轨迹查询页面 (tracking.html)
|
||||||
|
- 车牌号输入区域
|
||||||
|
- 轨迹查询结果展示
|
||||||
|
- 轨迹详情信息
|
||||||
|
|
||||||
|
## 技术实现要点
|
||||||
|
- 响应式移动端设计
|
||||||
|
- 简化数据结构
|
||||||
|
- 快速查询功能
|
||||||
|
- 清晰的视觉层次
|
||||||
BIN
mobile/resources/alert-icon.png
Normal file
BIN
mobile/resources/alert-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 568 KiB |
BIN
mobile/resources/dashboard-bg.png
Normal file
BIN
mobile/resources/dashboard-bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 709 KiB |
BIN
mobile/resources/tracking-icon.png
Normal file
BIN
mobile/resources/tracking-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 466 KiB |
BIN
mobile/resources/truck-icon.png
Normal file
BIN
mobile/resources/truck-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 332 KiB |
190
mobile/tracking.html
Normal file
190
mobile/tracking.html
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>轨迹查询 - 危化品车辆监控系统</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body { font-family: 'Noto Sans SC', sans-serif; }
|
||||||
|
.dashboard-bg {
|
||||||
|
background: linear-gradient(135deg, #f9fafb 0%, #f3f4f6 100%);
|
||||||
|
background-image: radial-gradient(circle at 1px 1px, rgba(30,58,138,0.1) 1px, transparent 0);
|
||||||
|
background-size: 20px 20px;
|
||||||
|
}
|
||||||
|
.nav-item.active { color: #1e3a8a; }
|
||||||
|
.nav-item.active svg { fill: #1e3a8a; }
|
||||||
|
.search-btn:active { transform: scale(0.95); }
|
||||||
|
.result-card { transition: all 0.3s ease; }
|
||||||
|
.result-card:active { transform: scale(0.98); }
|
||||||
|
.timeline-item { position: relative; }
|
||||||
|
.timeline-item::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 15px;
|
||||||
|
top: 40px;
|
||||||
|
bottom: -10px;
|
||||||
|
width: 2px;
|
||||||
|
background: #e5e7eb;
|
||||||
|
}
|
||||||
|
.timeline-item:last-child::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="dashboard-bg min-h-screen">
|
||||||
|
<!-- Header -->
|
||||||
|
<header class="bg-white shadow-sm sticky top-0 z-50">
|
||||||
|
<div class="px-4 py-3">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<button onclick="goBack()" class="p-1">
|
||||||
|
<svg class="w-6 h-6 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div>
|
||||||
|
<h1 class="text-lg font-bold text-gray-900">轨迹查询</h1>
|
||||||
|
<p class="text-xs text-gray-500">车辆轨迹回放</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Search Section -->
|
||||||
|
<section class="px-4 py-4">
|
||||||
|
<div class="bg-white rounded-xl shadow-sm p-4">
|
||||||
|
<h2 class="text-base font-semibold text-gray-900 mb-4">输入车牌号查询轨迹</h2>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="relative">
|
||||||
|
<input type="text" id="license-plate-input" placeholder="请输入车牌号,如:京A12345"
|
||||||
|
class="w-full px-4 py-3 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
onkeyup="handleInputChange()">
|
||||||
|
<svg class="absolute right-3 top-3 w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Quick Search Suggestions -->
|
||||||
|
<div class="flex flex-wrap gap-2" id="search-suggestions">
|
||||||
|
<button onclick="quickSearch('京A')" class="px-3 py-1 bg-gray-100 text-gray-700 rounded-lg text-xs">京A</button>
|
||||||
|
<button onclick="quickSearch('京B')" class="px-3 py-1 bg-gray-100 text-gray-700 rounded-lg text-xs">京B</button>
|
||||||
|
<button onclick="quickSearch('京C')" class="px-3 py-1 bg-gray-100 text-gray-700 rounded-lg text-xs">京C</button>
|
||||||
|
<button onclick="quickSearch('京D')" class="px-3 py-1 bg-gray-100 text-gray-700 rounded-lg text-xs">京D</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button onclick="searchTrajectory()" class="search-btn w-full bg-blue-600 text-white py-3 px-4 rounded-lg text-sm font-medium hover:bg-blue-700 transition-colors">
|
||||||
|
查询轨迹
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Search Results -->
|
||||||
|
<section id="results-section" class="px-4 pb-20 hidden">
|
||||||
|
<div class="bg-white rounded-xl shadow-sm p-4 mb-4">
|
||||||
|
<div class="flex items-center justify-between mb-4">
|
||||||
|
<h2 class="text-base font-semibold text-gray-900">查询结果</h2>
|
||||||
|
<button onclick="clearResults()" class="text-xs text-gray-500">清除结果</button>
|
||||||
|
</div>
|
||||||
|
<div id="search-results">
|
||||||
|
<!-- Results will be populated by JavaScript -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Trajectory Details -->
|
||||||
|
<div id="trajectory-details" class="bg-white rounded-xl shadow-sm p-4 hidden">
|
||||||
|
<h3 class="text-base font-semibold text-gray-900 mb-4">轨迹详情</h3>
|
||||||
|
<div class="space-y-3" id="trajectory-timeline">
|
||||||
|
<!-- Timeline will be populated by JavaScript -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Recent Searches -->
|
||||||
|
<section class="px-4 pb-20" id="recent-searches-section">
|
||||||
|
<div class="bg-white rounded-xl shadow-sm p-4">
|
||||||
|
<h2 class="text-base font-semibold text-gray-900 mb-4">最近查询</h2>
|
||||||
|
<div class="space-y-2" id="recent-searches">
|
||||||
|
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<div class="w-8 h-8 bg-blue-100 rounded-lg flex items-center justify-center">
|
||||||
|
<svg class="w-4 h-4 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm font-medium text-gray-900">京A12345</p>
|
||||||
|
<p class="text-xs text-gray-500">2024-01-15 14:30</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button onclick="quickSearch('京A12345')" class="text-xs text-blue-600">查询</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<div class="w-8 h-8 bg-green-100 rounded-lg flex items-center justify-center">
|
||||||
|
<svg class="w-4 h-4 text-green-600" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<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"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm font-medium text-gray-900">京B67890</p>
|
||||||
|
<p class="text-xs text-gray-500">2024-01-15 13:15</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button onclick="quickSearch('京B67890')" class="text-xs text-blue-600">查询</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<div class="w-8 h-8 bg-orange-100 rounded-lg flex items-center justify-center">
|
||||||
|
<svg class="w-4 h-4 text-orange-600" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm font-medium text-gray-900">京C24680</p>
|
||||||
|
<p class="text-xs text-gray-500">2024-01-15 11:20</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button onclick="quickSearch('京C24680')" class="text-xs text-blue-600">查询</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Bottom Navigation -->
|
||||||
|
<nav class="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 z-50">
|
||||||
|
<div class="grid grid-cols-3 h-16">
|
||||||
|
<button onclick="navigateToHome()" class="nav-item flex flex-col items-center justify-center space-y-1 text-gray-400">
|
||||||
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="text-xs">首页</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="navigateToMonitor()" class="nav-item flex flex-col items-center justify-center space-y-1 text-gray-400">
|
||||||
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="text-xs">监控</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="navigateToTracking()" class="nav-item active flex flex-col items-center justify-center space-y-1">
|
||||||
|
<svg class="w-5 h-5" 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>
|
||||||
|
<span class="text-xs">轨迹</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<script src="main.js"></script>
|
||||||
|
<script>
|
||||||
|
// Initialize tracking page
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
loadRecentSearches();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user