This commit is contained in:
2025-08-08 10:38:10 +08:00
commit c7dc4d6c02
114 changed files with 11010 additions and 0 deletions
+66
View File
@@ -0,0 +1,66 @@
{% extends "base.html" %}
{% block content %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/about.css') }}">
<div class="about-container">
<div class="about-header">
<h1>关于本平台</h1>
</div>
<div class="about-content">
<div class="about-section">
<h2>平台简介</h2>
<p>本平台是一个专门用于管理和处理政企重点项目运维工单的平台。平台支持工单的派发、处理、统计等功能,旨在提高运维工作效率和管理透明度。</p>
</div>
<div class="about-section">
<ul style="list-style-type: disc; margin-left: 20px;">
<li>工单管理:支持普通工单和手工故障单的创建、派发和处理</li>
<li>数据统计:提供周期性的工单处理统计和运维奖励计算</li>
<li>历史记录:记录和查询历史工单信息</li>
<li>设备管理:支持设备信息的管理和维护</li>
</ul>
</div>
<div class="about-section">
<h2>常用功能</h2>
<ul class="quick-links">
<li class="quick-link-item">
<a href="https://web.wps.cn/wo/sl/v3Qs5r1?app_id=3SDuJ9CDIyuC7KvoKUM6uT" class="quick-link">"4+N"项目关联账号查询</a>
</li>
<li class="quick-link-item">
<a href="https://www.kdocs.cn/l/crjbtOrFLgoD" class="quick-link">"4+N"项目和数字校园设备安装明细</a>
</li>
<li class="quick-link-item">
<a href="https://www.kdocs.cn/l/cdnzqi5GvcHo" class="quick-link">"4+N"项目云监控平台安装指南</a>
</li>
<li class="quick-link-item">
<a href="https://www.kdocs.cn/l/cg1kMuthlS1j" class="quick-link">大化教育城域网故障排查指南</a>
</li>
<li class="quick-link-item">
<a href="https://onu.dhdx.fun/statistics" class="quick-link">大化教育城域网终端信息管理系统</a>
</li>
<li class="quick-link-item">
<a href="https://www.kdocs.cn/l/cgJAP7E065tK" class="quick-link">大化教育城域网OLT安装规划表</a>
</li>
</ul>
</div>
<div class="about-section">
<h2>信息</h2>
<div class="dev-info">
<p>当前平台处于试行阶段,如果遇到页面错误,功能不正常等问题请截图后与管理员联系;</p>
<p>如果您有任何建议或意见,请随时与我们联系。</p>
<p>感谢您的支持和使用!</p>
</div>
<div class="dev-info">
<p><strong>联系:</strong>韦矍森</p>
<p><strong>电话:</strong>19977899008</p>
<p><strong>部门:</strong>大化政企业务交付运营中心</p>
</div>
</div>
<div class="footer">
<p>© 2025 政企重点项目运维管理平台</p>
</div>
</div>
</div>
{% endblock %}
+125
View File
@@ -0,0 +1,125 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>大化重点项目工单管理平台</title>
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
</head>
<body>
<nav class="navbar navbar-expand-lg nav_container">
<div class="container-fluid">
<a class="nav_brand">
{% if request.endpoint == 'dispatch' %}
故障派单
{% elif request.endpoint == 'receive' %}
在途工单
{% elif request.endpoint == 'statistics' %}
工单统计
{% elif request.endpoint == 'history' %}
历史工单
{% elif request.endpoint == 'manual' %}
申报故障
{% elif request.endpoint == 'dashboard' %}
设备仪表盘
{% else %}
重点项目工单管理平台
{% endif %}
</a>
<button class="navbar-toggler nav_toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon nav_toggler_icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0 nav_menu">
{% if current_user.is_authenticated %}
{% if current_user.role == '统计员' %}
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'dashboard' }}" href="{{ url_for('dashboard') }}">仪表盘</a>
</li>
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'dispatch' }}" href="{{ url_for('dispatch') }}">派单</a>
</li>
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'receive' }}" href="{{ url_for('receive') }}">在途</a>
</li>
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'manual' }}" href="{{ url_for('manual') }}">申报</a>
</li>
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'statistics' }}" href="{{ url_for('statistics') }}">统计</a>
</li>
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'history' }}" href="{{ url_for('history') }}">历史</a>
</li>
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'about' }}" href="{{ url_for('about') }}">关于</a>
</li>
{% elif current_user.role == '装维员' %}
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'dashboard' }}" href="{{ url_for('dashboard') }}">仪表盘</a>
</li>
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'receive' }}" href="{{ url_for('receive') }}">在途</a>
</li>
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'manual' }}" href="{{ url_for('manual') }}">申报</a>
</li>
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'statistics' }}" href="{{ url_for('statistics') }}">统计</a>
</li>
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'history' }}" href="{{ url_for('history') }}">历史</a>
</li>
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'about' }}" href="{{ url_for('about') }}">关于</a>
</li>
{% endif %}
{% endif %}
</ul>
<ul class="navbar-nav nav_menu">
{% if current_user.is_authenticated %}
<li class="nav-item">
<a class="nav_link" href="{{ url_for('logout') }}">退出</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
{% if not request.endpoint == 'login' %}
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="modal" id="flashModal" tabindex="-1" style="display: block; background: rgba(0,0,0,0.5);">
<div class="modal-dialog modal-dialog-centered" style="z-index: 1060;">
<div class="modal-content">
<div class="modal-body">
{% for message in messages %}
<p class="mb-0">{{ message }}</p>
{% endfor %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" onclick="closeFlashModal()">确定</button>
</div>
</div>
</div>
</div>
{% endif %}
{% endwith %}
{% endif %}
{% block content %}{% endblock %}
</div>
<script src="{{ url_for('static', filename='js/bootstrap.bundle.min.js') }}"></script>
<script>
function closeFlashModal() {
document.getElementById('flashModal').style.display = 'none';
}
</script>
</body>
</html>
+418
View File
@@ -0,0 +1,418 @@
{% extends "base.html" %}
{% block content %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/dashboard.css') }}">
<div class="dashboard_container">
<h1 class="dashboard_title">设备在线率仪表盘</h1>
<!-- 图表区域 -->
<div class="dashboard_section">
<h2 class="section_title">设备在线率趋势图</h2>
<div class="chart_container">
<div class="chart_wrapper">
<canvas id="overallChart"></canvas>
</div>
<div class="chart_wrapper">
<canvas id="branchChart"></canvas>
</div>
</div>
<div class="chart_container">
<div class="chart_wrapper">
<canvas id="deviceTypeChart"></canvas>
</div>
<div class="chart_wrapper">
<canvas id="townChart"></canvas>
</div>
</div>
</div>
<!-- 总体设备在线率 -->
<div class="dashboard_section">
<h2 class="section_title">各类型设备总体在线率</h2>
<div class="card_grid">
{% for device_type, stats in overall_stats.items() %}
<div class="dashboard_card">
<div class="card_header">
<h3 class="card_title">{{ device_type }}</h3>
</div>
<div class="card_content">
<div class="progress_container">
<div class="progress_bar" style="width: {{ stats.online_rate }}%">
<span class="progress_text">{{ stats.online_rate }}%</span>
</div>
</div>
<div class="stats_details">
<div class="stats_item">
<span class="stats_label">设备总数</span>
<span class="stats_value">{{ stats.total }}</span>
</div>
<div class="stats_item">
<span class="stats_label">派单数</span>
<span class="stats_value">{{ stats.work_orders }}</span>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<!-- 各支局设备在线率 -->
<div class="dashboard_section">
<h2 class="section_title">各支局设备在线率</h2>
<div class="table_container">
<table class="dashboard_table">
<thead>
<tr>
<th>支局</th>
<th>设备类型</th>
<th>设备总数</th>
<th>派单数</th>
<th>在线率</th>
<th>状态</th>
</tr>
</thead>
<tbody>
{% for branch, types in branch_stats.items() %}
{% for device_type, stats in types.items() %}
{% if stats.total > 0 %}
<tr>
{% if loop.first %}
<td rowspan="{{ types|length }}" class="branch_cell">{{ branch }}</td>
{% endif %}
<td>{{ device_type }}</td>
<td>{{ stats.total }}</td>
<td>{{ stats.work_orders }}</td>
<td>{{ stats.online_rate }}%</td>
<td>
{% if stats.online_rate >= 90 %}
<span class="status_badge good">良好</span>
{% elif stats.online_rate >= 75 %}
<span class="status_badge normal">一般</span>
{% else %}
<span class="status_badge bad">需关注</span>
{% endif %}
</td>
</tr>
{% endif %}
{% endfor %}
{% endfor %}
</tbody>
</table>
</div>
</div>
<!-- 各乡镇设备在线率 -->
<div class="dashboard_section">
<h2 class="section_title">各乡镇设备在线率</h2>
<div class="accordion_container">
{% for town, types in town_stats.items() %}
<div class="accordion_item">
<div class="accordion_header" onclick="toggleAccordion(this)">
<div class="accordion_title">{{ town }}</div>
<div class="accordion_summary">
<span>总设备数: {{ types['总计'].total }}</span>
<span>在线率: {{ types['总计'].online_rate }}%</span>
{% if types['总计'].online_rate >= 90 %}
<span class="status_badge good">良好</span>
{% elif types['总计'].online_rate >= 75 %}
<span class="status_badge normal">一般</span>
{% else %}
<span class="status_badge bad">需关注</span>
{% endif %}
</div>
<div class="accordion_icon"></div>
</div>
<div class="accordion_content">
<table class="detail_table">
<thead>
<tr>
<th>设备类型</th>
<th>设备总数</th>
<th>派单数</th>
<th>在线率</th>
<th>状态</th>
</tr>
</thead>
<tbody>
{% for device_type, stats in types.items() %}
{% if device_type != '总计' and stats.total > 0 %}
<tr>
<td>{{ device_type }}</td>
<td>{{ stats.total }}</td>
<td>{{ stats.work_orders }}</td>
<td>{{ stats.online_rate }}%</td>
<td>
{% if stats.online_rate >= 90 %}
<span class="status_badge good">良好</span>
{% elif stats.online_rate >= 75 %}
<span class="status_badge normal">一般</span>
{% else %}
<span class="status_badge bad">需关注</span>
{% endif %}
</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<!-- 引入Chart.js库 -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
function toggleAccordion(element) {
const content = element.nextElementSibling;
const icon = element.querySelector('.accordion_icon');
if (content.style.maxHeight) {
content.style.maxHeight = null;
icon.textContent = '▼';
} else {
content.style.maxHeight = content.scrollHeight + "px";
icon.textContent = '▲';
}
}
// 图表数据准备
document.addEventListener('DOMContentLoaded', function() {
// 设备类型在线率图表
const deviceTypeCtx = document.getElementById('deviceTypeChart').getContext('2d');
const deviceTypeLabels = [];
const deviceTypeData = [];
const deviceTypeColors = [];
{% for device_type, stats in overall_stats.items() %}
deviceTypeLabels.push('{{ device_type }}');
deviceTypeData.push({{ stats.online_rate }});
// 根据在线率设置颜色
if ({{ stats.online_rate }} >= 90) {
deviceTypeColors.push('rgba(40, 167, 69, 0.7)');
} else if ({{ stats.online_rate }} >= 75) {
deviceTypeColors.push('rgba(255, 193, 7, 0.7)');
} else {
deviceTypeColors.push('rgba(220, 53, 69, 0.7)');
}
{% endfor %}
new Chart(deviceTypeCtx, {
type: 'bar',
data: {
labels: deviceTypeLabels,
datasets: [{
label: '设备类型在线率(%)',
data: deviceTypeData,
backgroundColor: deviceTypeColors,
borderColor: deviceTypeColors.map(color => color.replace('0.7', '1')),
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '各设备类型在线率对比'
},
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
max: 100,
title: {
display: true,
text: '在线率(%)'
}
}
}
}
});
// 支局在线率对比图
const branchCtx = document.getElementById('branchChart').getContext('2d');
const branchLabels = [];
const branchData = [];
const branchColors = [];
{% for branch, types in branch_stats.items() %}
branchLabels.push('{{ branch }}');
branchData.push({{ types['总计'].online_rate }});
// 根据在线率设置颜色
if ({{ types['总计'].online_rate }} >= 90) {
branchColors.push('rgba(40, 167, 69, 0.7)');
} else if ({{ types['总计'].online_rate }} >= 75) {
branchColors.push('rgba(255, 193, 7, 0.7)');
} else {
branchColors.push('rgba(220, 53, 69, 0.7)');
}
{% endfor %}
new Chart(branchCtx, {
type: 'bar',
data: {
labels: branchLabels,
datasets: [{
label: '支局在线率(%)',
data: branchData,
backgroundColor: branchColors,
borderColor: branchColors.map(color => color.replace('0.7', '1')),
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '各支局在线率对比'
},
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
max: 100,
title: {
display: true,
text: '在线率(%)'
}
}
}
}
});
// 乡镇在线率对比图
const townCtx = document.getElementById('townChart').getContext('2d');
const townLabels = [];
const townData = [];
const townColors = [];
{% for town, types in town_stats.items() %}
townLabels.push('{{ town }}');
townData.push({{ types['总计'].online_rate }});
// 根据在线率设置颜色
if ({{ types['总计'].online_rate }} >= 90) {
townColors.push('rgba(40, 167, 69, 0.7)');
} else if ({{ types['总计'].online_rate }} >= 75) {
townColors.push('rgba(255, 193, 7, 0.7)');
} else {
townColors.push('rgba(220, 53, 69, 0.7)');
}
{% endfor %}
new Chart(townCtx, {
type: 'bar',
data: {
labels: townLabels,
datasets: [{
label: '乡镇在线率(%)',
data: townData,
backgroundColor: townColors,
borderColor: townColors.map(color => color.replace('0.7', '1')),
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '各乡镇在线率对比'
},
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
max: 100,
title: {
display: true,
text: '在线率(%)'
}
}
}
}
});
// 总体在线率趋势图(模拟数据)
const overallCtx = document.getElementById('overallChart').getContext('2d');
// 模拟过去7天的数据
const dates = [];
const today = new Date();
for (let i = 6; i >= 0; i--) {
const date = new Date();
date.setDate(today.getDate() - i);
dates.push(date.getMonth() + 1 + '/' + date.getDate());
}
// 生成模拟数据,假设在线率在当前值附近波动
const generateTrendData = (baseValue) => {
return Array.from({length: 7}, () => {
// 在基准值附近随机波动,但确保不超过100和不低于50
const fluctuation = Math.random() * 10 - 5; // -5 到 +5 之间的随机数
return Math.min(100, Math.max(50, baseValue + fluctuation));
});
};
// 假设当前总体在线率为所有设备类型在线率的平均值
let avgOnlineRate = 0;
let count = 0;
{% for device_type, stats in overall_stats.items() %}
avgOnlineRate += {{ stats.online_rate }};
count++;
{% endfor %}
avgOnlineRate = avgOnlineRate / (count || 1);
new Chart(overallCtx, {
type: 'line',
data: {
labels: dates,
datasets: [{
label: '总体在线率趋势(%)',
data: generateTrendData(avgOnlineRate),
fill: false,
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: '总体在线率7天趋势'
}
},
scales: {
y: {
beginAtZero: false,
min: Math.max(0, avgOnlineRate - 20),
max: Math.min(100, avgOnlineRate + 20),
title: {
display: true,
text: '在线率(%)'
}
}
}
}
});
});
</script>
{% endblock %}
+141
View File
@@ -0,0 +1,141 @@
{% extends "base.html" %}
{% block content %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/dispatch.css') }}">
<div class="dispatch_filter">
<form method="GET" action="{{ url_for('dispatch') }}" class="dispatch_search_form">
<input type="text" name="search" class="dispatch_search" placeholder="输入搜索词" value="{{ request.args.get('search', '') }}">
<select name="town" class="dispatch_search">
<option value="">所有乡镇</option>
{% for town in towns %}
<option value="{{ town }}" {% if town == request.args.get('town') %}selected{% endif %}>{{ town }}</option>
{% endfor %}
</select>
<select name="branch" class="dispatch_search">
<option value="">所有支局</option>
{% for branch in branches %}
<option value="{{ branch }}" {% if branch == request.args.get('branch') %}selected{% endif %}>{{ branch }}</option>
{% endfor %}
</select>
<button type="submit" class="dispatch_btn dispatch_btn_primary">搜索</button>
</form>
<div class="dispatch_action_bar">
<button type="button" class="dispatch_btn dispatch_btn_secondary_bj" onclick="handleSync('alarm')">同步报警盒</button>
<button type="button" class="dispatch_btn dispatch_btn_secondary_sxt" onclick="handleSync('camera')">同步摄像头</button>
<button type="button" class="dispatch_btn dispatch_btn_secondary_qx" id="selectAllBtn" onclick="handleSelectAll()">全选</button>
<button type="button" class="dispatch_btn dispatch_btn_secondary_fx" onclick="handleInvertSelect()">反选</button>
<button type="submit" form="dispatchForm" class="dispatch_btn dispatch_btn_primary">派单</button>
</div>
</div>
<!-- 派单表单 -->
<form id="dispatchForm" method="POST" action="{{ url_for('dispatch') }}">
<!-- 设备表格 -->
<table class="dispatch_table">
<thead>
<tr>
<th>选择</th>
<th>设备编号</th>
<th>所属乡镇</th>
<th>所属支局</th>
<th>单位名称</th>
<th>项目类型</th>
<th>设备类型</th>
<th>备注</th>
</tr>
</thead>
<tbody>
{% for device in devices.items %}
<tr>
<td><input type="checkbox" class="dispatch_checkbox" name="device_ids" value="{{ device.device_id }}"></td>
<td>{{ device.device_id }}</td>
<td>{{ device.town }}</td>
<td>{{ device.branch }}</td>
<td>{{ device.unit_name }}</td>
<td>{{ device.project_type }}</td>
<td>{{ device.device_type }}</td>
<td>
<span class="remark-display" onclick="editRemark('{{ device.device_id }}', this)">{{ device.remark or '点击添加备注' }}</span>
<input type="text" class="remark-input" style="display: none;" value="{{ device.remark or '' }}"
onblur="saveRemark('{{ device.device_id }}', this)"
onkeypress="if(event.key==='Enter') saveRemark('{{ device.device_id }}', this)">
</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>
</div>
<!-- 分页导航部分 -->
<div class="dispatch_pagination">
{% if devices.has_prev %}
<a href="{{ url_for('dispatch', page=devices.prev_num, search=request.args.get('search', ''), town=request.args.get('town', ''), branch=request.args.get('branch', ''), per_page=request.args.get('per_page', '50')) }}" class="dispatch_page_link">上一页</a>
{% endif %}
<span class="dispatch_page_info">第 {{ devices.page }} 页,共 {{ devices.pages }} 页</span>
<select class="per_page_select" onchange="changePerPage(this.value)">
<option value="10" {% if request.args.get('per_page', '50') == '10' %}selected{% endif %}>10条/页</option>
<option value="20" {% if request.args.get('per_page', '50') == '20' %}selected{% endif %}>20条/页</option>
<option value="50" {% if request.args.get('per_page', '50') == '50' %}selected{% endif %}>50条/页</option>
<option value="100" {% if request.args.get('per_page', '50') == '100' %}selected{% endif %}>100条/页</option>
</select>
{% if devices.has_next %}
<a href="{{ url_for('dispatch', page=devices.next_num, search=request.args.get('search', ''), town=request.args.get('town', ''), branch=request.args.get('branch', ''), per_page=request.args.get('per_page', '50')) }}" class="dispatch_page_link">下一页</a>
{% endif %}
</div>
</div>
<!-- 添加验证码模态框 -->
<div id="captchaModal" class="modal">
<div class="modal-content">
<h3>请输入验证码</h3>
<img id="captchaImage" src="{{ url_for('static', filename='captcha.png') }}" alt="验证码">
<input type="text" id="captchaInput" placeholder="请输入验证码">
<div class="modal-buttons">
<button onclick="submitCaptcha()">确定</button>
<button onclick="closeCaptchaModal()">取消</button>
</div>
</div>
</div>
<!-- 添加一键报警确认模态框 -->
<div id="alarmConfirmModal" class="modal">
<div class="modal-content">
<h3>确认同步</h3>
<p>确定要同步一键报警设备状态吗?</p>
<div class="modal-buttons">
<button onclick="confirmAlarmSync()" class="dispatch_btn dispatch_btn_primary">确定</button>
<button onclick="closeAlarmConfirmModal()" class="dispatch_btn">取消</button>
</div>
</div>
</div>
<!-- 添加确认模态框 -->
<div id="confirmModal" class="modal">
<div class="modal-content">
<h3>数据更新完成</h3>
<p>已更新 <span id="updatedCount">0</span> 个设备的状态</p>
<p>是否要进行派单操作?</p>
<div class="modal-buttons">
<button onclick="handleDispatch(true)"></button>
<button onclick="cancelDispatch()"></button>
</div>
</div>
</div>
<!-- 添加结果模态框 -->
<div id="resultModal" class="modal">
<div class="modal-content">
<h3>操作结果</h3>
<p id="resultMessage"></p>
<div class="modal-buttons">
<button onclick="window.location.reload()">确定</button>
</div>
</div>
</div>
<script src="{{ url_for('static', filename='js/dispatch.js') }}"></script>
{% endblock %}
+88
View File
@@ -0,0 +1,88 @@
{% extends "base.html" %}
{% block content %}
<div class="history_container">
<!-- 添加搜索框 -->
<div class="history_search">
<form method="GET" action="{{ url_for('history') }}" class="history_search_form">
<input type="text" name="search" value="{{ search }}" placeholder="搜索工单号、设备、支局、处理人等..." class="history_search_input">
<button type="submit" class="history_search_button">搜索</button>
</form>
</div>
<!-- PC端表格视图 -->
<table class="history_table">
<thead>
<tr>
<th>工单号</th>
<th>所属支局</th>
<th>故障设备</th>
<th>处理人</th>
<th>处理时间</th>
<th>故障类型</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for handling in handlings.items %}
<tr>
<td>{{ handling['order_id'] }}</td>
<td>{{ handling['branch'] }}</td>
<td>{{ handling['device_id'] }}</td>
<td>{{ handling['handler'] }}</td>
<td>{{ handling['time'].strftime('%Y-%m-%d %H:%M:%S') }}</td>
<td>{{ handling['fault_type'] }}</td>
<td>
<a href="{{ url_for('history_detail', order_id=handling['order_id']) }}" class="view_btn">查看</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- 移动端卡片视图 -->
<div class="card_container">
{% for handling in handlings.items %}
<a href="{{ url_for('history_detail', order_id=handling['order_id']) }}" class="history_card {% if handling['type'] == 'manual' %}manual-card{% else %}system-card{% endif %}">
<div class="history_card_header">
<span class="history_card_title">工单号: {{ handling['order_id'] }}</span>
<span class="history_card_time">{{ handling['time'].strftime('%Y-%m-%d') }}</span>
</div>
<div class="history_card_content">
<div class="history_card_item">
<span class="history_card_label">所属支局</span>
<span class="history_card_value">{{ handling['branch'] }}</span>
</div>
<div class="history_card_item">
<span class="history_card_label">故障设备</span>
<span class="history_card_value">{{ handling['device_id'] }}</span>
</div>
<div class="history_card_item">
<span class="history_card_label">处理人</span>
<span class="history_card_value">{{ handling['handler'] }}</span>
</div>
<div class="history_card_item">
<span class="history_card_label">故障类型</span>
<span class="history_card_value">{{ handling['fault_type'] }}</span>
</div>
<div class="history_card_item">
<span class="history_card_label">工单类型</span>
<span class="history_card_value">{{ '手工申报' if handling['type'] == 'manual' else '系统派单' }}</span>
</div>
</div>
</a>
{% endfor %}
</div>
<!-- 分页导航 -->
<div class="history_pagination">
{% if handlings.has_prev %}
<a href="{{ url_for('history', page=handlings.prev_num) }}" class="history_page_link">上一页</a>
{% endif %}
<span class="history_page_info">第 {{ handlings.page }} 页,共 {{ handlings.pages }} 页</span>
{% if handlings.has_next %}
<a href="{{ url_for('history', page=handlings.next_num) }}" class="history_page_link">下一页</a>
{% endif %}
</div>
</div>
{% endblock %}
+132
View File
@@ -0,0 +1,132 @@
{% extends "base.html" %}
{% block content %}
<div class="history_detail_container">
<!-- 工单信息卡片 -->
<div class="detail_card">
<div class="detail_header">
<h3 class="detail_title">工单详情 #{{ handling.order_id }}</h3>
<span class="detail_status">{{ '申报' if order_type == 'manual' else '系统' }}</span>
</div>
<div class="detail_section">
<h4 class="section_title">基本信息</h4>
<div class="detail_grid">
<div class="detail_item">
<span class="detail_label">所属支局</span>
<span class="detail_value">{{ handling.branch }}</span>
</div>
<div class="detail_item">
<span class="detail_label">故障设备</span>
<span class="detail_value">{{ handling.device_id }}</span>
</div>
<div class="detail_item">
<span class="detail_label">处理人</span>
<span class="detail_value highlight">{{ handling.handler }}</span>
</div>
{% if order_type == 'manual' %}
<div class="detail_item">
<span class="detail_label">项目类型</span>
<span class="detail_value">{{ handling.project_type }}</span>
</div>
<div class="detail_item">
<span class="detail_label">设备位置</span>
<span class="detail_value">{{ handling.device_location }}</span>
</div>
<div class="detail_item">
<span class="detail_label">故障时间</span>
<span class="detail_value">{{ handling.fault_time.strftime('%Y-%m-%d %H:%M:%S') }}</span>
</div>
{% else %}
<div class="detail_item">
<span class="detail_label">处理时间</span>
<span class="detail_value">{{ handling.restore_time.strftime('%Y-%m-%d %H:%M:%S') }}</span>
</div>
{% endif %}
</div>
</div>
<div class="detail_section">
<h4 class="section_title">故障信息</h4>
<div class="detail_grid">
<div class="detail_item">
<span class="detail_label">故障类型</span>
<span class="detail_value badge">{{ handling.fault_type }}</span>
</div>
<div class="detail_item">
<span class="detail_label">故障对接人</span>
<span class="detail_value">{{ handling.contact_person }}</span>
</div>
<div class="detail_item">
<span class="detail_label">联系电话</span>
<span class="detail_value">{{ handling.contact_phone }}</span>
</div>
<div class="detail_item">
<span class="detail_label">距离支局</span>
<span class="detail_value">{{ handling.distance_from_branch }} 公里</span>
</div>
</div>
</div>
<div class="detail_section">
<h4 class="section_title">处理详情</h4>
<div class="detail_description">
<p>{{ handling.handle_description }}</p>
</div>
{% if handling.remark %}
<div class="detail_remark">
<h5>备注信息</h5>
<p>{{ handling.remark }}</p>
</div>
{% endif %}
</div>
{% if handling.photo_paths %}
<div class="detail_section">
<h4 class="section_title">现场图片</h4>
<div class="photo_grid">
{% for photo in handling.photo_paths.split(',') %}
<div class="photo_item">
<img src="{{ url_for('static', filename='uploads/' + photo) }}" alt="现场图片"
onclick="showFullImage(this.src)">
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
<div class="detail_actions">
<a href="{{ url_for('history') }}" class="detail_btn detail_btn_secondary">
<i class="fas fa-arrow-left"></i> 返回列表
</a>
</div>
</div>
<!-- 图片预览模态框 -->
<div id="imageModal" class="modal">
<span class="modal_close">&times;</span>
<img class="modal_content" id="modalImage">
</div>
<script>
function showFullImage(src) {
var modal = document.getElementById("imageModal");
var modalImg = document.getElementById("modalImage");
modal.style.display = "block";
modalImg.src = src;
}
var modal = document.getElementById("imageModal");
var span = document.getElementsByClassName("modal_close")[0];
span.onclick = function() {
modal.style.display = "none";
}
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
</script>
{% endblock %}
+22
View File
@@ -0,0 +1,22 @@
{% extends "base.html" %}
{% block content %}
<div class="login_container">
<div class="login_box">
<h2 class="login_title">欢迎使用</h2>
<div class="login_subtitle">大化重点项目工单管理平台</div>
<form method="POST" class="login_form">
<div class="login_input_group">
<label for="phone" class="login_label">用户名</label>
<input type="text" id="phone" name="phone" class="login_input" required>
</div>
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="login_error">请先登录以访问此页面</div>
{% endif %}
{% endwith %}
<button type="submit" class="login_button">登 录</button>
</form>
</div>
</div>
{% endblock %}
+130
View File
@@ -0,0 +1,130 @@
{% extends "base.html" %}
{% block content %}
<div class="manual_container">
<div class="form_tips">
<p>注意事项</p>
<ul>
<li>本页面用于填写用户来电申报或者巡检途中发现的故障、移机,申报前请确保在途工单中没有重复故障</li>
<li>现场照片最多可上传5张,支持jpg、jpeg、png</li>
<li>距离支局公里数请填写支局办公室到故障地点实际距离</li>
<li><span class="required">*</span> 的字段为必填项</li>
</ul>
</div>
<form method="POST" enctype="multipart/form-data" class="manual_form">
<div class="form-group">
<label for="project_type">项目类型<span class="required">*</span></label>
<select id="project_type" name="project_type" required>
<option value="">请选择项目类型</option>
<option value='校园安防"4+N"项目'>校园安防"4+N"项目</option>
<option value="综治视联网">综治视联网</option>
<option value="教育城域网">教育城域网</option>
<option value="其他">其他</option>
</select>
</div>
<div class="form-group">
<label for="device_id">施工设备类型<span class="required">*</span></label>
<input type="text" id="device_id" name="device_id" required placeholder="如:摄像头、一键报警盒、光猫等">
</div>
<div class="form-group">
<label for="device_location">施工单位名称<span class="required">*</span></label>
<input type="text" id="device_location" name="device_location" required placeholder="请填写前往施工的单位名称如:古河村委、都阳镇中心小学等">
</div>
<div class="form-group">
<label for="fault_time">施工完成时间<span class="required">*</span></label>
<input type="datetime-local" id="fault_time" name="fault_time" required>
</div>
<div class="form-group">
<label for="handler">施工处理人<span class="required">*</span></label>
<input type="text" id="handler" name="handler" value="{{ current_user.name }}" required>
</div>
<div class="form-group">
<label for="fault_type">故障类型<span class="required">*</span></label>
<select id="fault_type" name="fault_type" required>
<option value="">请选择故障类型</option>
<option value="光猫故障">光猫故障</option>
<option value="设备故障">设备故障</option>
<option value="电源故障">电源故障</option>
<option value="电路故障">电路故障</option>
<option value="网线故障">网线故障</option>
<option value="皮线故障">皮线故障</option>
<option value="数据问题">数据问题</option>
<option value="用户欠费">用户欠费</option>
<option value="移机">移机</option>
<option value="新增">新增</option>
<option value="其他">其他</option>
</select>
</div>
<div class="form-group">
<label for="handle_description">处理描述<span class="required">*</span></label>
<textarea id="handle_description" name="handle_description" placeholder="请简述处理过程,如:光猫电源适配器坏,更好光猫电源适配器后恢复。" required></textarea>
</div>
<div class="form-group">
<label for="photo">现场照片(最多5张)</label>
<input type="file" id="photo" name="photo" multiple accept="image/*">
</div>
<div class="form-group">
<label for="contact_person">施工对接人<span class="required">*</span></label>
<input type="text" id="contact_person" name="contact_person" placeholder="请输入本次施工单位对接人" required>
</div>
<div class="form-group">
<label for="contact_phone">对接人联系电话</label>
<input type="tel" id="contact_phone" name="contact_phone" placeholder="请输入本次施工单位对接人联系电话">
</div>
<div class="form-group">
<label for="distance_from_branch">距离支局公里数<span class="required">*</span></label>
<input type="number" step="0.1" id="distance_from_branch" name="distance_from_branch" required placeholder="请输入实际路程距离,如:5.5、50">
</div>
<div class="form-group">
<label for="remark">备注</label>
<textarea id="remark" name="remark" placeholder="如有其他需要说明的信息,请在此填写"></textarea>
</div>
<button type="submit" class="manual_submit">提交</button>
</form>
</div>
<style>
.form_tips {
background-color: #f8f9fa;
padding: 15px;
border-radius: 4px;
margin-bottom: 20px;
}
.form_tips ul {
margin: 10px 0 0 20px;
padding: 0;
}
.form_tips li {
margin-bottom: 5px;
color: #666;
}
.required {
color: #dc3545;
margin-left: 3px;
}
.manual_form input::placeholder,
.manual_form textarea::placeholder {
color: #999;
font-size: 13px;
}
.manual_form select option:first-child {
color: #999;
}
</style>
{% endblock %}
+162
View File
@@ -0,0 +1,162 @@
{% extends "base.html" %}
{% block content %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/receive.css') }}">
<!-- 统计信息 -->
<div class="receive_stats">
<div class="stats_item">
<span class="stats_label">在途工单总数</span>
<span class="stats_value">{{ total_orders }}</span>
</div>
{% if current_user.branch == '交付中心' %}
<div class="stats_branches_row">
{% for branch, count in branch_stats.items() %}
<a href="{{ url_for('receive', branch=branch) }}" class="branch_stat {% if branch == selected_branch %}active{% endif %}">
<span class="branch_name">{{ branch }}</span>
<span class="branch_count">{{ count }}</span>
</a>
{% endfor %}
</div>
<div class="receive_actions">
<button onclick="recallAllOrders()" class="receive_btn receive_btn_warning">一键追回所有工单</button>
{% if selected_branch %}
<a href="{{ url_for('receive') }}" class="receive_btn receive_btn_secondary">清除筛选</a>
{% endif %}
</div>
{% endif %}
</div>
<!-- 筛选区域 -->
<div class="receive_filter">
<form method="GET" action="{{ url_for('receive') }}" class="filter_form">
<select name="device_type" class="filter_select">
<option value="">所有设备类型</option>
{% for device_type in device_types %}
<option value="{{ device_type }}" {% if device_type == request.args.get('device_type') %}selected{% endif %}>{{ device_type }}</option>
{% endfor %}
</select>
<select name="project_type" class="filter_select">
<option value="">所有项目类型</option>
{% for project_type in project_types %}
<option value="{{ project_type }}" {% if project_type == request.args.get('project_type') %}selected{% endif %}>{{ project_type }}</option>
{% endfor %}
</select>
<input type="hidden" name="branch" value="{{ selected_branch }}">
<input type="hidden" name="page" value="1">
<input type="hidden" name="per_page" value="{{ request.args.get('per_page', '50') }}">
<button type="submit" class="receive_btn receive_btn_primary">筛选</button>
{% if request.args.get('device_type') or request.args.get('project_type') %}
<a href="{{ url_for('receive', branch=selected_branch, per_page=request.args.get('per_page', '50')) }}" class="receive_btn receive_btn_secondary">清除筛选</a>
{% endif %}
</form>
</div>
<table class="receive_table">
<thead>
<tr>
<th>工单号</th>
<th>所属支局</th>
<th>故障设备</th>
<th>设备类型</th>
<th>项目类型</th>
<th>派单时间</th>
<th>派单时长</th>
</tr>
</thead>
<tbody>
{% for work_order in work_orders %}
<tr>
<td>{{ work_order.order_id }}</td>
<td>{{ work_order.device.branch }}</td>
<td><a href="{{ url_for('receive_detail', work_order_id=work_order.order_id) }}" class="receive_action">{{ work_order.device.device_id }}</a></td>
<td>{{ work_order.device.device_type }}</td>
<td>{{ work_order.device.project_type }}</td>
<td>{{ work_order.dispatch_time }}</td>
<td>{{ work_order.duration }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- 移动端卡片视图 -->
<div class="card_container">
{% for work_order in work_orders %}
<a href="{{ url_for('receive_detail', work_order_id=work_order.order_id) }}" class="receive_card">
<div class="receive_card_header">
<span class="receive_card_title">工单号: {{ work_order.order_id }}</span>
<span class="receive_card_duration">{{ work_order.duration }}</span>
</div>
<div class="receive_card_content">
<div class="receive_card_item">
<span class="receive_card_label">故障设备</span>
<span class="receive_card_value">{{ work_order.device.device_id }}</span>
</div>
<div class="receive_card_item">
<span class="receive_card_label">设备类型</span>
<span class="receive_card_value" data-device-type="{{ work_order.device.device_type }}">{{ work_order.device.device_type }}</span>
</div>
<div class="receive_card_item">
<span class="receive_card_label">所属支局</span>
<span class="receive_card_value">{{ work_order.device.branch }}</span>
</div>
<div class="receive_card_item">
<span class="receive_card_label">项目类型</span>
<span class="receive_card_value">{{ work_order.device.project_type }}</span>
</div>
<div class="receive_card_item">
<span class="receive_card_label">派单时间</span>
<span class="receive_card_value">{{ work_order.dispatch_time }}</span>
</div>
</div>
</a>
{% endfor %}
</div>
<!-- 添加分页 -->
<div class="receive_pagination">
{% if work_orders.has_prev %}
<a href="{{ url_for('receive', page=work_orders.prev_num, branch=selected_branch, device_type=request.args.get('device_type', ''), project_type=request.args.get('project_type', ''), per_page=request.args.get('per_page', '50')) }}" class="page_link">上一页</a>
{% endif %}
<span class="page_info">第 {{ work_orders.page }} 页,共 {{ work_orders.pages }} 页</span>
<select class="per_page_select" onchange="changePerPage(this.value)">
<option value="10" {% if request.args.get('per_page', '50') == '10' %}selected{% endif %}>10条/页</option>
<option value="20" {% if request.args.get('per_page', '50') == '20' %}selected{% endif %}>20条/页</option>
<option value="50" {% if request.args.get('per_page', '50') == '50' %}selected{% endif %}>50条/页</option>
<option value="100" {% if request.args.get('per_page', '50') == '100' %}selected{% endif %}>100条/页</option>
</select>
{% if work_orders.has_next %}
<a href="{{ url_for('receive', page=work_orders.next_num, branch=selected_branch, device_type=request.args.get('device_type', ''), project_type=request.args.get('project_type', ''), per_page=request.args.get('per_page', '50')) }}" class="page_link">下一页</a>
{% endif %}
</div>
<!-- 确认追回弹窗 -->
<div id="confirmModal" class="modal">
<div class="modal-content">
<h3>确认追回</h3>
<div class="modal-body">
<i class="modal-icon warning"></i>
<p>确定要追回所有在途工单吗?</p>
</div>
<div class="modal-buttons">
<button onclick="confirmRecall()" class="receive_btn receive_btn_warning">确定</button>
<button onclick="closeModal()" class="receive_btn receive_btn_secondary">取消</button>
</div>
</div>
</div>
<div id="resultModal" class="modal">
<div class="modal-content">
<h3>操作结果</h3>
<div class="modal-body">
<i class="modal-icon info"></i>
<p id="resultMessage"></p>
</div>
<div class="modal-buttons">
<button onclick="closeResultModal()" class="receive_btn receive_btn_primary">确定</button>
</div>
</div>
</div>
<script src="{{ url_for('static', filename='js/receive.js') }}"></script>
{% endblock %}
+141
View File
@@ -0,0 +1,141 @@
{% extends "base.html" %}
{% block content %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/receive_detail.css') }}">
<div class="receive_detail_container">
<!-- 工单信息卡片 -->
<div class="detail_card">
<h3 class="detail_title">工单信息</h3>
<div class="detail_grid">
<div class="detail_item">
<span class="detail_label">工单号:</span>
<span class="detail_value">{{ work_order.order_id }}</span>
</div>
<div class="detail_item">
<span class="detail_label">所属支局:</span>
<span class="detail_value">{{ work_order.device.branch }}</span>
</div>
<div class="detail_item">
<span class="detail_label">故障设备:</span>
<span class="detail_value">{{ work_order.device.device_id }}</span>
</div>
<div class="detail_item">
<span class="detail_label">设备类型:</span>
<span class="detail_value">{{ work_order.device.device_type }}</span>
</div>
<div class="detail_item">
<span class="detail_label">项目类型:</span>
<span class="detail_value">{{ work_order.device.project_type }}</span>
</div>
<div class="detail_item">
<span class="detail_label">派单时间:</span>
<span class="detail_value">{{ work_order.dispatch_time }}</span>
</div>
<div class="detail_item">
<span class="detail_label">关联账号:</span>
<span class="detail_value">{{ work_order.device.account }}</span>
</div>
</div>
</div>
<!-- 处理信息表单 -->
<div class="detail_card">
<h3 class="detail_title">处理信息</h3>
<form method="POST" enctype="multipart/form-data" class="detail_form">
<div class="form-group">
<label for="handler">故障处理人<span class="required">*</span></label>
<input type="text" id="handler" name="handler" value="{{ current_user.name }}" required>
</div>
<div class="form-group">
<label for="fault_type">故障类型<span class="required">*</span></label>
<select id="fault_type" name="fault_type" required>
<option value="">请选择故障类型</option>
<option value="光猫故障">光猫故障</option>
<option value="设备故障">设备故障</option>
<option value="电源故障">电源故障</option>
<option value="电路故障">电路故障</option>
<option value="网线故障">网线故障</option>
<option value="皮线故障">皮线故障</option>
<option value="数据问题">数据问题</option>
<option value="用户欠费">用户欠费</option>
<option value="其他">其他</option>
</select>
</div>
<div class="form-group">
<label for="handle_description">处理描述<span class="required">*</span></label>
<textarea id="handle_description" name="handle_description"
placeholder="请简述处理过程,如:光猫电源适配器坏,更换光猫电源适配器后恢复。" required></textarea>
</div>
<div class="form-group">
<label for="photo">现场图片(最多5张)</label>
<input type="file" id="photo" name="photo" multiple accept="image/*" class="detail_file_input">
</div>
<div class="form-group">
<label for="contact_person">故障对接人<span class="required">*</span></label>
<input type="text" id="contact_person" name="contact_person"
value="{{ work_order.contact_person }}"
placeholder="请输入本次故障单位对接人" required>
</div>
<div class="form-group">
<label for="contact_phone">联系电话</label>
<input type="tel" id="contact_phone" name="contact_phone"
value="{{ work_order.contact_phone }}"
placeholder="请输入本次故障单位对接人联系电话">
</div>
<div class="form-group">
<label for="distance_from_branch">距离支局公里数<span class="required">*</span></label>
<input type="number" id="distance_from_branch" name="distance_from_branch"
step="0.1" placeholder="请输入支局办公室到故障地点实际距离" required>
</div>
<div class="form-group">
<label for="remark">备注</label>
<textarea id="remark" name="remark"
placeholder="如有其他需要说明的信息请在此备注"></textarea>
</div>
<div class="form_tips">
<p>注意事项</p>
<ul>
<li>现场照片最多可上传5张,支持jpg、jpeg、png</li>
<li>距离支局公里数请填写支局办公室到故障地点实际距离</li>
<li><span class="required">*</span> 的字段为必填项</li>
</ul>
</div>
<div class="detail_actions">
<button type="submit" class="detail_btn detail_btn_primary">提交</button>
{% if current_user.role == '统计员' %}
<a href="{{ url_for('recall_order', order_id=work_order.order_id) }}"
class="detail_btn detail_btn_danger"
onclick="return confirm('确定要追回这张工单吗?')">追回</a>
{% endif %}
<a href="{{ url_for('receive') }}" class="detail_btn detail_btn_secondary">返回</a>
</div>
</form>
</div>
</div>
<script>
document.getElementById('fault_type').addEventListener('change', function() {
const faultType = this.value;
const descriptionField = document.getElementById('handle_description');
// 根据故障类型设置默认描述文本
const descriptions = {
'光猫故障': '检查发现光猫故障,更换光猫后恢复正常。',
'设备故障': '检查发现设备故障,重启设备后恢复正常。',
'电源故障': '检查发现电源适配器故障,更换电源适配器后恢复正常。',
'电路故障': '检查发现电路故障,修复电路后恢复正常。',
'网线故障': '检查发现网线故障,更换网线后恢复正常。',
'皮线故障': '检查发现皮线故障,更换皮线后恢复正常。',
'数据问题': '检查发现数据配置异常,修改配置后恢复正常。',
'用户欠费': '检查发现用户已欠费,通知用户缴费后恢复正常。',
'其他': ''
};
// 如果选择了故障类型,则填入对应的描述文本
if (faultType && descriptions[faultType]) {
descriptionField.value = descriptions[faultType];
} else {
descriptionField.value = '';
}
});
</script>
{% endblock %}
+255
View File
@@ -0,0 +1,255 @@
{% extends "base.html" %}
{% block content %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/statistics.css') }}">
<div class="statistics_container">
<!-- 本周统计 -->
<div class="statistics_section">
<h2 class="statistics_title">本周统计
<a href="{{ url_for('static', filename='file/关于政企交付运营中心2025年项目运维及交付实施激励方案.pdf') }}"
class="incentive_button" target="_blank">📄 关于政企交付运营中心2025年项目运维及交付实施激励方案</a>
</h2>
<!-- 普通工单统计 -->
<div class="statistics_card">
<h3>普通工单</h3>
{% if current_user.role == '统计员' %}
<div class="statistics_grid">
<div class="statistics_item">
<span class="statistics_label">派单总数</span>
<span class="statistics_value">{{ week_work_stats.total }}</span>
</div>
<div class="statistics_item">
<span class="statistics_label">已处理</span>
<span class="statistics_value">{{ week_work_stats.completed }}</span>
</div>
<div class="statistics_item">
<span class="statistics_label">未处理</span>
<span class="statistics_value">{{ week_work_stats.pending }}</span>
</div>
</div>
{% else %}
<div class="statistics_grid">
<div class="statistics_item">
<span class="statistics_label">已处理工单</span>
<span class="statistics_value">{{ week_work_stats.completed }}</span>
</div>
</div>
{% endif %}
</div>
<!-- 手工故障单统计 -->
<div class="statistics_card">
<h3>手工故障单</h3>
<div class="statistics_grid">
<div class="statistics_item">
<span class="statistics_label">处理完成数量</span>
<span class="statistics_value">{{ week_manual_stats }}</span>
</div>
</div>
</div>
<!-- 运维奖励统计 -->
<div class="statistics_card">
<h3>运维奖励(元)</h3>
<div class="statistics_grid">
{% if week_rewards is mapping %}
{% for project_type, reward in week_rewards.items() %}
<div class="statistics_item">
<span class="statistics_label">{{ project_type }}</span>
<span class="statistics_value">{{ "%.2f"|format(reward) }}</span>
</div>
{% endfor %}
{% endif %}
<div class="statistics_item total_reward">
<span class="statistics_label">总计</span>
<span class="statistics_value">{{ "%.2f"|format(week_rewards.values()|sum) if week_rewards is mapping else "%.2f"|format(week_rewards|default(0)) }}</span>
</div>
</div>
</div>
</div>
<!-- 本月统计 -->
<div class="statistics_section">
<h2 class="statistics_title">本月统计
{% if current_user.role == '统计员' %}
<button onclick="showMonthPicker()" class="export_button">📥︎ 导出往月明细</button>
{% endif %}
</h2>
<!-- 普通工单统计 -->
<div class="statistics_card">
<h3>普通工单</h3>
{% if current_user.role == '统计员' %}
<div class="statistics_grid">
<div class="statistics_item">
<span class="statistics_label">派单总数</span>
<span class="statistics_value">{{ month_work_stats.total }}</span>
</div>
<div class="statistics_item">
<span class="statistics_label">已处理</span>
<span class="statistics_value">{{ month_work_stats.completed }}</span>
</div>
<div class="statistics_item">
<span class="statistics_label">未处理</span>
<span class="statistics_value">{{ month_work_stats.pending }}</span>
</div>
</div>
{% else %}
<div class="statistics_grid">
<div class="statistics_item">
<span class="statistics_label">已处理工单</span>
<span class="statistics_value">{{ month_work_stats.completed }}</span>
</div>
</div>
{% endif %}
</div>
<!-- 手工故障单统计 -->
<div class="statistics_card">
<h3>手工故障单</h3>
<div class="statistics_grid">
<div class="statistics_item">
<span class="statistics_label">处理完成数量</span>
<span class="statistics_value">{{ month_manual_stats }}</span>
</div>
</div>
</div>
<!-- 运维奖励统计 -->
<div class="statistics_card">
<h3>运维奖励(元)</h3>
<div class="statistics_grid">
{% if month_rewards is mapping %}
{% for project_type, reward in month_rewards.items() %}
<div class="statistics_item">
<span class="statistics_label">{{ project_type }}</span>
<span class="statistics_value">{{ "%.2f"|format(reward) }}</span>
</div>
{% endfor %}
{% endif %}
<div class="statistics_item total_reward">
<span class="statistics_label">总计</span>
<span class="statistics_value">{{ "%.2f"|format(month_rewards.values()|sum) if month_rewards is mapping else "%.2f"|format(month_rewards|default(0)) }}</span>
</div>
</div>
</div>
{% if current_user.role == '统计员' %}
<div class="statistics_section">
<h2 class="statistics_title">本周奖励统计</h2>
<div class="statistics_card">
<div class="handler_stats_table">
<table>
<thead>
<tr>
<th>处理人</th>
<th>普通工单</th>
<th>手工工单</th>
<th>校园安防"4+N"项目</th>
<th>综治视联网</th>
<th>教育城域网</th>
<th>其他</th>
<th>预计奖励</th>
</tr>
</thead>
<tbody>
{% for handler, stats in week_handler_stats.items() %}
<tr>
<td>{{ handler }}</td>
<td>{{ stats.work_orders }}</td>
<td>{{ stats.manual_orders }}</td>
<td>{{ "%.2f"|format(stats.rewards['校园安防"4+N"项目']|default(0)) }}</td>
<td>{{ "%.2f"|format(stats.rewards['综治视联网']|default(0)) }}</td>
<td>{{ "%.2f"|format(stats.rewards['教育城域网']|default(0)) }}</td>
<td>{{ "%.2f"|format(stats.rewards['其他']|default(0)) }}</td>
<td>{{ "%.2f"|format(stats.rewards.values()|sum) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="statistics_section">
<h2 class="statistics_title">本月奖励统计</h2>
<div class="statistics_card">
<div class="handler_stats_table">
<table>
<thead>
<tr>
<th>处理人</th>
<th>普通工单</th>
<th>手工工单</th>
<th>校园安防"4+N"项目</th>
<th>综治视联网</th>
<th>教育城域网</th>
<th>其他</th>
<th>预计奖励</th>
</tr>
</thead>
<tbody>
{% for handler, stats in month_handler_stats.items() %}
<tr>
<td>{{ handler }}</td>
<td>{{ stats.work_orders }}</td>
<td>{{ stats.manual_orders }}</td>
<td>{{ "%.2f"|format(stats.rewards['校园安防"4+N"项目']|default(0)) }}</td>
<td>{{ "%.2f"|format(stats.rewards['综治视联网']|default(0)) }}</td>
<td>{{ "%.2f"|format(stats.rewards['教育城域网']|default(0)) }}</td>
<td>{{ "%.2f"|format(stats.rewards['其他']|default(0)) }}</td>
<td>{{ "%.2f"|format(stats.rewards.values()|sum) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endif %}
</div>
</div>
<!-- 月份选择器模态框 -->
<div id="monthPickerModal" class="modal" style="display: none;">
<div class="modal-content">
<h3>选择导出月份</h3>
<div class="month-picker-form">
<input type="month" id="monthPicker" class="month-input">
<div class="modal-buttons">
<button onclick="exportMonthlyStats()" class="export_button">导出</button>
<button onclick="closeModal()" class="cancel_button">取消</button>
</div>
</div>
</div>
</div>
<script>
function showMonthPicker() {
document.getElementById('monthPickerModal').style.display = 'flex';
}
function closeModal() {
document.getElementById('monthPickerModal').style.display = 'none';
}
function exportMonthlyStats() {
const monthPicker = document.getElementById('monthPicker');
const selectedMonth = monthPicker.value; // 格式:YYYY-MM
if (!selectedMonth) {
alert('请选择月份');
return;
}
// 将 YYYY-MM 格式拆分为年和月
const [year, month] = selectedMonth.split('-');
window.location.href = `/export_monthly_stats/${year}/${month}`;
closeModal();
}
// 点击模态框外部关闭
document.getElementById('monthPickerModal').addEventListener('click', function(e) {
if (e.target === this) {
closeModal();
}
});
</script>
{% endblock %}