Python pyqt+flask做一个简单实用的自动排班系统

这是一个基于Flask和PyQt的排班系统,可以将Web界面嵌入到桌面应用程序中。

系统界面:

功能特点:

  • 读取员工信息和现有排班表

  • 自动生成排班表

  • 美观的Web界面

  • 独立的桌面应用程序

整体架构:

系统采用前后端分离的架构设计,通过 PyQt5 的 WebEngine 组件将 Web 界面嵌入到桌面应用中。

├── 桌面应用层 (PyQt5)

│ └── WebEngine 视图

├── Web 层 (Flask)

│ ├── 路由控制

│ └── 业务逻辑

└── 数据层

├── CSV 数据文件

└── Excel 导出

核心模块:

主程序模块 (main.py)

  • 负责初始化 PyQt5 应用
  • 集成 Flask 服务器
  • 管理主窗口和 Web 视图

后端服务模块 (app.py)

  • 提供 RESTful API
  • 处理排班算法
  • 管理数据导入导出

前端界面模块 (templates/index.html)

  • 员工列表管理
  • 排班表显示
  • 用户交互处理

核心代码:main.py

python 复制代码
import sys
import time
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import QUrl
from flask import Flask
import threading
import os

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("排班系统")
        self.setGeometry(100, 100, 1200, 800)
        
        # 创建中心部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)
        
        # 创建Web视图
        self.web_view = QWebEngineView()
        layout.addWidget(self.web_view)
        
        # 启动Flask服务器
        self.start_flask_server()
        
        # 等待服务器启动后加载页面
        time.sleep(1)  # 给服务器一点启动时间
        self.web_view.setUrl(QUrl("http://127.0.0.1:3863"))
        
    def start_flask_server(self):
        # 在新线程中启动Flask服务器
        threading.Thread(target=self.run_flask, daemon=True).start()
        
    def run_flask(self):
        from app import app
        app.run(host='127.0.0.1', port=3863)

def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main() 

核心代码:app.py

python 复制代码
from flask import Flask, render_template, request, jsonify, send_file
import pandas as pd
from datetime import datetime, timedelta
import calendar
import json
import numpy as np
import os

app = Flask(__name__)

# 班次定义
SHIFTS = {
    '白班': 'D',
    '晚班': 'N',
    '休息': 'R'
}

# 读取员工数据
def load_employee_data():
    try:
        df = pd.read_csv('Employee.csv', encoding='utf-8')
        # 只返回员工姓名列
        return pd.DataFrame({'name': df["Employee'sName"]})
    except Exception as e:
        print(f"Error loading employee data: {e}")
        return pd.DataFrame({'name': []})

# 读取排班表
def load_schedule():
    try:
        df = pd.read_excel('客户服务部排班表20250301-20250331.xls')
        return df
    except Exception as e:
        print(f"Error loading schedule: {e}")
        return pd.DataFrame()

def get_month_calendar(year, month):
    cal = calendar.monthcalendar(year, month)
    return cal

def generate_monthly_schedule(employees, year, month):
    num_days = calendar.monthrange(year, month)[1]
    num_employees = len(employees)
    
    # 将employees列表转换为numpy数组
    employees_array = np.array(employees)
    
    # 创建排班表
    schedule = pd.DataFrame(index=employees, columns=range(1, num_days + 1))
    schedule.fillna('R', inplace=True)  # 默认全部休息
    
    # 为每一天分配班次
    for day in range(1, num_days + 1):
        # 确保每天有足够的白班和晚班
        day_employees = employees_array.copy()
        np.random.shuffle(day_employees)
        
        # 分配白班(约40%的员工)
        day_shifts = int(num_employees * 0.4)
        schedule.loc[day_employees[:day_shifts], day] = 'D'
        
        # 分配晚班(约30%的员工)
        night_shifts = int(num_employees * 0.3)
        schedule.loc[day_employees[day_shifts:day_shifts+night_shifts], day] = 'N'
    
    # 确保每周至少休息两天
    for employee in employees:
        for week in range(0, num_days, 7):
            week_schedule = schedule.loc[employee, week+1:min(week+7, num_days)]
            rest_days = (week_schedule == 'R').sum()
            if rest_days < 2:
                work_days = list(week_schedule[week_schedule != 'R'].index)
                if work_days:  # 确保有工作日可以调整
                    np.random.shuffle(work_days)
                    for i in range(min(2-rest_days, len(work_days))):
                        schedule.loc[employee, work_days[i]] = 'R'
    
    return schedule

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/api/employees')
def get_employees():
    df = load_employee_data()
    return jsonify(df.to_dict('records'))

@app.route('/api/calendar/<int:year>/<int:month>')
def get_calendar(year, month):
    cal = get_month_calendar(year, month)
    return jsonify(cal)

@app.route('/api/generate_schedule', methods=['POST'])
def generate_schedule():
    try:
        data = request.get_json()
        year = data.get('year', 2025)
        month = data.get('month', 1)
        selected_employees = data.get('employees', [])
        
        if not selected_employees:
            return jsonify({"status": "error", "message": "请选择员工"})
            
        schedule = generate_monthly_schedule(selected_employees, year, month)
        
        # 将DataFrame转换为字典格式
        schedule_dict = {}
        for employee in selected_employees:
            schedule_dict[employee] = schedule.loc[employee].to_dict()
            
        return jsonify({
            "status": "success",
            "schedule": schedule_dict,
            "message": "排班表生成成功"
        })
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)})

@app.route('/api/export_schedule', methods=['POST'])
def export_schedule():
    try:
        data = request.get_json()
        year = data.get('year', 2025)
        month = data.get('month', 1)
        schedule_data = data.get('schedule', {})
        
        # 创建新的排班表
        df = pd.DataFrame.from_dict(schedule_data, orient='index')
        
        # 设置列名为日期
        df.columns = [str(i) for i in range(1, len(df.columns) + 1)]
        
        # 重置索引,将员工名称作为一列
        df.reset_index(inplace=True)
        df.rename(columns={'index': '姓名'}, inplace=True)
        
        # 保存文件
        output_file = f'客户服务部排班表{year}{month:02d}01-{year}{month:02d}{calendar.monthrange(year, month)[1]}.xlsx'
        
        # 使用 openpyxl 引擎保存为 xlsx 格式
        df.to_excel(output_file, index=False, engine='openpyxl')
        
        # 返回文件下载路径
        return send_file(
            output_file,
            as_attachment=True,
            download_name=output_file,
            mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )
    except Exception as e:
        print(f"Export error: {str(e)}")  # 添加错误日志
        return jsonify({"status": "error", "message": f"导出失败: {str(e)}"})

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=3863, debug=True) 
相关推荐
apcipot_rain7 小时前
C++转Java日记(04)
java·开发语言·c++
特立独行的猫A8 小时前
我给女儿写了个打字练习:一边敲键盘,一边把小学英语单词记牢
java·后端·python
cpolar技术支持8 小时前
「我这边连不上你那个服务」—— 用清单驱动的 TCP 连通性只读看板,cpolar 让远端同事自己取证
python·内网穿透·tcp·cpolar·网络诊断
云和数据.ChenGuang8 小时前
LangGraph 异步 Node 的设计与写法实践
开发语言·人工智能·python·机器学习·php
天诰9 小时前
SA-K-11 检索会消失吗与四个未解问题
java·服务器·人工智能·python·机器学习·ai编程
benchmark_cc10 小时前
用 QuantDash 读取美股行情:AAPL.US 的运行前检查、排错顺序与数据校验清单
python·数据分析·pandas·量化交易·股票数据·quantdash
蜗牛一步步往上爬12 小时前
第十六章 C++语言在深度学习中的应用
开发语言·c++·深度学习
羲云网络14 小时前
GPT Image 2.6 接入指南:Flare、Sunburst 怎么选,编辑失败如何排查
人工智能·python·aigc·图像生成·acedatacloud
张张123y14 小时前
DSH 插件调试避坑:client.js 设置面板、热重载与高频报错排查表
开发语言·javascript·人工智能·开源·ecmascript·后端开发·ai agent
冻柠檬飞冰走茶17 小时前
字母移动游戏题解:贪心求最长可保留连续子串(附 C 实现)
c语言·开发语言·数据结构·算法·动态规划