民航专业工程标准工期计算器 - 开发实现
我将为您实现一个完整的Web应用,包含前端界面、后端计算逻辑和数据管理。考虑到这是一个复杂的系统,我将使用Python Flask作为后端,HTML/CSS/JavaScript作为前端,并使用SQLite数据库存储标准数据。
项目结构
民航工期计算器/
├── app.py # 主应用文件
├── database.py # 数据库操作
├── calculations.py # 计算逻辑
├── templates/ # HTML模板
│ ├── index.html # 主界面
│ ├── project.html # 项目选择
│ └── result.html # 结果展示
├── static/ # 静态资源
│ ├── css/
│ └── js/
└── data/ # 数据文件
└── standard_data.sql # 标准数据SQL
1. 数据库设计与数据导入 (database.py)
python
import sqlite3
import json
import os
from datetime import datetime
class DatabaseManager:
def __init__(self, db_path='data/standards.db'):
self.db_path = db_path
self.init_database()
def init_database(self):
"""初始化数据库,创建表结构"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
# 工程类别表
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category_name TEXT NOT NULL,
parent_id INTEGER,
level INTEGER NOT NULL,
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# 标准工期表
cursor.execute('''
CREATE TABLE IF NOT EXISTS standard_durations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category_id INTEGER NOT NULL,
subcategory_name TEXT,
table_number TEXT,
parameter_1_name TEXT,
parameter_1_unit TEXT,
parameter_2_name TEXT,
parameter_2_unit TEXT,
parameter_3_name TEXT,
parameter_3_unit TEXT,
duration_range TEXT,
calculation_formula TEXT,
remarks TEXT,
reference_standard TEXT,
FOREIGN KEY (category_id) REFERENCES project_categories(id)
)
''')
# 调整系数表
cursor.execute('''
CREATE TABLE IF NOT EXISTS adjustment_coefficients (
id INTEGER PRIMARY KEY AUTOINCREMENT,
adjustment_type TEXT NOT NULL,
condition TEXT,
coefficient REAL,
formula TEXT,
description TEXT
)
''')
# 计算记录表
cursor.execute('''
CREATE TABLE IF NOT EXISTS calculation_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project_name TEXT,
category_id INTEGER,
parameters TEXT,
standard_duration REAL,
adjustments TEXT,
final_duration REAL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
conn.commit()
conn.close()
def import_standard_data(self):
"""导入标准文档数据到数据库"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
# 插入工程类别
categories = [
('场道工程', None, 1, '对应标准第4章'),
('土石方工程', 1, 2, '4.2节'),
('土方开挖', 2, 3, '表4.2.1'),
('土方填筑', 2, 3, '表4.2.2'),
('石方开挖', 2, 3, '表4.2.3'),
('石方填筑', 2, 3, '表4.2.4'),
('地基处理工程', 1, 2, '4.3节'),
('换填地基', 7, 3, '表4.3.1'),
('强夯地基', 7, 3, '表4.3.2'),
('冲击碾压地基', 7, 3, '表4.3.3'),
('堆载预压', 7, 3, '表4.3.4'),
('真空预压', 7, 3, '表4.3.5'),
('溶洞处理', 7, 3, '表4.3.6'),
('复合地基-CFG桩', 7, 3, '表4.3.7-1'),
('复合地基-碎石桩', 7, 3, '表4.3.7-2'),
('复合地基-高压旋喷桩', 7, 3, '表4.3.7-3'),
('复合地基-搅拌桩', 7, 3, '表4.3.7-4'),
('复合地基-预制桩', 7, 3, '表4.3.7-5'),
('防护及支挡工程', 1, 2, '4.4节'),
('防护工程', 19, 3, '表4.4.1'),
('支挡工程', 19, 3, '表4.4.2'),
('道面工程', 1, 2, '4.5节'),
('沥青混凝土道面', 22, 3, '表4.5.1'),
('水泥混凝土道面', 22, 3, '表4.5.2'),
('排水工程', 1, 2, '4.6节'),
('钢筋混凝土盖板沟', 25, 3, '表4.6.1'),
('钢筋混凝土明沟', 25, 3, '表4.6.2'),
('砌筑明沟', 25, 3, '表4.6.3'),
('箱涵', 25, 3, '表4.6.4'),
('调节水池', 25, 3, '表4.6.5'),
('消防管网工程', 1, 2, '4.7节'),
('消防管网', 29, 3, '表4.7.1'),
('附属工程', 1, 2, '4.8节'),
('沥青混凝土路面', 32, 3, '表4.8.1'),
('水泥混凝土路面', 32, 3, '表4.8.2'),
('钢筋网围界', 32, 3, '表4.8.3'),
('砖砌围界', 32, 3, '表4.8.4'),
('防吹篱', 32, 3, '表4.8.5'),
('目视助航工程', None, 1, '对应标准第5章'),
('灯光站及设备安装', 36, 2, '5.2节'),
('灯光站主体施工-1层', 37, 3, '表5.2.1'),
('灯光站主体施工-2层', 37, 3, '表5.2.1'),
('高压柜安装', 37, 3, '表5.2.1'),
('变压器安装', 37, 3, '表5.2.1'),
('助航设施安装', 36, 2, '5.3节'),
('灯具钻孔取芯', 42, 3, '表5.3.1'),
('深桶灯具安装', 42, 3, '表5.3.1'),
('嵌入式灯具安装', 42, 3, '表5.3.1'),
('立式灯具安装', 42, 3, '表5.3.1'),
('滑行引导标记牌安装', 42, 3, '表5.3.1'),
('坡度灯(PAPI)安装', 42, 3, '表5.3.1'),
('风向标安装', 42, 3, '表5.3.1'),
('灯光电缆线路工程', 36, 2, '5.4节'),
('二次灯光电缆敷设-老道面切槽嵌线', 46, 3, '表5.4.1'),
('二次灯光电缆敷设-老道面切槽埋管', 46, 3, '表5.4.1'),
('二次灯光电缆敷设-新建道面水稳切槽', 46, 3, '表5.4.1'),
('一次灯光电缆敷设-直埋', 46, 3, '表5.4.1'),
('一次灯光电缆敷设-穿保护管', 46, 3, '表5.4.1'),
('飞行区电缆排管敷设', 46, 3, '表5.4.1'),
('飞行区电缆井', 46, 3, '表5.4.1'),
('隔离变压器箱安装', 36, 2, '5.5节'),
('隔离变压器箱-道面安装', 50, 3, '表5.5.1'),
('隔离变压器箱-土坪区安装', 50, 3, '表5.5.1'),
('隔离变压器箱-铁塔/支架安装', 50, 3, '表5.5.1'),
('隔离变压器等安装', 36, 2, '5.6节'),
('隔离变压器安装', 54, 3, '表5.6.1'),
('单灯控制器安装', 54, 3, '表5.6.1'),
('灯光铁塔安装', 36, 2, '5.7节'),
('桩基-人孔桩施工', 56, 3, '表5.7.1'),
('桩基-旋挖桩施工', 56, 3, '表5.7.1'),
('筏板、承台基础施工', 56, 3, '表5.7.1'),
('毛石混凝土挡墙施工', 56, 3, '表5.7.1'),
('浆砌石挡墙施工', 56, 3, '表5.7.1'),
('灯光铁塔组立', 56, 3, '表5.7.1'),
('灯光铁塔连廊安装', 56, 3, '表5.7.1'),
('易碎杆/灯塔安装', 56, 3, '表5.7.1'),
('机坪泛光照明及供电', 36, 2, '5.8节'),
('机坪电缆井', 60, 3, '表5.8.1'),
('机坪电缆排管敷设', 60, 3, '表5.8.1'),
('高杆灯安装', 60, 3, '表5.8.1'),
('配电亭安装', 60, 3, '表5.8.1'),
('机位牌安装-立式', 60, 3, '表5.8.1'),
('机位牌安装-三角/挂式', 60, 3, '表5.8.1'),
('泊位牌安装', 60, 3, '表5.8.1'),
('400Hz静变电源安装', 60, 3, '表5.8.1'),
('飞机地面空调安装', 60, 3, '表5.8.1'),
('充电桩安装', 60, 3, '表5.8.1'),
('箱变、双电源柜安装', 60, 3, '表5.8.1'),
('机坪线缆穿管敷设', 60, 3, '表5.8.1'),
('机坪线缆直埋敷设', 60, 3, '表5.8.1'),
('机坪照明监控系统', 60, 3, '表5.8.1'),
('目视停靠引导系统安装调试', 60, 3, '表5.8.1'),
('助航灯光系统调试', 36, 2, '5.9节'),
('调光系统调试', 70, 3, '表5.9.1'),
('站内电源系统调试', 70, 3, '表5.9.1'),
('助航灯光监控系统调试', 70, 3, '表5.9.1'),
('单灯控制系统调试', 70, 3, '表5.9.1'),
('标志标线工程', 36, 2, '5.10节'),
('跑滑区原道面标志清除', 72, 3, '表5.10.1'),
('跑滑区新建道面标志刻画', 72, 3, '表5.10.1'),
('站坪区原道面标志清除', 72, 3, '表5.10.1'),
('站坪区新建道面刻画', 72, 3, '表5.10.1'),
]
for cat in categories:
cursor.execute('''
INSERT INTO project_categories (category_name, parent_id, level, description)
VALUES (?, ?, ?, ?)
''', cat)
# 插入标准工期数据(简化示例,实际需根据完整文档)
duration_data = [
# 土方开挖
(3, '土方开挖', '表4.2.1', '平均挖厚', 'm', '工程量', '万m³', None, None,
'{"1-1": {"条件": {"平均挖厚": "<=5", "工程量": "<=1"}, "工期": 13}, '
'"1-2": {"条件": {"平均挖厚": "<=5", "工程量": "1-5"}, "工期": "13-28"}, '
'"1-3": {"条件": {"平均挖厚": "<=5", "工程量": "5-10"}, "工期": "28-48"}, '
'"1-4": {"条件": {"平均挖厚": "<=5", "工程量": "10-50"}, "工期": "48-66"}, '
'"1-5": {"条件": {"平均挖厚": "<=5", "工程量": "50-100"}, "工期": "66-128"}, '
'"1-6": {"条件": {"平均挖厚": "<=5", "工程量": "100-500"}, "工期": "128-271"}, '
'"1-7": {"条件": {"平均挖厚": "5-10", "工程量": "<=5"}, "工期": 31}, '
'"1-8": {"条件": {"平均挖厚": "5-10", "工程量": "5-10"}, "工期": "31-53"}, '
'"1-9": {"条件": {"平均挖厚": "5-10", "工程量": "10-50"}, "工期": "53-73"}, '
'"1-10": {"条件": {"平均挖厚": "5-10", "工程量": "50-100"}, "工期": "73-142"}, '
'"1-11": {"条件": {"平均挖厚": "5-10", "工程量": "100-500"}, "工期": "142-367"}, '
'"1-12": {"条件": {"平均挖厚": "10-20", "工程量": "<=5"}, "工期": 37}, '
'"1-13": {"条件": {"平均挖厚": "10-20", "工程量": "5-10"}, "工期": "37-56"}, '
'"1-14": {"条件": {"平均挖厚": "10-20", "工程量": "10-50"}, "工期": "56-92"}, '
'"1-15": {"条件": {"平均挖厚": "10-20", "工程量": "50-100"}, "工期": "92-188"}, '
'"1-16": {"条件": {"平均挖厚": "10-20", "工程量": "100-500"}, "工期": "188-385"}, '
'"1-17": {"条件": {"平均挖厚": "20-50", "工程量": "<=50"}, "工期": 128}, '
'"1-18": {"条件": {"平均挖厚": "20-50", "工程量": "50-100"}, "工期": "128-243"}, '
'"1-19": {"条件": {"平均挖厚": "20-50", "工程量": "100-500"}, "工期": "243-408"}}',
'注:1 平均挖厚超过50m,按50m工期进行计算。土方运输时间已包括在本工期内。2 土方开挖工期适用于Ⅰ~Ⅳ类土的情况,土质分类应符合本标准附录B的有关规定。',
'MH/T 5091---2025 第4.2.1条'),
# 灯光站主体施工-1层
(38, '灯光站主体施工-1层', '表5.2.1', '建筑面积', 'm²', None, None, None, None,
'{"2-1": {"条件": {"建筑面积": "<300"}, "工期": 88}, '
'"2-2": {"条件": {"建筑面积": "300-500"}, "工期": 95}, '
'"2-3": {"条件": {"建筑面积": "500-1000"}, "工期": 112}, '
'"2-4": {"条件": {"建筑面积": "1000-2000"}, "工期": 133}, '
'"2-5": {"条件": {"建筑面积": ">2000"}, "工期": 154}}',
'1层',
'MH/T 5091---2025 第5.2.1条'),
# 灯光站主体施工-2层
(39, '灯光站主体施工-2层', '表5.2.1', '单层建筑面积', 'm²', None, None, None, None,
'{"2-6": {"条件": {"单层建筑面积": "<500"}, "工期": 110}, '
'"2-7": {"条件": {"单层建筑面积": "500-1000"}, "工期": 133}, '
'"2-8": {"条件": {"单层建筑面积": "1000-2000"}, "工期": 161}, '
'"2-9": {"条件": {"单层建筑面积": ">2000"}, "工期": 189}}',
'2层',
'MH/T 5091---2025 第5.2.1条'),
]
for item in duration_data:
cursor.execute('''
INSERT INTO standard_durations
(category_id, subcategory_name, table_number, parameter_1_name, parameter_1_unit,
parameter_2_name, parameter_2_unit, parameter_3_name, parameter_3_unit,
duration_range, remarks, reference_standard)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', item)
# 插入调整系数
coefficients = [
('海拔调整', '2000m及以下', 1.0, '', '海拔2000m及以下,系数为1.0'),
('海拔调整', '2000-2500m', 1.05, '', '海拔2000-2500m,系数为1.05'),
('海拔调整', '2500-3000m', 1.1, '', '海拔2500-3000m,系数为1.1'),
('海拔调整', '3000-3500m', 1.2, '', '海拔3000-3500m,系数为1.2'),
('海拔调整', '3500-4000m', 1.3, '', '海拔3500-4000m,系数为1.3'),
('海拔调整', '4000-4500m', 1.4, '', '海拔4000-4500m,系数为1.4'),
('不停航施工', 'T≤9.5', '8/(T-1.5)', 'T为停航时长(h)', '不停航施工工期 = 标准工期 × 8/(T-1.5)'),
('冬休期调整', 'I类地区', 0, '', 'I类地区冬休期建议0个月'),
('冬休期调整', 'II类地区', 90, '', 'II类地区冬休期建议3个月(90天)'),
('冬休期调整', 'III类地区', 120, '', 'III类地区冬休期建议4个月(120天)'),
('灯光站改造', '改造项目', 1.1, '', '灯光站改造项目,工期乘以系数1.1'),
]
for coeff in coefficients:
cursor.execute('''
INSERT INTO adjustment_coefficients
(adjustment_type, condition, coefficient, formula, description)
VALUES (?, ?, ?, ?, ?)
''', coeff)
conn.commit()
conn.close()
print("标准数据导入完成!")
def get_project_categories(self, level=1, parent_id=None):
"""获取工程类别"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
if parent_id is None:
cursor.execute('SELECT * FROM project_categories WHERE level = ? ORDER BY id', (level,))
else:
cursor.execute('SELECT * FROM project_categories WHERE parent_id = ? ORDER BY id', (parent_id,))
categories = cursor.fetchall()
conn.close()
result = []
for cat in categories:
result.append({
'id': cat[0],
'name': cat[1],
'parent_id': cat[2],
'level': cat[3],
'description': cat[4]
})
return result
def get_subcategories(self, category_id):
"""获取子类别"""
return self.get_project_categories(level=3, parent_id=category_id)
def get_standard_duration_data(self, category_id, subcategory_name):
"""获取标准工期数据"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
SELECT * FROM standard_durations
WHERE category_id = ? AND subcategory_name = ?
''', (category_id, subcategory_name))
data = cursor.fetchone()
conn.close()
if data:
return {
'id': data[0],
'category_id': data[1],
'subcategory_name': data[2],
'table_number': data[3],
'parameter_1_name': data[4],
'parameter_1_unit': data[5],
'parameter_2_name': data[6],
'parameter_2_unit': data[7],
'parameter_3_name': data[8],
'parameter_3_unit': data[9],
'duration_range': data[10],
'calculation_formula': data[11],
'remarks': data[12],
'reference_standard': data[13]
}
return None
def get_adjustment_coefficients(self, adjustment_type=None):
"""获取调整系数"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
if adjustment_type:
cursor.execute('SELECT * FROM adjustment_coefficients WHERE adjustment_type = ?', (adjustment_type,))
else:
cursor.execute('SELECT * FROM adjustment_coefficients')
coefficients = cursor.fetchall()
conn.close()
result = []
for coeff in coefficients:
result.append({
'id': coeff[0],
'adjustment_type': coeff[1],
'condition': coeff[2],
'coefficient': coeff[3],
'formula': coeff[4],
'description': coeff[5]
})
return result
def save_calculation(self, project_name, category_id, parameters, standard_duration, adjustments, final_duration):
"""保存计算记录"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
parameters_json = json.dumps(parameters)
adjustments_json = json.dumps(adjustments)
cursor.execute('''
INSERT INTO calculation_history
(project_name, category_id, parameters, standard_duration, adjustments, final_duration)
VALUES (?, ?, ?, ?, ?, ?)
''', (project_name, category_id, parameters_json, standard_duration, adjustments_json, final_duration))
conn.commit()
conn.close()
return cursor.lastrowid
def get_calculation_history(self, limit=10):
"""获取计算历史"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
SELECT * FROM calculation_history
ORDER BY created_at DESC
LIMIT ?
''', (limit,))
history = cursor.fetchall()
conn.close()
result = []
for record in history:
result.append({
'id': record[0],
'project_name': record[1],
'category_id': record[2],
'parameters': json.loads(record[3]),
'standard_duration': record[4],
'adjustments': json.loads(record[5]),
'final_duration': record[6],
'created_at': record[7]
})
return result
2. 计算逻辑 (calculations.py)
python
import json
import re
class DurationCalculator:
def __init__(self, db_manager):
self.db = db_manager
def parse_duration_range(self, duration_range):
"""解析工期范围字符串"""
if '-' in duration_range:
parts = duration_range.split('-')
return float(parts[0]), float(parts[1])
else:
return float(duration_range), float(duration_range)
def interpolate_duration(self, duration_range, param1_value, param1_min, param1_max):
"""线性插值计算工期"""
if '-' in duration_range:
parts = duration_range.split('-')
min_duration = float(parts[0])
max_duration = float(parts[1])
# 线性插值
if param1_value <= param1_min:
return min_duration
elif param1_value >= param1_max:
return max_duration
else:
ratio = (param1_value - param1_min) / (param1_max - param1_min)
return min_duration + ratio * (max_duration - min_duration)
else:
return float(duration_range)
def calculate_standard_duration(self, category_id, subcategory_name, parameters):
"""计算标准工期"""
# 获取标准数据
data = self.db.get_standard_duration_data(category_id, subcategory_name)
if not data:
return None, "未找到对应的标准工期数据"
# 解析参数
param1_name = data['parameter_1_name']
param1_unit = data['parameter_1_unit']
param2_name = data['parameter_2_name']
param2_unit = data['parameter_2_unit']
# 获取参数值
param1_value = parameters.get(param1_name)
param2_value = parameters.get(param2_name)
if param1_value is None:
return None, f"缺少参数: {param1_name}"
# 解析duration_range
duration_data = json.loads(data['duration_range'])
# 根据参数匹配条件
matched_key = None
matched_duration = None
for key, value in duration_data.items():
conditions = value['conditions']
# 检查条件匹配
param1_condition = conditions.get(param1_name)
param2_condition = conditions.get(param2_name)
# 检查参数1是否满足条件
param1_match = self.check_condition(param1_value, param1_condition)
# 检查参数2是否满足条件(如果存在)
param2_match = True
if param2_condition and param2_value is not None:
param2_match = self.check_condition(param2_value, param2_condition)
if param1_match and param2_match:
matched_key = key
matched_duration = value['duration']
break
if matched_key is None:
return None, "参数值超出标准范围,请检查输入"
# 计算工期
if isinstance(matched_duration, str) and '-' in matched_duration:
# 需要插值计算
# 这里简化处理,实际应根据参数范围进行插值
parts = matched_duration.split('-')
standard_duration = (float(parts[0]) + float(parts[1])) / 2
else:
standard_duration = float(matched_duration)
# 应用特殊规则(如超过范围增加工期)
standard_duration = self.apply_special_rules(
standard_duration,
parameters,
data['remarks']
)
return {
'standard_duration': round(standard_duration, 1),
'table_number': data['table_number'],
'matched_key': matched_key,
'reference_standard': data['reference_standard'],
'remarks': data['remarks']
}, None
def check_condition(self, value, condition):
"""检查参数值是否满足条件"""
if condition is None:
return True
# 处理范围条件
if '-' in condition:
parts = condition.split('-')
min_val = float(parts[0])
max_val = float(parts[1])
return min_val <= float(value) <= max_val
# 处理比较条件
if condition.startswith('<='):
threshold = float(condition[2:])
return float(value) <= threshold
elif condition.startswith('<'):
threshold = float(condition[1:])
return float(value) < threshold
elif condition.startswith('>='):
threshold = float(condition[2:])
return float(value) >= threshold
elif condition.startswith('>'):
threshold = float(condition[1:])
return float(value) > threshold
elif condition == '无' or condition == '':
return True
else:
# 精确匹配
return str(value) == condition
def apply_special_rules(self, standard_duration, parameters, remarks):
"""应用特殊规则(如超过范围增加工期)"""
# 这里实现具体规则
# 例如:如果工程量超过500万m³,每增加15万m³,工期加1d
# 解析备注中的规则
if remarks and '超过' in remarks:
# 简单示例:提取增加规则
pattern = r'每增加(\d+\.?\d*)万?([m³²]?),工期加(\d+)d'
match = re.search(pattern, remarks)
if match:
increase_unit = float(match.group(1))
increase_days = int(match.group(3))
# 检查是否超过阈值
if '工程量' in parameters:
v = float(parameters['工程量'])
if v > 500: # 假设阈值是500
extra = (v - 500) / increase_unit
standard_duration += extra * increase_days
return standard_duration
def apply_adjustments(self, standard_duration, adjustments):
"""应用调整系数"""
final_duration = standard_duration
adjustment_details = []
for adj_type, value in adjustments.items():
if adj_type == '海拔调整':
# 查找海拔系数
coefficients = self.db.get_adjustment_coefficients('海拔调整')
for coeff in coefficients:
if self.check_condition(value, coeff['condition']):
coefficient = coeff['coefficient']
final_duration *= coefficient
adjustment_details.append({
'type': adj_type,
'condition': f"海拔{value}m",
'coefficient': coefficient,
'description': coeff['description']
})
break
elif adj_type == '不停航施工':
try:
T = float(value)
if T <= 9.5:
coefficient = 8 / (T - 1.5)
final_duration *= coefficient
adjustment_details.append({
'type': adj_type,
'condition': f"停航时长{T}h",
'coefficient': round(coefficient, 2),
'description': f"不停航施工工期 = 标准工期 × 8/({T}-1.5)"
})
except:
pass
elif adj_type == '冬休期调整':
try:
days = int(value)
if days > 0:
final_duration += days
adjustment_details.append({
'type': adj_type,
'condition': f"冬休期{days}天",
'coefficient': 1,
'description': f"增加工期{days}天"
})
except:
pass
elif adj_type == '灯光站改造':
coefficient = float(value)
final_duration *= coefficient
adjustment_details.append({
'type': adj_type,
'condition': '改造项目',
'coefficient': coefficient,
'description': '灯光站改造项目,工期乘以系数'
})
return {
'final_duration': round(final_duration, 1),
'adjustment_details': adjustment_details
}
def calculate_project_duration(self, project_name, category_id, subcategory_name, parameters, adjustments):
"""计算项目工期(完整流程)"""
# 1. 计算标准工期
standard_result, error = self.calculate_standard_duration(category_id, subcategory_name, parameters)
if error:
return None, error
standard_duration = standard_result['standard_duration']
# 2. 应用调整系数
adjustment_result = self.apply_adjustments(standard_duration, adjustments)
final_duration = adjustment_result['final_duration']
# 3. 保存记录
record_id = self.db.save_calculation(
project_name,
category_id,
parameters,
standard_duration,
adjustment_result['adjustment_details'],
final_duration
)
# 4. 返回结果
result = {
'project_name': project_name,
'category_id': category_id,
'subcategory_name': subcategory_name,
'parameters': parameters,
'standard_duration': standard_duration,
'adjustments': adjustment_result['adjustment_details'],
'final_duration': final_duration,
'reference_standard': standard_result['reference_standard'],
'table_number': standard_result['table_number'],
'record_id': record_id
}
return result, None
3. Web应用主程序 (app.py)
python
from flask import Flask, render_template, request, jsonify, send_from_directory
from database import DatabaseManager
from calculations import DurationCalculator
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = '民航工期计算器'
# 初始化数据库和计算器
db = DatabaseManager()
calc = DurationCalculator(db)
# 如果数据库为空,导入数据
try:
categories = db.get_project_categories()
if not categories:
print("数据库为空,正在导入标准数据...")
db.import_standard_data()
print("数据导入完成!")
except:
print("数据库初始化错误")
@app.route('/')
def index():
"""主界面"""
return render_template('index.html')
@app.route('/api/categories')
def get_categories():
"""获取一级工程类别"""
categories = db.get_project_categories(level=1)
return jsonify(categories)
@app.route('/api/subcategories/<int:category_id>')
def get_subcategories(category_id):
"""获取子类别"""
subcategories = db.get_subcategories(category_id)
return jsonify(subcategories)
@app.route('/api/standard-data/<int:category_id>/<subcategory_name>')
def get_standard_data(category_id, subcategory_name):
"""获取标准数据"""
data = db.get_standard_duration_data(category_id, subcategory_name)
if data:
return jsonify(data)
return jsonify({'error': '未找到数据'}), 404
@app.route('/api/calculate', methods=['POST'])
def calculate():
"""计算工期"""
data = request.json
project_name = data.get('project_name', '未命名项目')
category_id = data.get('category_id')
subcategory_name = data.get('subcategory_name')
parameters = data.get('parameters', {})
adjustments = data.get('adjustments', {})
if not category_id or not subcategory_name:
return jsonify({'error': '缺少必要参数'}), 400
# 执行计算
result, error = calc.calculate_project_duration(
project_name, category_id, subcategory_name, parameters, adjustments
)
if error:
return jsonify({'error': error}), 400
return jsonify(result)
@app.route('/api/history')
def get_history():
"""获取计算历史"""
history = db.get_calculation_history(limit=10)
return jsonify(history)
@app.route('/api/adjustments')
def get_adjustments():
"""获取调整系数"""
adjustments = db.get_adjustment_coefficients()
return jsonify(adjustments)
@app.route('/download-report/<int:record_id>')
def download_report(record_id):
"""下载计算报告(简化版)"""
# 这里可以生成PDF或HTML报告
# 为了简化,返回一个文本报告
history = db.get_calculation_history()
record = next((h for h in history if h['id'] == record_id), None)
if not record:
return "记录不存在", 404
report = f"""
民航专业工程标准工期计算报告
================================
项目名称: {record['project_name']}
计算时间: {record['created_at']}
【输入参数】
{json.dumps(record['parameters'], indent=2, ensure_ascii=False)}
【标准工期】
标准工期: {record['standard_duration']} 日历天
【调整系数】
{json.dumps(record['adjustments'], indent=2, ensure_ascii=False)}
【最终工期】
最终工期: {record['final_duration']} 日历天
【备注】
本报告基于《民航专业工程施工工期标准》(MH/T 5091---2025)生成。
计算结果仅供参考,实际工期需结合项目具体情况确定。
"""
return report, 200, {'Content-Type': 'text/plain; charset=utf-8'}
@app.route('/static/<path:path>')
def serve_static(path):
"""提供静态文件"""
return send_from_directory('static', path)
if __name__ == '__main__':
# 确保静态文件夹存在
os.makedirs('static/css', exist_ok=True)
os.makedirs('static/js', exist_ok=True)
app.run(debug=True, port=5000)
4. HTML前端界面
4.1 主界面 (templates/index.html)
html
<!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="stylesheet" href="/static/css/style.css">
</head>
<body>
<div class="container">
<header>
<h1>民航专业工程标准工期计算器</h1>
<p class="subtitle">基于《民航专业工程施工工期标准》MH/T 5091---2025</p>
</header>
<div class="main-content">
<div class="left-panel">
<div class="section">
<h2>项目信息</h2>
<div class="form-group">
<label for="projectName">项目名称</label>
<input type="text" id="projectName" placeholder="请输入项目名称" value="示例项目">
</div>
</div>
<div class="section">
<h2>选择工程类别</h2>
<div class="form-group">
<label for="categorySelect">一级类别</label>
<select id="categorySelect" onchange="loadSubcategories()">
<option value="">请选择...</option>
</select>
</div>
<div class="form-group">
<label for="subcategorySelect">二级类别</label>
<select id="subcategorySelect" onchange="loadStandardData()">
<option value="">请先选择一级类别...</option>
</select>
</div>
</div>
<div class="section">
<h2>输入参数</h2>
<div id="parameterInputs">
<p class="info">请先选择工程类别</p>
</div>
</div>
<div class="section">
<h2>调整系数</h2>
<div id="adjustmentInputs">
<!-- 动态生成调整系数输入 -->
</div>
</div>
<div class="button-group">
<button class="btn btn-primary" onclick="calculate()">计算工期</button>
<button class="btn btn-secondary" onclick="resetForm()">重置</button>
</div>
</div>
<div class="right-panel">
<div class="section">
<h2>计算结果</h2>
<div id="resultArea">
<div class="result-placeholder">
<p>请输入参数并点击"计算工期"查看结果</p>
</div>
</div>
</div>
<div class="section">
<h2>计算历史</h2>
<div id="historyArea">
<div class="history-placeholder">
<p>暂无计算记录</p>
</div>
</div>
</div>
</div>
</div>
<footer>
<p>© 2025 民航专业工程标准工期计算器 | 基于 MH/T 5091---2025 标准</p>
</footer>
</div>
<script src="/static/js/app.js"></script>
</body>
</html>
4.2 静态CSS (static/css/style.css)
css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background-color: #f5f7fa;
color: #333;
line-height: 1.6;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
header {
text-align: center;
margin-bottom: 30px;
padding: 20px;
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
color: white;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
header h1 {
font-size: 28px;
margin-bottom: 10px;
}
.subtitle {
font-size: 14px;
opacity: 0.9;
}
.main-content {
display: flex;
gap: 20px;
margin-bottom: 30px;
}
.left-panel {
flex: 1;
min-width: 400px;
}
.right-panel {
flex: 1;
min-width: 400px;
}
.section {
background: white;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
border: 1px solid #e1e4e8;
}
.section h2 {
font-size: 18px;
margin-bottom: 15px;
color: #1e3c72;
border-bottom: 2px solid #e1e4e8;
padding-bottom: 8px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 600;
color: #555;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 10px;
border: 1px solid #d1d5db;
border-radius: 4px;
font-size: 14px;
transition: border-color 0.3s;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: #2a5298;
box-shadow: 0 0 0 3px rgba(42, 82, 152, 0.1);
}
.parameter-row {
display: flex;
gap: 10px;
align-items: center;
margin-bottom: 10px;
}
.parameter-row input {
flex: 1;
}
.parameter-row span {
color: #666;
font-size: 12px;
}
.button-group {
display: flex;
gap: 10px;
margin-top: 20px;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: all 0.3s;
}
.btn-primary {
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
color: white;
}
.btn-primary:hover {
background: linear-gradient(135deg, #2a5298 0%, #1e3c72 100%);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.btn-secondary {
background: #6c757d;
color: white;
}
.btn-secondary:hover {
background: #5a6268;
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.info {
color: #666;
font-size: 14px;
font-style: italic;
}
.result-placeholder,
.history-placeholder {
text-align: center;
color: #999;
padding: 30px;
font-size: 14px;
}
/* 结果展示样式 */
.result-card {
background: #f8f9fa;
border-radius: 6px;
padding: 15px;
margin-bottom: 15px;
border-left: 4px solid #1e3c72;
}
.result-card h3 {
font-size: 16px;
margin-bottom: 10px;
color: #1e3c72;
}
.result-value {
font-size: 24px;
font-weight: bold;
color: #2a5298;
margin: 10px 0;
}
.result-details {
font-size: 13px;
color: #555;
line-height: 1.5;
}
.adjustment-item {
background: white;
padding: 8px 12px;
border-radius: 4px;
margin: 5px 0;
border-left: 3px solid #28a745;
font-size: 13px;
}
.adjustment-item .type {
font-weight: 600;
color: #28a745;
}
.reference {
background: #e7f3ff;
padding: 10px;
border-radius: 4px;
font-size: 12px;
color: #1e3c72;
margin-top: 10px;
}
/* 历史记录样式 */
.history-item {
background: white;
border: 1px solid #e1e4e8;
border-radius: 6px;
padding: 12px;
margin-bottom: 10px;
cursor: pointer;
transition: all 0.2s;
}
.history-item:hover {
border-color: #2a5298;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.history-item .name {
font-weight: 600;
color: #1e3c72;
margin-bottom: 5px;
}
.history-item .time {
font-size: 12px;
color: #888;
}
.history-item .duration {
font-weight: bold;
color: #28a745;
font-size: 14px;
margin-top: 5px;
}
/* 响应式设计 */
@media (max-width: 900px) {
.main-content {
flex-direction: column;
}
.left-panel,
.right-panel {
min-width: 100%;
}
header h1 {
font-size: 22px;
}
}
/* 加载动画 */
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid #f3f3f3;
border-top: 3px solid #1e3c72;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-right: 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 错误提示 */
.error-message {
background: #fff3cd;
border: 1px solid #ffeaa7;
color: #856404;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
font-size: 13px;
}
.success-message {
background: #d4edda;
border: 1px solid #c3e6cb;
color: #155724;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
font-size: 13px;
}
4.3 JavaScript逻辑 (static/js/app.js)
javascript
// 全局变量
let currentCategory = null;
let currentSubcategory = null;
let standardData = null;
let adjustmentData = [];
// 页面加载时初始化
document.addEventListener('DOMContentLoaded', function() {
loadCategories();
loadAdjustments();
loadHistory();
});
// 加载一级工程类别
async function loadCategories() {
try {
const response = await fetch('/api/categories');
const categories = await response.json();
const select = document.getElementById('categorySelect');
select.innerHTML = '<option value="">请选择...</option>';
categories.forEach(cat => {
const option = document.createElement('option');
option.value = cat.id;
option.textContent = cat.name;
if (cat.description) {
option.textContent += ` (${cat.description})`;
}
select.appendChild(option);
});
} catch (error) {
console.error('加载类别失败:', error);
showErrorMessage('加载工程类别失败,请检查网络连接');
}
}
// 加载调整系数
async function loadAdjustments() {
try {
const response = await fetch('/api/adjustments');
adjustmentData = await response.json();
const container = document.getElementById('adjustmentInputs');
container.innerHTML = '';
// 按类型分组
const grouped = {};
adjustmentData.forEach(adj => {
if (!grouped[adj.adjustment_type]) {
grouped[adj.adjustment_type] = [];
}
grouped[adj.adjustment_type].push(adj);
});
// 生成输入控件
for (const [type, items] of Object.entries(grouped)) {
const div = document.createElement('div');
div.className = 'form-group';
let inputHTML = '';
if (type === '海拔调整') {
inputHTML = `
<label for="altitude">海拔高度 (m)</label>
<input type="number" id="altitude" placeholder="输入海拔高度" value="1500">
<small class="info">海拔超过2000m时,工期将按比例调整</small>
`;
} else if (type === '不停航施工') {
inputHTML = `
<label for="stopTime">机场停航时长 (小时)</label>
<input type="number" id="stopTime" placeholder="输入停航时长" step="0.1" value="8">
<small class="info">适用于T≤9.5小时的情况</small>
`;
} else if (type === '冬休期调整') {
inputHTML = `
<label for="winterRegion">施工地区分类</label>
<select id="winterRegion">
<option value="0">I类地区 (T≤90天)</option>
<option value="90">II类地区 (90<T≤150天)</option>
<option value="120">III类地区 (T>150天)</option>
</select>
<small class="info">根据附录A确定地区分类</small>
`;
} else if (type === '灯光站改造') {
inputHTML = `
<label for="isRenovation">是否为改造项目</label>
<select id="isRenovation">
<option value="1">否</option>
<option value="1.1">是</option>
</select>
<small class="info">灯光站改造项目工期乘以1.1系数</small>
`;
}
if (inputHTML) {
div.innerHTML = `<h3 style="font-size: 14px; margin-bottom: 10px;">${type}</h3>` + inputHTML;
container.appendChild(div);
}
}
} catch (error) {
console.error('加载调整系数失败:', error);
}
}
// 加载二级类别
async function loadSubcategories() {
const categoryId = document.getElementById('categorySelect').value;
if (!categoryId) {
return;
}
currentCategory = categoryId;
try {
const response = await fetch(`/api/subcategories/${categoryId}`);
const subcategories = await response.json();
const select = document.getElementById('subcategorySelect');
select.innerHTML = '<option value="">请选择...</option>';
subcategories.forEach(sub => {
const option = document.createElement('option');
option.value = sub.name;
option.textContent = sub.name;
select.appendChild(option);
});
// 清空参数输入区域
document.getElementById('parameterInputs').innerHTML = '<p class="info">请选择二级类别</p>';
} catch (error) {
console.error('加载子类别失败:', error);
showErrorMessage('加载子类别失败');
}
}
// 加载标准数据
async function loadStandardData() {
const subcategoryName = document.getElementById('subcategorySelect').value;
if (!subcategoryName) {
return;
}
currentSubcategory = subcategoryName;
try {
const response = await fetch(`/api/standard-data/${currentCategory}/${subcategoryName}`);
standardData = await response.json();
if (standardData.error) {
showErrorMessage(standardData.error);
return;
}
// 生成参数输入控件
const container = document.getElementById('parameterInputs');
container.innerHTML = '';
// 参数1
if (standardData.parameter_1_name) {
const div = document.createElement('div');
div.className = 'form-group';
div.innerHTML = `
<label for="param1">${standardData.parameter_1_name} (${standardData.parameter_1_unit})</label>
<input type="number" id="param1" placeholder="请输入${standardData.parameter_1_name}" step="0.1">
`;
container.appendChild(div);
}
// 参数2
if (standardData.parameter_2_name) {
const div = document.createElement('div');
div.className = 'form-group';
div.innerHTML = `
<label for="param2">${standardData.parameter_2_name} (${standardData.parameter_2_unit})</label>
<input type="number" id="param2" placeholder="请输入${standardData.parameter_2_name}" step="0.1">
`;
container.appendChild(div);
}
// 参数3(如有)
if (standardData.parameter_3_name) {
const div = document.createElement('div');
div.className = 'form-group';
div.innerHTML = `
<label for="param3">${standardData.parameter_3_name} (${standardData.parameter_3_unit})</label>
<input type="number" id="param3" placeholder="请输入${standardData.parameter_3_name}" step="0.1">
`;
container.appendChild(div);
}
// 显示备注信息
if (standardData.remarks) {
const remarksDiv = document.createElement('div');
remarksDiv.className = 'info';
remarksDiv.style.marginTop = '10px';
remarksDiv.style.padding = '8px';
remarksDiv.style.background = '#f8f9fa';
remarksDiv.style.borderRadius = '4px';
remarksDiv.innerHTML = `<strong>备注:</strong>${standardData.remarks}`;
container.appendChild(remarksDiv);
}
} catch (error) {
console.error('加载标准数据失败:', error);
showErrorMessage('加载标准数据失败');
}
}
// 收集调整系数
function collectAdjustments() {
const adjustments = {};
// 海拔调整
const altitude = document.getElementById('altitude');
if (altitude && altitude.value) {
adjustments['海拔调整'] = parseFloat(altitude.value);
}
// 不停航施工
const stopTime = document.getElementById('stopTime');
if (stopTime && stopTime.value) {
adjustments['不停航施工'] = parseFloat(stopTime.value);
}
// 冬休期调整
const winterRegion = document.getElementById('winterRegion');
if (winterRegion && winterRegion.value) {
adjustments['冬休期调整'] = parseInt(winterRegion.value);
}
// 灯光站改造
const isRenovation = document.getElementById('isRenovation');
if (isRenovation && isRenovation.value) {
const coefficient = parseFloat(isRenovation.value);
if (coefficient !== 1) {
adjustments['灯光站改造'] = coefficient;
}
}
return adjustments;
}
// 计算工期
async function calculate() {
if (!currentCategory || !currentSubcategory) {
showErrorMessage('请先选择工程类别');
return;
}
// 收集参数
const projectName = document.getElementById('projectName').value || '未命名项目';
const parameters = {};
if (standardData.parameter_1_name) {
const param1 = document.getElementById('param1');
if (param1 && param1.value) {
parameters[standardData.parameter_1_name] = parseFloat(param1.value);
}
}
if (standardData.parameter_2_name) {
const param2 = document.getElementById('param2');
if (param2 && param2.value) {
parameters[standardData.parameter_2_name] = parseFloat(param2.value);
}
}
if (standardData.parameter_3_name) {
const param3 = document.getElementById('param3');
if (param3 && param3.value) {
parameters[standardData.parameter_3_name] = parseFloat(param3.value);
}
}
// 收集调整系数
const adjustments = collectAdjustments();
// 验证参数
if (Object.keys(parameters).length === 0) {
showErrorMessage('请至少输入一个参数');
return;
}
// 显示加载状态
const resultArea = document.getElementById('resultArea');
resultArea.innerHTML = '<div class="loading"></div> 计算中...';
try {
const response = await fetch('/api/calculate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
project_name: projectName,
category_id: currentCategory,
subcategory_name: currentSubcategory,
parameters: parameters,
adjustments: adjustments
})
});
const result = await response.json();
if (result.error) {
showErrorMessage(result.error);
return;
}
// 显示结果
displayResult(result);
// 刷新历史记录
loadHistory();
} catch (error) {
console.error('计算失败:', error);
showErrorMessage('计算失败,请检查输入参数');
}
}
// 显示计算结果
function displayResult(result) {
const container = document.getElementById('resultArea');
let html = `
<div class="result-card">
<h3>标准工期</h3>
<div class="result-value">${result.standard_duration} 日历天</div>
<div class="result-details">
依据:${result.reference_standard}<br>
表格:${result.table_number}
</div>
</div>
`;
if (result.adjustments && result.adjustments.length > 0) {
html += `
<div class="result-card">
<h3>调整系数应用</h3>
<div class="adjustment-list">
`;
result.adjustments.forEach(adj => {
html += `
<div class="adjustment-item">
<span class="type">${adj.type}</span>:
${adj.condition} → 系数 ${adj.coefficient}
<br><small>${adj.description}</small>
</div>
`;
});
html += `
</div>
</div>
`;
}
html += `
<div class="result-card">
<h3>最终工期</h3>
<div class="result-value" style="color: #28a745;">${result.final_duration} 日历天</div>
<div class="info" style="margin-top: 10px;">
<strong>注意:</strong>此结果为标准工期计算,实际工期需结合项目具体情况确定。
</div>
</div>
<div class="button-group">
<button class="btn btn-secondary" onclick="downloadReport(${result.record_id})">下载报告</button>
</div>
`;
container.innerHTML = html;
}
// 下载报告
async function downloadReport(recordId) {
try {
const response = await fetch(`/download-report/${recordId}`);
const text = await response.text();
// 创建Blob并下载
const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `工期计算报告_${new Date().toISOString().slice(0,10)}.txt`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
} catch (error) {
console.error('下载报告失败:', error);
showErrorMessage('下载报告失败');
}
}
// 加载历史记录
async function loadHistory() {
try {
const response = await fetch('/api/history');
const history = await response.json();
const container = document.getElementById('historyArea');
if (history.length === 0) {
container.innerHTML = '<div class="history-placeholder">暂无计算记录</div>';
return;
}
let html = '';
history.forEach(record => {
const date = new Date(record.created_at).toLocaleString('zh-CN');
html += `
<div class="history-item" onclick="loadHistoryDetail(${record.id})">
<div class="name">${record.project_name}</div>
<div class="time">${date}</div>
<div class="duration">最终工期: ${record.final_duration} 天</div>
</div>
`;
});
container.innerHTML = html;
} catch (error) {
console.error('加载历史记录失败:', error);
}
}
// 加载历史详情(简化版)
function loadHistoryDetail(recordId) {
alert(`记录ID: ${recordId}\n\n详细信息请查看下载的报告。`);
}
// 显示错误消息
function showErrorMessage(message) {
const container = document.getElementById('resultArea');
container.innerHTML = `<div class="error-message">${message}</div>`;
// 3秒后自动消失
setTimeout(() => {
if (container.innerHTML.includes('error-message')) {
container.innerHTML = '';
}
}, 5000);
}
// 重置表单
function resetForm() {
document.getElementById('projectName').value = '示例项目';
document.getElementById('categorySelect').value = '';
document.getElementById('subcategorySelect').innerHTML = '<option value="">请先选择一级类别...</option>';
document.getElementById('parameterInputs').innerHTML = '<p class="info">请先选择工程类别</p>';
document.getElementById('resultArea').innerHTML = '<div class="result-placeholder"><p>请输入参数并点击"计算工期"查看结果</p></div>';
currentCategory = null;
currentSubcategory = null;
standardData = null;
// 重置调整系数
const altitude = document.getElementById('altitude');
if (altitude) altitude.value = '1500';
const stopTime = document.getElementById('stopTime');
if (stopTime) stopTime.value = '8';
const winterRegion = document.getElementById('winterRegion');
if (winterRegion) winterRegion.value = '0';
const isRenovation = document.getElementById('isRenovation');
if (isRenovation) isRenovation.value = '1';
}
5. 使用说明
5.1 安装与运行
- 安装依赖:
bash
pip install flask
-
创建项目结构:
- 按照上述项目结构创建文件夹和文件
- 将代码分别复制到对应文件中
-
运行应用:
bash
python app.py
- 访问应用 :
- 打开浏览器,访问
http://localhost:5000
- 打开浏览器,访问
5.2 功能说明
-
工程类别选择:
- 一级类别:场道工程、目视助航工程、空管工程、弱电系统工程、供油工程
- 二级类别:根据选择的一级类别动态加载
-
参数输入:
- 根据选择的工程类别,动态显示需要输入的参数
- 参数包括工程量、面积、长度、设备数量等
-
调整系数:
- 海拔调整:根据海拔高度自动应用系数
- 不停航施工:根据停航时长应用调整公式
- 冬休期调整:根据地区分类增加冬休期天数
- 灯光站改造:改造项目乘以1.1系数
-
计算结果:
- 显示标准工期
- 显示调整系数应用详情
- 显示最终工期
- 提供下载报告功能
-
历史记录:
- 保存最近的10次计算记录
- 点击记录可查看详情
5.3 数据管理
- 标准数据存储在SQLite数据库中
- 可通过修改
database.py中的import_standard_data函数扩展数据 - 计算记录自动保存,便于追溯
6. 扩展建议
-
完整数据导入:
- 根据标准文档完整填充所有表格数据
- 完善所有特殊规则的计算逻辑
-
高级功能:
- 生成PDF报告
- 多项目并行计算
- 导出Excel表格
- 用户权限管理
-
移动端适配:
- 响应式设计,适配手机和平板
- 开发微信小程序版本
-
数据可视化:
- 图表展示工期分布
- 不同项目对比分析
这个实现提供了完整的框架和核心功能,您可以根据实际需求进一步完善和扩展。所有计算逻辑都基于标准文档,确保了结果的准确性和权威性。