322 lines
13 KiB
Python
322 lines
13 KiB
Python
|
|
import flet as ft
|
|||
|
|
from datetime import datetime
|
|||
|
|
|
|||
|
|
def main(page: ft.Page):
|
|||
|
|
# Состояние формы
|
|||
|
|
state = {
|
|||
|
|
"current_step": 0,
|
|||
|
|
"operators_name": "",
|
|||
|
|
"factory": "",
|
|||
|
|
"line": "",
|
|||
|
|
"error_zone": "",
|
|||
|
|
"problems": set(), # будем накапливать выбранные значения
|
|||
|
|
"downtime_reasons": set(),
|
|||
|
|
"custom_problem": "",
|
|||
|
|
"custom_reason": "",
|
|||
|
|
"fix_method": "",
|
|||
|
|
"start_at": "",
|
|||
|
|
"end_at": "",
|
|||
|
|
# Зависимые опции:
|
|||
|
|
"line_options": [],
|
|||
|
|
"zone_options": [],
|
|||
|
|
"problem_options": [],
|
|||
|
|
"reason_options": [],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Справочники (аналогичные примерам в React/Ionic)
|
|||
|
|
factory_line_map = {
|
|||
|
|
"Славда": ["ПЭТ", "19л", "5л"],
|
|||
|
|
"Скит": ["Sipa", "Devin", "JR", "19 Литров", "5 Литров"]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
line_zone_map = {
|
|||
|
|
"Sipa": ["Сериализация", "Упаковка", "Сборка палеты", "Закрытие палеты"],
|
|||
|
|
"Devin": ["Сериализация", "Упаковка", "Сборка палеты"],
|
|||
|
|
"JR": ["Сериализация", "Упаковка", "Сборка палеты", "Закрытие палеты"],
|
|||
|
|
"19 Литров": ["Валидация", "Тонкий клиент"],
|
|||
|
|
"5 Литров": ["Валидация", "Тонкий клиент"],
|
|||
|
|
"ПЭТ": ["Сериализация", "Упаковка", "Сборка палеты", "Закрытие палеты"],
|
|||
|
|
"5л": ["Сериализация", "Упаковка", "Сборка палеты", "Закрытие палеты"],
|
|||
|
|
"19л": ["Валидация", "Тонкий клиент"],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
common_problems = {
|
|||
|
|
"Славда": ["Не печатает код", "Камера не считывает код", "Сбой принтера"],
|
|||
|
|
"Скит": ["Не печатает код", "Камера не считывает код", "Сбой принтера"],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
common_downtime_reasons = {
|
|||
|
|
"Славда": ["Сбой камеры", "Ошибка валидации", "Проблема с печатью"],
|
|||
|
|
"Скит": ["Сбой камеры", "Ошибка валидации", "Проблема с печатью"],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Элементы UI, которые будут обновляться
|
|||
|
|
operators_name_field = ft.TextField(label="Имя оператора *", width=300)
|
|||
|
|
factory_dropdown = ft.Dropdown(
|
|||
|
|
label="Завод *",
|
|||
|
|
options=[
|
|||
|
|
ft.dropdown.Option("Славда"),
|
|||
|
|
ft.dropdown.Option("Скит")
|
|||
|
|
],
|
|||
|
|
width=300
|
|||
|
|
)
|
|||
|
|
line_dropdown = ft.Dropdown(label="Линия *", width=300)
|
|||
|
|
zone_dropdown = ft.Dropdown(label="Зона *", width=300)
|
|||
|
|
|
|||
|
|
fix_method_field = ft.TextField(label="Как решили проблему? *", width=300)
|
|||
|
|
start_at_field = ft.TextField(label="Начало простоя (YYYY-MM-DD HH:MM) *", width=300)
|
|||
|
|
end_at_field = ft.TextField(label="Окончание простоя (YYYY-MM-DD HH:MM) *", width=300)
|
|||
|
|
|
|||
|
|
custom_problem_field = ft.TextField(label="Своя проблема (необязательно)", width=300)
|
|||
|
|
custom_reason_field = ft.TextField(label="Своя причина (необязательно)", width=300)
|
|||
|
|
|
|||
|
|
# Для выбора нескольких вариантов – используем колонки с чекбоксами
|
|||
|
|
problems_checkboxes = ft.Column()
|
|||
|
|
reasons_checkboxes = ft.Column()
|
|||
|
|
|
|||
|
|
# Функция для показа уведомлений (SnackBar)
|
|||
|
|
def show_snackbar(message, bgcolor):
|
|||
|
|
page.snack_bar = ft.SnackBar(ft.Text(message), bgcolor=bgcolor)
|
|||
|
|
page.snack_bar.open = True
|
|||
|
|
page.update()
|
|||
|
|
|
|||
|
|
# Обновление зависимых списков при выборе завода
|
|||
|
|
def update_factory_dependent():
|
|||
|
|
factory = state["factory"]
|
|||
|
|
state["line_options"] = factory_line_map.get(factory, [])
|
|||
|
|
line_dropdown.options = [ft.dropdown.Option(opt) for opt in state["line_options"]]
|
|||
|
|
line_dropdown.value = None
|
|||
|
|
state["line"] = ""
|
|||
|
|
|
|||
|
|
state["problem_options"] = common_problems.get(factory, [])
|
|||
|
|
state["reason_options"] = common_downtime_reasons.get(factory, [])
|
|||
|
|
update_problems_checkboxes()
|
|||
|
|
update_reasons_checkboxes()
|
|||
|
|
page.update()
|
|||
|
|
|
|||
|
|
# Обновление списка зон при выборе линии
|
|||
|
|
def update_line_dependent():
|
|||
|
|
line = state["line"]
|
|||
|
|
state["zone_options"] = line_zone_map.get(line, [])
|
|||
|
|
zone_dropdown.options = [ft.dropdown.Option(opt) for opt in state["zone_options"]]
|
|||
|
|
zone_dropdown.value = None
|
|||
|
|
state["error_zone"] = ""
|
|||
|
|
page.update()
|
|||
|
|
|
|||
|
|
def update_problems_checkboxes():
|
|||
|
|
problems_checkboxes.controls.clear()
|
|||
|
|
for prob in state["problem_options"]:
|
|||
|
|
chk = ft.Checkbox(label=prob, value=False)
|
|||
|
|
def on_change(e, label=prob):
|
|||
|
|
if e.control.value:
|
|||
|
|
state["problems"].add(label)
|
|||
|
|
else:
|
|||
|
|
state["problems"].discard(label)
|
|||
|
|
chk.on_change = on_change
|
|||
|
|
problems_checkboxes.controls.append(chk)
|
|||
|
|
page.update()
|
|||
|
|
|
|||
|
|
def update_reasons_checkboxes():
|
|||
|
|
reasons_checkboxes.controls.clear()
|
|||
|
|
for reason in state["reason_options"]:
|
|||
|
|
chk = ft.Checkbox(label=reason, value=False)
|
|||
|
|
def on_change(e, label=reason):
|
|||
|
|
if e.control.value:
|
|||
|
|
state["downtime_reasons"].add(label)
|
|||
|
|
else:
|
|||
|
|
state["downtime_reasons"].discard(label)
|
|||
|
|
chk.on_change = on_change
|
|||
|
|
reasons_checkboxes.controls.append(chk)
|
|||
|
|
page.update()
|
|||
|
|
|
|||
|
|
# Функция валидации этапов
|
|||
|
|
def validate_step():
|
|||
|
|
step = state["current_step"]
|
|||
|
|
if step == 0:
|
|||
|
|
if not operators_name_field.value.strip() or not factory_dropdown.value:
|
|||
|
|
show_snackbar("Введите имя и выберите завод", ft.colors.RED)
|
|||
|
|
return False
|
|||
|
|
elif step == 1:
|
|||
|
|
if not line_dropdown.value or not zone_dropdown.value:
|
|||
|
|
show_snackbar("Выберите линию и зону", ft.colors.RED)
|
|||
|
|
return False
|
|||
|
|
elif step == 2:
|
|||
|
|
total_problems = set(state["problems"])
|
|||
|
|
if custom_problem_field.value and custom_problem_field.value.strip():
|
|||
|
|
total_problems.add(custom_problem_field.value.strip())
|
|||
|
|
total_reasons = set(state["downtime_reasons"])
|
|||
|
|
if custom_reason_field.value and custom_reason_field.value.strip():
|
|||
|
|
total_reasons.add(custom_reason_field.value.strip())
|
|||
|
|
if not total_problems:
|
|||
|
|
show_snackbar("Укажите хотя бы одну проблему", ft.colors.RED)
|
|||
|
|
return False
|
|||
|
|
if not total_reasons:
|
|||
|
|
show_snackbar("Укажите хотя бы одну причину простоя", ft.colors.RED)
|
|||
|
|
return False
|
|||
|
|
elif step == 3:
|
|||
|
|
if not fix_method_field.value.strip():
|
|||
|
|
show_snackbar("Опишите, как решили проблему", ft.colors.RED)
|
|||
|
|
return False
|
|||
|
|
if not start_at_field.value.strip() or not end_at_field.value.strip():
|
|||
|
|
show_snackbar("Укажите время начала и окончания", ft.colors.RED)
|
|||
|
|
return False
|
|||
|
|
try:
|
|||
|
|
datetime.strptime(start_at_field.value.strip(), "%Y-%m-%d %H:%M")
|
|||
|
|
datetime.strptime(end_at_field.value.strip(), "%Y-%m-%d %H:%M")
|
|||
|
|
except ValueError:
|
|||
|
|
show_snackbar("Неверный формат даты/времени", ft.colors.RED)
|
|||
|
|
return False
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
# Контейнер для отображения содержимого этапа
|
|||
|
|
content_column = ft.Column()
|
|||
|
|
# Прогресс-бар (значение от 0 до 1)
|
|||
|
|
progress_bar = ft.ProgressBar(value=(state["current_step"]+1)/4)
|
|||
|
|
|
|||
|
|
# Кнопки навигации
|
|||
|
|
next_button = ft.ElevatedButton(text="Далее")
|
|||
|
|
back_button = ft.ElevatedButton(text="Назад")
|
|||
|
|
submit_button = ft.ElevatedButton(text="Отправить")
|
|||
|
|
|
|||
|
|
# Обновление UI в зависимости от этапа
|
|||
|
|
def update_ui():
|
|||
|
|
content_column.controls.clear()
|
|||
|
|
progress_bar.value = (state["current_step"]+1)/4
|
|||
|
|
|
|||
|
|
if state["current_step"] == 0:
|
|||
|
|
content_column.controls.extend([
|
|||
|
|
operators_name_field,
|
|||
|
|
factory_dropdown,
|
|||
|
|
])
|
|||
|
|
elif state["current_step"] == 1:
|
|||
|
|
content_column.controls.extend([
|
|||
|
|
line_dropdown,
|
|||
|
|
zone_dropdown,
|
|||
|
|
])
|
|||
|
|
elif state["current_step"] == 2:
|
|||
|
|
content_column.controls.extend([
|
|||
|
|
ft.Text("Проблемы (можно выбрать несколько):"),
|
|||
|
|
problems_checkboxes,
|
|||
|
|
custom_problem_field,
|
|||
|
|
ft.Text("Причины простоя (можно выбрать несколько):"),
|
|||
|
|
reasons_checkboxes,
|
|||
|
|
custom_reason_field,
|
|||
|
|
])
|
|||
|
|
elif state["current_step"] == 3:
|
|||
|
|
content_column.controls.extend([
|
|||
|
|
fix_method_field,
|
|||
|
|
start_at_field,
|
|||
|
|
end_at_field,
|
|||
|
|
])
|
|||
|
|
|
|||
|
|
# Кнопки навигации
|
|||
|
|
nav_buttons = []
|
|||
|
|
if state["current_step"] > 0:
|
|||
|
|
nav_buttons.append(back_button)
|
|||
|
|
if state["current_step"] < 3:
|
|||
|
|
nav_buttons.append(next_button)
|
|||
|
|
if state["current_step"] == 3:
|
|||
|
|
nav_buttons.append(submit_button)
|
|||
|
|
content_column.controls.append(ft.Row(controls=nav_buttons, spacing=20))
|
|||
|
|
page.update()
|
|||
|
|
|
|||
|
|
# Обработчики кнопок
|
|||
|
|
def on_next(e):
|
|||
|
|
if validate_step():
|
|||
|
|
if state["current_step"] == 0:
|
|||
|
|
state["operators_name"] = operators_name_field.value
|
|||
|
|
state["factory"] = factory_dropdown.value
|
|||
|
|
update_factory_dependent()
|
|||
|
|
elif state["current_step"] == 1:
|
|||
|
|
state["line"] = line_dropdown.value
|
|||
|
|
state["error_zone"] = zone_dropdown.value
|
|||
|
|
state["current_step"] += 1
|
|||
|
|
update_ui()
|
|||
|
|
|
|||
|
|
def on_back(e):
|
|||
|
|
state["current_step"] -= 1
|
|||
|
|
update_ui()
|
|||
|
|
|
|||
|
|
def on_submit(e):
|
|||
|
|
if not validate_step():
|
|||
|
|
return
|
|||
|
|
state["fix_method"] = fix_method_field.value
|
|||
|
|
state["start_at"] = start_at_field.value
|
|||
|
|
state["end_at"] = end_at_field.value
|
|||
|
|
|
|||
|
|
total_problems = list(state["problems"])
|
|||
|
|
if custom_problem_field.value and custom_problem_field.value.strip():
|
|||
|
|
total_problems.append(custom_problem_field.value.strip())
|
|||
|
|
total_reasons = list(state["downtime_reasons"])
|
|||
|
|
if custom_reason_field.value and custom_reason_field.value.strip():
|
|||
|
|
total_reasons.append(custom_reason_field.value.strip())
|
|||
|
|
|
|||
|
|
payload = {
|
|||
|
|
"factory": state["factory"],
|
|||
|
|
"line": state["line"],
|
|||
|
|
"operators_name": state["operators_name"],
|
|||
|
|
"error_zone": state["error_zone"],
|
|||
|
|
"problem": ", ".join(total_problems),
|
|||
|
|
"downtime_reason": ", ".join(total_reasons),
|
|||
|
|
"fix_method": state["fix_method"],
|
|||
|
|
"start_at": state["start_at"],
|
|||
|
|
"end_at": state["end_at"],
|
|||
|
|
}
|
|||
|
|
print("[DEBUG] Отправляем JSON:", payload)
|
|||
|
|
show_snackbar("Данные успешно отправлены!", ft.colors.GREEN)
|
|||
|
|
# Сброс формы
|
|||
|
|
state.update({
|
|||
|
|
"current_step": 0,
|
|||
|
|
"operators_name": "",
|
|||
|
|
"factory": "",
|
|||
|
|
"line": "",
|
|||
|
|
"error_zone": "",
|
|||
|
|
"problems": set(),
|
|||
|
|
"downtime_reasons": set(),
|
|||
|
|
"custom_problem": "",
|
|||
|
|
"custom_reason": "",
|
|||
|
|
"fix_method": "",
|
|||
|
|
"start_at": "",
|
|||
|
|
"end_at": "",
|
|||
|
|
"line_options": [],
|
|||
|
|
"zone_options": [],
|
|||
|
|
"problem_options": [],
|
|||
|
|
"reason_options": [],
|
|||
|
|
})
|
|||
|
|
operators_name_field.value = ""
|
|||
|
|
factory_dropdown.value = None
|
|||
|
|
line_dropdown.value = None
|
|||
|
|
zone_dropdown.value = None
|
|||
|
|
fix_method_field.value = ""
|
|||
|
|
start_at_field.value = ""
|
|||
|
|
end_at_field.value = ""
|
|||
|
|
custom_problem_field.value = ""
|
|||
|
|
custom_reason_field.value = ""
|
|||
|
|
problems_checkboxes.controls.clear()
|
|||
|
|
reasons_checkboxes.controls.clear()
|
|||
|
|
update_ui()
|
|||
|
|
|
|||
|
|
next_button.on_click = on_next
|
|||
|
|
back_button.on_click = on_back
|
|||
|
|
submit_button.on_click = on_submit
|
|||
|
|
|
|||
|
|
def on_factory_change(e):
|
|||
|
|
state["factory"] = factory_dropdown.value
|
|||
|
|
update_factory_dependent()
|
|||
|
|
factory_dropdown.on_change = on_factory_change
|
|||
|
|
|
|||
|
|
def on_line_change(e):
|
|||
|
|
state["line"] = line_dropdown.value
|
|||
|
|
update_line_dependent()
|
|||
|
|
line_dropdown.on_change = on_line_change
|
|||
|
|
|
|||
|
|
def on_zone_change(e):
|
|||
|
|
state["error_zone"] = zone_dropdown.value
|
|||
|
|
zone_dropdown.on_change = on_zone_change
|
|||
|
|
|
|||
|
|
page.title = "Журнал простоев (мобильная версия) - Flet"
|
|||
|
|
page.add(progress_bar, content_column)
|
|||
|
|
update_ui()
|
|||
|
|
|
|||
|
|
ft.app(target=main)
|