677 lines
26 KiB
Python
677 lines
26 KiB
Python
import flet as ft
|
|
import pandas as pd
|
|
import os
|
|
|
|
class TeacherStudentMatcher:
|
|
def __init__(self, page: ft.Page):
|
|
self.page = page
|
|
self.page.title = "老师学员匹配系统"
|
|
self.page.window_width = 1400
|
|
self.page.window_height = 800
|
|
self.page.theme_mode = ft.ThemeMode.LIGHT
|
|
self.page.padding = 0
|
|
|
|
self.page.theme = ft.Theme(
|
|
font_family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif"
|
|
)
|
|
|
|
self.teachers = []
|
|
self.students = []
|
|
self.matches = []
|
|
|
|
self.setup_ui()
|
|
|
|
def setup_ui(self):
|
|
self.page.clean()
|
|
|
|
header = ft.Container(
|
|
content=ft.Row(
|
|
controls=[
|
|
ft.Column(
|
|
controls=[
|
|
ft.Text("老师学员匹配系统", size=28, weight=ft.FontWeight.W_600, color="#1a1a2e"),
|
|
ft.Text("Teacher-Student Matching System", size=12, weight=ft.FontWeight.W_400, color="#666666"),
|
|
],
|
|
spacing=2,
|
|
horizontal_alignment=ft.CrossAxisAlignment.START,
|
|
),
|
|
],
|
|
alignment=ft.MainAxisAlignment.START,
|
|
),
|
|
padding=ft.Padding.only(left=40, right=40, top=30, bottom=20),
|
|
)
|
|
|
|
button_bar = ft.Container(
|
|
content=ft.Row(
|
|
controls=[
|
|
ft.Text("数据操作", size=12, weight=ft.FontWeight.W_500, color="#888888"),
|
|
ft.Container(width=1, height=20, bgcolor="#e0e0e0"),
|
|
ft.Button(
|
|
"导入老师信息",
|
|
on_click=self.import_teachers,
|
|
bgcolor="#ffffff",
|
|
color="#333333",
|
|
height=36,
|
|
elevation=0,
|
|
style=ft.ButtonStyle(
|
|
shape=ft.RoundedRectangleBorder(radius=6),
|
|
side=ft.BorderSide(1, "#d0d0d0"),
|
|
),
|
|
),
|
|
ft.Button(
|
|
"导入学生信息",
|
|
on_click=self.import_students,
|
|
bgcolor="#ffffff",
|
|
color="#333333",
|
|
height=36,
|
|
elevation=0,
|
|
style=ft.ButtonStyle(
|
|
shape=ft.RoundedRectangleBorder(radius=6),
|
|
side=ft.BorderSide(1, "#d0d0d0"),
|
|
),
|
|
),
|
|
ft.Container(width=1, height=20, bgcolor="#e0e0e0"),
|
|
ft.Text("匹配操作", size=12, weight=ft.FontWeight.W_500, color="#888888"),
|
|
ft.Container(width=1, height=20, bgcolor="#e0e0e0"),
|
|
ft.Button(
|
|
"开始匹配",
|
|
on_click=self.start_match,
|
|
bgcolor="#2563eb",
|
|
color="#ffffff",
|
|
height=36,
|
|
elevation=0,
|
|
style=ft.ButtonStyle(
|
|
shape=ft.RoundedRectangleBorder(radius=6),
|
|
),
|
|
),
|
|
ft.Button(
|
|
"导出结果",
|
|
on_click=self.export_results,
|
|
bgcolor="#ffffff",
|
|
color="#333333",
|
|
height=36,
|
|
elevation=0,
|
|
style=ft.ButtonStyle(
|
|
shape=ft.RoundedRectangleBorder(radius=6),
|
|
side=ft.BorderSide(1, "#d0d0d0"),
|
|
),
|
|
),
|
|
ft.Button(
|
|
"清空",
|
|
on_click=self.clear_all,
|
|
bgcolor="#ffffff",
|
|
color="#666666",
|
|
height=36,
|
|
elevation=0,
|
|
style=ft.ButtonStyle(
|
|
shape=ft.RoundedRectangleBorder(radius=6),
|
|
side=ft.BorderSide(1, "#d0d0d0"),
|
|
),
|
|
),
|
|
],
|
|
spacing=12,
|
|
vertical_alignment=ft.CrossAxisAlignment.CENTER,
|
|
),
|
|
padding=ft.Padding.only(left=40, right=40, top=10, bottom=20),
|
|
)
|
|
|
|
divider1 = ft.Container(height=1, bgcolor="#f0f0f0")
|
|
|
|
main_content = ft.Row(
|
|
controls=[
|
|
ft.Container(
|
|
content=ft.Column(
|
|
controls=[
|
|
ft.Container(
|
|
content=ft.Column(
|
|
controls=[
|
|
ft.Text("老师信息", size=14, weight=ft.FontWeight.W_700, color="#1a1a2e"),
|
|
ft.Text("Teacher Information", size=10, weight=ft.FontWeight.W_500, color="#888888"),
|
|
],
|
|
spacing=4,
|
|
),
|
|
padding=ft.Padding.only(left=24, right=24, top=20, bottom=16),
|
|
),
|
|
ft.Container(height=1, bgcolor="#f5f5f5"),
|
|
ft.Container(
|
|
content=self.create_teacher_table(),
|
|
padding=ft.Padding.only(left=16, right=16, bottom=16),
|
|
expand=True,
|
|
),
|
|
],
|
|
),
|
|
bgcolor="#ffffff",
|
|
border_radius=12,
|
|
border=ft.Border.all(1, "#f0f0f0"),
|
|
expand=True,
|
|
margin=ft.Margin.only(left=20, right=10, top=0, bottom=20),
|
|
),
|
|
ft.Container(
|
|
content=ft.Column(
|
|
controls=[
|
|
ft.Container(
|
|
content=ft.Column(
|
|
controls=[
|
|
ft.Text("学生信息", size=14, weight=ft.FontWeight.W_700, color="#1a1a2e"),
|
|
ft.Text("Student Information", size=10, weight=ft.FontWeight.W_500, color="#888888"),
|
|
],
|
|
spacing=4,
|
|
),
|
|
padding=ft.Padding.only(left=24, right=24, top=20, bottom=16),
|
|
),
|
|
ft.Container(height=1, bgcolor="#f5f5f5"),
|
|
ft.Container(
|
|
content=self.create_student_table(),
|
|
padding=ft.Padding.only(left=16, right=16, bottom=16),
|
|
expand=True,
|
|
),
|
|
],
|
|
),
|
|
bgcolor="#ffffff",
|
|
border_radius=12,
|
|
border=ft.Border.all(1, "#f0f0f0"),
|
|
expand=True,
|
|
margin=ft.Margin.only(left=10, right=10, top=0, bottom=20),
|
|
),
|
|
ft.Container(
|
|
content=ft.Column(
|
|
controls=[
|
|
ft.Container(
|
|
content=ft.Column(
|
|
controls=[
|
|
ft.Text("匹配结果", size=14, weight=ft.FontWeight.W_700, color="#1a1a2e"),
|
|
ft.Text("Matching Results", size=10, weight=ft.FontWeight.W_500, color="#888888"),
|
|
],
|
|
spacing=4,
|
|
),
|
|
padding=ft.Padding.only(left=24, right=24, top=20, bottom=16),
|
|
),
|
|
ft.Container(height=1, bgcolor="#f5f5f5"),
|
|
ft.Container(
|
|
content=self.result_status_text(),
|
|
padding=ft.Padding.only(left=24, right=24, top=12, bottom=8),
|
|
),
|
|
ft.Container(height=1, bgcolor="#f5f5f5"),
|
|
ft.Container(
|
|
content=self.create_result_table(),
|
|
padding=ft.Padding.only(left=16, right=16, bottom=16),
|
|
expand=True,
|
|
),
|
|
],
|
|
),
|
|
bgcolor="#ffffff",
|
|
border_radius=12,
|
|
border=ft.Border.all(1, "#f0f0f0"),
|
|
expand=True,
|
|
margin=ft.Margin.only(left=10, right=20, top=0, bottom=20),
|
|
),
|
|
],
|
|
spacing=0,
|
|
expand=True,
|
|
vertical_alignment=ft.CrossAxisAlignment.START,
|
|
)
|
|
|
|
self.page.add(header)
|
|
self.page.add(button_bar)
|
|
self.page.add(divider1)
|
|
self.page.add(main_content)
|
|
|
|
def create_teacher_table(self):
|
|
self.teacher_table = ft.DataTable(
|
|
columns=[
|
|
ft.DataColumn(ft.Text("姓名", weight=ft.FontWeight.W_500, color="#555555")),
|
|
ft.DataColumn(ft.Text("类型", weight=ft.FontWeight.W_500, color="#555555")),
|
|
],
|
|
rows=[],
|
|
heading_row_color="#fafafa",
|
|
data_row_color={"hovered": "#f8f9fa", "selected": "#f0f4ff"},
|
|
border=ft.Border.all(0, "#f0f0f0"),
|
|
vertical_lines=ft.border.BorderSide(0, "#f0f0f0"),
|
|
horizontal_lines=ft.border.BorderSide(1, "#f5f5f5"),
|
|
column_spacing=30,
|
|
heading_row_height=40,
|
|
data_row_min_height=40,
|
|
)
|
|
return ft.ListView(
|
|
controls=[self.teacher_table],
|
|
spacing=0,
|
|
padding=0,
|
|
expand=True,
|
|
)
|
|
|
|
def create_student_table(self):
|
|
self.student_table = ft.DataTable(
|
|
columns=[
|
|
ft.DataColumn(ft.Text("姓名", weight=ft.FontWeight.W_500, color="#555555")),
|
|
ft.DataColumn(ft.Text("历史老师", weight=ft.FontWeight.W_500, color="#555555")),
|
|
],
|
|
rows=[],
|
|
heading_row_color="#fafafa",
|
|
data_row_color={"hovered": "#f8f9fa", "selected": "#f0f4ff"},
|
|
border=ft.Border.all(0, "#f0f0f0"),
|
|
vertical_lines=ft.border.BorderSide(0, "#f0f0f0"),
|
|
horizontal_lines=ft.border.BorderSide(1, "#f5f5f5"),
|
|
column_spacing=20,
|
|
heading_row_height=40,
|
|
data_row_min_height=40,
|
|
)
|
|
return ft.ListView(
|
|
controls=[self.student_table],
|
|
spacing=0,
|
|
padding=0,
|
|
expand=True,
|
|
)
|
|
|
|
def result_status_text(self):
|
|
self.status_text = ft.Text(
|
|
"等待匹配...",
|
|
size=12,
|
|
weight=ft.FontWeight.W_400,
|
|
color="#888888",
|
|
)
|
|
return self.status_text
|
|
|
|
def create_result_table(self):
|
|
self.result_table = ft.DataTable(
|
|
columns=[
|
|
ft.DataColumn(ft.Text("学生", weight=ft.FontWeight.W_500, color="#555555")),
|
|
ft.DataColumn(ft.Text("老师", weight=ft.FontWeight.W_500, color="#555555")),
|
|
ft.DataColumn(ft.Text("日期", weight=ft.FontWeight.W_500, color="#555555")),
|
|
ft.DataColumn(ft.Text("时段", weight=ft.FontWeight.W_500, color="#555555")),
|
|
ft.DataColumn(ft.Text("状态", weight=ft.FontWeight.W_500, color="#555555")),
|
|
],
|
|
rows=[],
|
|
heading_row_color="#fafafa",
|
|
data_row_color={"hovered": "#f8f9fa", "selected": "#f0f4ff"},
|
|
border=ft.Border.all(0, "#f0f0f0"),
|
|
vertical_lines=ft.border.BorderSide(0, "#f0f0f0"),
|
|
horizontal_lines=ft.border.BorderSide(1, "#f5f5f5"),
|
|
column_spacing=15,
|
|
heading_row_height=40,
|
|
data_row_min_height=40,
|
|
)
|
|
return ft.ListView(
|
|
controls=[self.result_table],
|
|
spacing=0,
|
|
padding=0,
|
|
expand=True,
|
|
)
|
|
|
|
def import_teachers(self, e):
|
|
import threading
|
|
from tkinter import filedialog
|
|
import tkinter as tk
|
|
|
|
result = {}
|
|
|
|
def ask_file():
|
|
root = tk.Tk()
|
|
root.withdraw()
|
|
root.attributes('-topmost', True)
|
|
file_path = filedialog.askopenfilename(
|
|
title="选择老师信息文件",
|
|
filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")]
|
|
)
|
|
result['path'] = file_path
|
|
root.destroy()
|
|
|
|
thread = threading.Thread(target=ask_file)
|
|
thread.start()
|
|
thread.join()
|
|
|
|
file_path = result.get('path', '')
|
|
if file_path:
|
|
try:
|
|
df = pd.read_excel(file_path)
|
|
self.teachers = []
|
|
|
|
fixed_columns = ["老师姓名", "老师类型"]
|
|
|
|
for _, row in df.iterrows():
|
|
availability = {}
|
|
for col in df.columns:
|
|
if col not in fixed_columns:
|
|
availability[col] = str(row.get(col, ""))
|
|
|
|
teacher_type = row.get("老师类型", "").strip()
|
|
if teacher_type in ["新", "新老师"]:
|
|
teacher_type = "新"
|
|
elif teacher_type in ["旧", "旧老师"]:
|
|
teacher_type = "旧"
|
|
else:
|
|
teacher_type = "旧"
|
|
|
|
teacher = {
|
|
"id": row.get("老师姓名", ""),
|
|
"name": row.get("老师姓名", ""),
|
|
"type": teacher_type,
|
|
"availability": availability
|
|
}
|
|
self.teachers.append(teacher)
|
|
|
|
self.update_teacher_table()
|
|
self.page.snack_bar = ft.SnackBar(
|
|
content=ft.Text(f"成功导入 {len(self.teachers)} 位老师"),
|
|
bgcolor="#10b981",
|
|
)
|
|
self.page.snack_bar.open = True
|
|
except Exception as ex:
|
|
self.page.snack_bar = ft.SnackBar(
|
|
content=ft.Text(f"导入失败: {str(ex)}"),
|
|
bgcolor="#ef4444",
|
|
)
|
|
self.page.snack_bar.open = True
|
|
|
|
def import_students(self, e):
|
|
import threading
|
|
from tkinter import filedialog
|
|
import tkinter as tk
|
|
|
|
result = {}
|
|
|
|
def ask_file():
|
|
root = tk.Tk()
|
|
root.withdraw()
|
|
root.attributes('-topmost', True)
|
|
file_path = filedialog.askopenfilename(
|
|
title="选择学生信息文件",
|
|
filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")]
|
|
)
|
|
result['path'] = file_path
|
|
root.destroy()
|
|
|
|
thread = threading.Thread(target=ask_file)
|
|
thread.start()
|
|
thread.join()
|
|
|
|
file_path = result.get('path', '')
|
|
if file_path:
|
|
try:
|
|
df = pd.read_excel(file_path)
|
|
self.students = []
|
|
|
|
fixed_columns = ["学生姓名", "历史匹配老师"]
|
|
|
|
for _, row in df.iterrows():
|
|
history = row.get("历史匹配老师", "")
|
|
history_teachers = [t.strip() for t in history.split(",")] if isinstance(history, str) else []
|
|
|
|
availability = {}
|
|
for col in df.columns:
|
|
if col not in fixed_columns:
|
|
availability[col] = str(row.get(col, ""))
|
|
|
|
student = {
|
|
"id": row.get("学生姓名", ""),
|
|
"name": row.get("学生姓名", ""),
|
|
"availability": availability,
|
|
"history": history_teachers
|
|
}
|
|
self.students.append(student)
|
|
|
|
self.update_student_table()
|
|
self.page.snack_bar = ft.SnackBar(
|
|
content=ft.Text(f"成功导入 {len(self.students)} 名学生"),
|
|
bgcolor="#10b981",
|
|
)
|
|
self.page.snack_bar.open = True
|
|
except Exception as ex:
|
|
self.page.snack_bar = ft.SnackBar(
|
|
content=ft.Text(f"导入失败: {str(ex)}"),
|
|
bgcolor="#ef4444",
|
|
)
|
|
self.page.snack_bar.open = True
|
|
|
|
def update_teacher_table(self):
|
|
self.teacher_table.rows = []
|
|
for teacher in self.teachers:
|
|
type_color = "#2563eb" if teacher["type"] == "新" else "#10b981"
|
|
self.teacher_table.rows.append(
|
|
ft.DataRow(
|
|
cells=[
|
|
ft.DataCell(ft.Text(teacher["name"], size=13, weight=ft.FontWeight.W_500, color="#333333")),
|
|
ft.DataCell(ft.Text(teacher["type"], size=12, weight=ft.FontWeight.W_600, color=type_color)),
|
|
]
|
|
)
|
|
)
|
|
self.page.update()
|
|
|
|
def update_student_table(self):
|
|
self.student_table.rows = []
|
|
for student in self.students:
|
|
history_str = ", ".join(student["history"][:3])
|
|
if len(student["history"]) > 3:
|
|
history_str += "..."
|
|
self.student_table.rows.append(
|
|
ft.DataRow(
|
|
cells=[
|
|
ft.DataCell(ft.Text(student["name"], size=13, weight=ft.FontWeight.W_500, color="#333333")),
|
|
ft.DataCell(ft.Text(history_str, size=12, weight=ft.FontWeight.W_500, color="#666666")),
|
|
]
|
|
)
|
|
)
|
|
self.page.update()
|
|
|
|
def parse_time_slots(self, time_str):
|
|
if not time_str:
|
|
return []
|
|
slots = []
|
|
for slot in time_str.split(';'):
|
|
slot = slot.strip()
|
|
if slot and '-' in slot:
|
|
parts = slot.split('-')
|
|
if len(parts) == 2:
|
|
start, end = parts
|
|
slots.append((start, end))
|
|
return slots
|
|
|
|
def start_match(self, e):
|
|
if not self.teachers:
|
|
self.page.snack_bar = ft.SnackBar(
|
|
content=ft.Text("请先导入老师信息"),
|
|
bgcolor="#f59e0b",
|
|
)
|
|
self.page.snack_bar.open = True
|
|
return
|
|
|
|
if not self.students:
|
|
self.page.snack_bar = ft.SnackBar(
|
|
content=ft.Text("请先导入学生信息"),
|
|
bgcolor="#f59e0b",
|
|
)
|
|
self.page.snack_bar.open = True
|
|
return
|
|
|
|
try:
|
|
self.matches = []
|
|
|
|
teacher_assigned_days = {}
|
|
for teacher in self.teachers:
|
|
teacher_assigned_days[teacher["id"]] = set()
|
|
|
|
teacher_match_count = {}
|
|
for teacher in self.teachers:
|
|
teacher_match_count[teacher["id"]] = 0
|
|
|
|
for student in self.students:
|
|
matched = False
|
|
possible_matches = []
|
|
|
|
history_count = len(student["history"])
|
|
expected_teacher_type = "旧" if history_count == 0 else ("新" if history_count % 2 == 1 else "旧")
|
|
|
|
for teacher in self.teachers:
|
|
if teacher["id"] in student["history"]:
|
|
continue
|
|
|
|
if teacher["type"] != expected_teacher_type:
|
|
continue
|
|
|
|
for day, student_time in student["availability"].items():
|
|
if day not in teacher["availability"]:
|
|
continue
|
|
|
|
if day in teacher_assigned_days[teacher["id"]]:
|
|
continue
|
|
|
|
teacher_time = teacher["availability"][day]
|
|
|
|
if student_time and teacher_time:
|
|
student_slots = self.parse_time_slots(student_time)
|
|
teacher_slots = self.parse_time_slots(teacher_time)
|
|
|
|
for s_start, s_end in student_slots:
|
|
for t_start, t_end in teacher_slots:
|
|
if s_start == t_start and s_end == t_end:
|
|
possible_matches.append((teacher, day, s_start, s_end))
|
|
|
|
if possible_matches:
|
|
possible_matches.sort(key=lambda x: teacher_match_count[x[0]["id"]])
|
|
|
|
teacher, day, s_start, s_end = possible_matches[0]
|
|
|
|
match = {
|
|
"student_name": student["name"],
|
|
"teacher_name": teacher["name"],
|
|
"match_date": day,
|
|
"match_period": f"{s_start}-{s_end}",
|
|
"status": "成功"
|
|
}
|
|
|
|
self.matches.append(match)
|
|
|
|
student["history"].append(teacher["id"])
|
|
|
|
teacher_assigned_days[teacher["id"]].add(day)
|
|
|
|
teacher_match_count[teacher["id"]] += 1
|
|
|
|
matched = True
|
|
|
|
if not matched:
|
|
match = {
|
|
"student_name": student["name"],
|
|
"teacher_name": "-",
|
|
"match_date": "-",
|
|
"match_period": "-",
|
|
"status": "未匹配"
|
|
}
|
|
self.matches.append(match)
|
|
|
|
self.update_result_table()
|
|
success_count = len([m for m in self.matches if m["status"] == "成功"])
|
|
self.status_text.value = f"匹配完成 · 成功 {success_count} / {len(self.matches)}"
|
|
self.status_text.color = "#10b981" if success_count == len(self.matches) else "#f59e0b"
|
|
self.page.snack_bar = ft.SnackBar(
|
|
content=ft.Text(f"匹配完成!成功匹配 {success_count} 名学生"),
|
|
bgcolor="#10b981",
|
|
)
|
|
self.page.snack_bar.open = True
|
|
|
|
except Exception as ex:
|
|
self.page.snack_bar = ft.SnackBar(
|
|
content=ft.Text(f"匹配失败: {str(ex)}"),
|
|
bgcolor="#ef4444",
|
|
)
|
|
self.page.snack_bar.open = True
|
|
|
|
def update_result_table(self):
|
|
self.result_table.rows = []
|
|
for match in self.matches:
|
|
is_success = match["status"] == "成功"
|
|
bg_color = "#f0fdf4" if is_success else "#fef2f2"
|
|
status_color = "#10b981" if is_success else "#ef4444"
|
|
self.result_table.rows.append(
|
|
ft.DataRow(
|
|
cells=[
|
|
ft.DataCell(ft.Text(match["student_name"], size=13, weight=ft.FontWeight.W_500, color="#333333")),
|
|
ft.DataCell(ft.Text(match["teacher_name"], size=13, weight=ft.FontWeight.W_500, color="#333333")),
|
|
ft.DataCell(ft.Text(match["match_date"], size=12, weight=ft.FontWeight.W_500, color="#555555")),
|
|
ft.DataCell(ft.Text(match["match_period"], size=12, weight=ft.FontWeight.W_500, color="#666666")),
|
|
ft.DataCell(ft.Container(
|
|
content=ft.Text(match["status"], size=12, weight=ft.FontWeight.W_600, color=status_color),
|
|
bgcolor=bg_color,
|
|
border_radius=4,
|
|
padding=ft.padding.symmetric(horizontal=8, vertical=2),
|
|
)),
|
|
]
|
|
)
|
|
)
|
|
self.page.update()
|
|
|
|
def export_results(self, e):
|
|
if not self.matches:
|
|
self.page.snack_bar = ft.SnackBar(
|
|
content=ft.Text("没有匹配结果可导出"),
|
|
bgcolor="#f59e0b",
|
|
)
|
|
self.page.snack_bar.open = True
|
|
return
|
|
|
|
import threading
|
|
from tkinter import filedialog
|
|
import tkinter as tk
|
|
|
|
result = {}
|
|
|
|
def ask_save():
|
|
root = tk.Tk()
|
|
root.withdraw()
|
|
root.attributes('-topmost', True)
|
|
file_path = filedialog.asksaveasfilename(
|
|
title="保存匹配结果",
|
|
defaultextension=".xlsx",
|
|
filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")]
|
|
)
|
|
result['path'] = file_path
|
|
root.destroy()
|
|
|
|
thread = threading.Thread(target=ask_save)
|
|
thread.start()
|
|
thread.join()
|
|
|
|
file_path = result.get('path', '')
|
|
if not file_path:
|
|
return
|
|
|
|
try:
|
|
df = pd.DataFrame(self.matches)
|
|
df.columns = ["学生", "老师", "日期", "时段", "状态"]
|
|
|
|
df.to_excel(file_path, index=False)
|
|
self.page.snack_bar = ft.SnackBar(
|
|
content=ft.Text(f"结果已导出到: {file_path}"),
|
|
bgcolor="#10b981",
|
|
)
|
|
self.page.snack_bar.open = True
|
|
except Exception as ex:
|
|
self.page.snack_bar = ft.SnackBar(
|
|
content=ft.Text(f"导出失败: {str(ex)}"),
|
|
bgcolor="#ef4444",
|
|
)
|
|
self.page.snack_bar.open = True
|
|
|
|
def clear_all(self, e):
|
|
self.teachers = []
|
|
self.students = []
|
|
self.matches = []
|
|
|
|
self.teacher_table.rows = []
|
|
self.student_table.rows = []
|
|
self.result_table.rows = []
|
|
|
|
self.status_text.value = "等待匹配..."
|
|
self.status_text.color = "#888888"
|
|
|
|
self.page.snack_bar = ft.SnackBar(
|
|
content=ft.Text("已清空所有数据"),
|
|
bgcolor="#6b7280",
|
|
)
|
|
self.page.snack_bar.open = True
|
|
|
|
def main(page: ft.Page):
|
|
TeacherStudentMatcher(page)
|
|
|
|
if __name__ == "__main__":
|
|
ft.run(main)
|