import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import sqlite3
import time
from datetime import datetime, date
from tkinter.simpledialog import Dialog
import openpyxl
from openpyxl.styles import Font, Alignment, Border, Side
from openpyxl.utils import get_column_letter
import os
import sys
class DatePickerDialog(Dialog):
"""自定义日期选择对话框"""
def __init__(self, parent, title=None, initial_date=None):
self.selected_date = initial_date or date.today()
super().__init__(parent, title=title or "选择日期")
def body(self, master):
# 年份选择
ttk.Label(master, text="年:").grid(row=0, column=0, padx=5, pady=5)
self.year_var = tk.IntVar(value=self.selected_date.year)
years = list(range(2020, 2031))
(master, textvariable=self.year_var, values=years, width=6).grid(row=0, column=1, padx=5, pady=5)
# 月份选择
ttk.Label(master, text="月:").grid(row=0, column=2, padx=5, pady=5)
self.month_var = tk.IntVar(value=self.selected_date.month)
months = list(range(1, 13))
(master, textvariable=self.month_var, values=months, width=4).grid(row=0, column=3, padx=5, pady=5)
# 日期选择
ttk.Label(master, text="日:").grid(row=0, column=4, padx=5, pady=5)
self.day_var = tk.IntVar(value=self.selected_date.day)
days = list(range(1, 32))
(master, textvariable=self.day_var, values=days, width=4).grid(row=0, column=5, padx=5, pady=5)
return master
def apply(self):
try:
year = self.year_var.get()
month = self.month_var.get()
day = self.day_var.get()
self.selected_date = date(year, month, day)
except ValueError:
messagebox.showerror("错误", "无效的日期!")
self.selected_date = date.today()
class LoginWindow:
"""登录界面"""
def __init__(self, root, main_app):
self.root = root
self.main_app = main_app
self.root.title("XXXXX进销存管理系统")
self.root.geometry("600x400")
self.root.configure(bg="#2c3e50")
# 设置窗口居中
self.center_window(600, 400)
# 创建主框架
self.main_frame = tk.Frame(self.root, bg="#2c3e50")
self.main_frame.pack(fill="both", expand=True, padx=50, pady=50)
# 系统标题
title = tk.Label(
self.main_frame,
text="XXXXX进销存管理系统",
font=("微软雅黑", 24, "bold"),
fg="#ecf0f1",
bg="#2c3e50"
)
title.pack(pady=(30, 50))
# 登录信息
login_frame = tk.Frame(self.main_frame, bg="#34495e", padx=20, pady=20, bd=0, relief="flat")
login_frame.pack(fill="x")
# 用户名
tk.Label(
login_frame,
text="用户名:",
font=("微软雅黑", 12),
fg="#ecf0f1",
bg="#34495e"
).grid(row=0, column=0, padx=5, pady=10, sticky="e")
self.username = ttk.Entry(login_frame, width=20, font=("微软雅黑", 12))
self.username.grid(row=0, column=1, padx=5, pady=10)
self.username.insert(0, "admin") # 只提供用户名,不提供密码
# 密码
tk.Label(
login_frame,
text="密 码:",
font=("微软雅黑", 12),
fg="#ecf0f1",
bg="#34495e"
).grid(row=1, column=0, padx=5, pady=10, sticky="e")
self.password = ttk.Entry(login_frame, width=20, font=("微软雅黑", 12), show="*")
self.password.grid(row=1, column=1, padx=5, pady=10)
# 登录按钮
login_btn = ttk.Button(
login_frame,
text="登 录",
width=15,
command=self.login,
style="Accent.TButton"
)
login_btn.grid(row=2, column=0, columnspan=2, pady=20)
# 状态信息
self.status = tk.Label(
self.main_frame,
text="",
font=("微软雅黑", 10),
fg="#e74c3c",
bg="#2c3e50"
)
self.status.pack(pady=10)
# 版权信息
copyright = tk.Label(
self.main_frame,
text="© 2025 XXXXX",
font=("微软雅黑", 10),
fg="#bdc3c7",
bg="#2c3e50"
)
copyright.pack(side="bottom", pady=10)
# 绑定回车键
self.root.bind('<Return>', lambda event: self.login())
# 设置样式
self.set_style()
def set_style(self):
style = ttk.Style()
style.configure("Accent.TButton",
foreground="white",
background="#3498db",
font=("微软雅黑", 11, "bold"),
borderwidth=0)
style.map("Accent.TButton",
background=[('active', '#2980b9'), ('pressed', '#1f618d')])
def center_window(self, width, height):
screen_width = self.root.winfo_screenwidth()
screen_height = self.root.winfo_screenheight()
x = (screen_width - width) // 2
y = (screen_height - height) // 2
self.root.geometry(f"{width}x{height}+{x}+{y}")
def login(self):
username = self.username.get()
password = self.password.get()
if username == "admin" and password == "******":
self.status.config(text="登录成功,正在进入系统...", fg="#2ecc71")
self.root.update()
time.sleep(1)
self.root.destroy()
self.main_app.show_main_window()
else:
self.status.config(text="用户名或密码错误!", fg="#e74c3c")
class PrintPreviewWindow:
"""打印预览窗口"""
def __init__(self, parent, data, headers, title):
self.parent = parent
self.data = data
self.headers = headers
self.title = title
self.window = tk.Toplevel(parent)
self.window.title("打印预览")
self.window.geometry("900x600")
self.window.configure(bg="#f5f5f5")
# 设置窗口居中
self.center_window(900, 600)
# 标题
title_frame = tk.Frame(self.window, bg="#3498db", height=60)
title_frame.pack(fill="x")
tk.Label(
title_frame,
text=self.title,
font=("微软雅黑", 18, "bold"),
fg="white",
bg="#3498db"
).pack(pady=15)
# 单位信息
tk.Label(
title_frame,
text="XXXXX",
font=("微软雅黑", 12),
fg="white",
bg="#3498db"
).pack(side="left", padx=20)
# 日期
date_label = tk.Label(
title_frame,
text=f"打印日期: {datetime.now().strftime('%Y-%m-%d')}",
font=("微软雅黑", 10),
fg="white",
bg="#3498db"
)
date_label.pack(side="right", padx=20)
# 表格框架
table_frame = tk.Frame(self.window, bg="#f5f5f5", padx=20, pady=20)
table_frame.pack(fill="both", expand=True, padx=10, pady=10)
# 创建表格
self.tree = ttk.Treeview(
table_frame,
columns=self.headers,
show="headings",
height=20
)
# 设置列
for i, header in enumerate(self.headers):
self.tree.column(f"#{i+1}", width=150, anchor="center")
self.tree.heading(f"#{i+1}", text=header)
# 添加滚动条
scrollbar = ttk.Scrollbar(table_frame, orient="vertical", command=self.tree.yview)
self.tree.configure(yscrollcommand=scrollbar.set)
scrollbar.pack(side="right", fill="y")
self.tree.pack(fill="both", expand=True)
# 添加数据
for row in self.data:
self.tree.insert("", "end", values=row)
# 按钮区域
btn_frame = tk.Frame(self.window, bg="#f5f5f5", pady=10)
btn_frame.pack(fill="x")
ttk.Button(
btn_frame,
text="打印",
width=15,
command=self.print_data,
style="Accent.TButton"
).pack(side="left", padx=20)
ttk.Button(
btn_frame,
text="导出",
width=15,
command=self.export_data,
style="Accent.TButton"
).pack(side="left", padx=10)
ttk.Button(
btn_frame,
text="关闭",
width=15,
command=self.window.destroy
).pack(side="right", padx=20)
def center_window(self, width, height):
screen_width = self.window.winfo_screenwidth()
screen_height = self.window.winfo_screenheight()
x = (screen_width - width) // 2
y = (screen_height - height) // 2
self.window.geometry(f"{width}x{height}+{x}+{y}")
def print_data(self):
messagebox.showinfo("打印", "数据已发送到打印机!")
def export_data(self):
filename = filedialog.asksaveasfilename(
defaultextension=".xlsx",
filetypes=[("Excel 文件", "*.xlsx"), ("所有文件", "*.*")],
title="保存为Excel文件"
)
if not filename:
return
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "数据导出"
# 设置标题
ws.merge_cells('A1:H1')
title_cell = ws['A1']
title_cell.value = self.title
title_cell.font = Font(size=16, bold=True)
title_cell.alignment = Alignment(horizontal='center', vertical='center')
# 设置单位信息
ws['A2'] = "单位:XXXXX"
ws['H2'] = f"导出日期:{datetime.now().strftime('%Y-%m-%d')}"
# 添加表头
for col_idx, header in enumerate(self.headers, 1):
cell = ws.cell(row=3, column=col_idx, value=header)
cell.font = Font(bold=True)
cell.alignment = Alignment(horizontal='center')
# 添加数据
for row_idx, row_data in enumerate(self.data, 4):
for col_idx, cell_value in enumerate(row_data, 1):
ws.cell(row=row_idx, column=col_idx, value=cell_value)
# 设置列宽
for col_idx in range(1, len(self.headers) + 1):
col_letter = get_column_letter(col_idx)
ws.column_dimensions[col_letter].width = 20
# 添加边框
thin_border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
for row in ws.iter_rows(min_row=3, max_row=len(self.data)+3, min_col=1, max_col=len(self.headers)):
for cell in row:
cell.border = thin_border
try:
wb.save(filename)
messagebox.showinfo("导出成功", f"数据已成功导出到:\n{filename}")
except Exception as e:
messagebox.showerror("导出失败", f"导出过程中发生错误:\n{str(e)}")
class InventorySystem:
def __init__(self):
# 创建登录窗口
self.login_root = tk.Tk()
self.login_window = LoginWindow(self.login_root, self)
# 初始化主窗口(但先不创建)
self.main_root = None
# 启动登录窗口事件循环
self.login_root.mainloop()
def show_main_window(self):
"""显示主窗口"""
# 创建主窗口
self.main_root = tk.Tk()
self.main_root.title("XXXXX进销存管理系统")
self.main_root.geometry("1200x700")
self.main_root.configure(bg="#ecf0f1")
# 设置窗口居中
self.center_window(1200, 700)
# 设置样式
self.set_style()
# 创建数据库连接
self.conn = sqlite3.connect('inventory.db')
self.cursor = self.conn.cursor()
self.create_tables()
# 创建主界面
self.create_main_interface()
# 加载初始数据
self.load_inventory()
# 启动主窗口事件循环
self.main_root.mainloop()
def set_style(self):
style = ttk.Style()
style.theme_use("clam")
# 配置标签页样式
style.configure("TNotebook", background="#3498db")
style.configure("TNotebook.Tab",
background="#2c3e50",
foreground="white",
font=("微软雅黑", 10, "bold"),
padding=[10, 5])
style.map("TNotebook.Tab",
background=[("selected", "#3498db")],
foreground=[("selected", "white")])
# 配置按钮样式
style.configure("TButton", font=("微软雅黑", 10), padding=5)
style.configure("Accent.TButton",
foreground="white",
background="#3498db",
font=("微软雅黑", 10, "bold"),
borderwidth=0)
style.map("Accent.TButton",
background=[('active', '#2980b9'), ('pressed', '#1f618d')])
# 配置标签框架样式
style.configure("TLabelframe", background="#ecf0f1", borderwidth=2, relief="groove")
style.configure("TLabelframe.Label", background="#3498db", foreground="white", font=("微软雅黑", 10, "bold"))
# 配置表格样式
style.configure("Treeview",
background="#ffffff",
foreground="#2c3e50",
rowheight=25,
fieldbackground="#ffffff")
style.configure("Treeview.Heading",
background="#3498db",
foreground="white",
font=("微软雅黑", 10, "bold"))
style.map("Treeview", background=[("selected", "#3498db")])
# 配置输入框样式
style.configure("TEntry", padding=5)
def center_window(self, width, height):
if self.main_root:
screen_width = self.main_root.winfo_screenwidth()
screen_height = self.main_root.winfo_screenheight()
x = (screen_width - width) // 2
y = (screen_height - height) // 2
self.main_root.geometry(f"{width}x{height}+{x}+{y}")
def create_tables(self):
# 创建进货表
self.cursor.execute('''
CREATE TABLE IF NOT EXISTS purchase (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT,
product_name TEXT,
specification TEXT,
unit TEXT,
quantity INTEGER,
note TEXT
)
''')
# 创建出货表
self.cursor.execute('''
CREATE TABLE IF NOT EXISTS sale (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT,
product_name TEXT,
specification TEXT,
unit TEXT,
quantity INTEGER,
department TEXT,
user TEXT,
note TEXT
)
''')
# 创建库存表
self.cursor.execute('''
CREATE TABLE IF NOT EXISTS inventory (
id INTEGER PRIMARY KEY AUTOINCREMENT,
product_name TEXT,
specification TEXT,
unit TEXT,
quantity INTEGER
)
''')
self.()
def create_main_interface(self):
# 创建菜单栏
self.create_menu()
# 创建标签页
self.notebook = ttk.Notebook(self.main_root, style="TNotebook")
self.notebook.pack(fill='both', expand=True, padx=10, pady=10)
# 创建进货标签页
self.purchase_frame = ttk.Frame(self.notebook)
self.notebook.add(self.purchase_frame, text='进货管理')
self.create_purchase_tab()
# 创建出货标签页
self.sale_frame = ttk.Frame(self.notebook)
self.notebook.add(self.sale_frame, text='出货管理')
self.create_sale_tab()
# 创建库存标签页
self.inventory_frame = ttk.Frame(self.notebook)
self.notebook.add(self.inventory_frame, text='库存管理')
self.create_inventory_tab()
# 创建查询标签页
self.query_frame = ttk.Frame(self.notebook)
self.notebook.add(self.query_frame, text='查询与导出')
self.create_query_tab()
def create_menu(self):
menu_bar = tk.Menu(self.main_root, bg="#ecf0f1", fg="#2c3e50")
# 文件菜单
file_menu = tk.Menu(menu_bar, tearoff=0, bg="#ecf0f1", fg="#2c3e50")
file_menu.add_command(label="退出", command=self.main_root.destroy)
menu_bar.add_cascade(label="文件", menu=file_menu)
# 帮助菜单
help_menu = tk.Menu(menu_bar, tearoff=0, bg="#ecf0f1", fg="#2c3e50")
help_menu.add_command(label="关于", command=self.show_about)
menu_bar.add_cascade(label="帮助", menu=help_menu)
self.main_root.config(menu=menu_bar)
def show_about(self):
messagebox.showinfo(
"关于",
"XXXXX进销存管理系统\n\n"
"版本: 2.0\n"
"开发日期: 2025年\n"
"版权所有 © 2025 XXXXX"
)
def create_purchase_tab(self):
# 表单区域
form_frame = ttk.LabelFrame(self.purchase_frame, text="进货信息录入", style="TLabelframe")
form_frame.pack(fill='x', padx=10, pady=5)
# 日期选择
ttk.Label(form_frame, text="到货日期:").grid(row=0, column=0, padx=5, pady=5, sticky='e')
self.purchase_date = tk.StringVar()
self.purchase_date.set(datetime.now().strftime("%Y-%m-%d"))
date_entry = ttk.Entry(form_frame, textvariable=self.purchase_date, width=15, state='readonly', style="TEntry")
date_entry.grid(row=0, column=1, padx=5, pady=5, sticky='w')
ttk.Button(form_frame, text="选择日期", command=lambda: self.select_date(self.purchase_date)).grid(row=0, column=2, padx=5, pady=5)
# 品名
ttk.Label(form_frame, text="品名:").grid(row=0, column=3, padx=5, pady=5, sticky='e')
self.purchase_product = (form_frame, width=15, style="TCombobox")
self.purchase_product.grid(row=0, column=4, padx=5, pady=5, sticky='w')
self.purchase_product['values'] = self.get_product_list()
# 规格
ttk.Label(form_frame, text="规格:").grid(row=1, column=0, padx=5, pady=5, sticky='e')
self.purchase_spec = ttk.Entry(form_frame, width=15, style="TEntry")
self.purchase_spec.grid(row=1, column=1, padx=5, pady=5, sticky='w')
# 单位
ttk.Label(form_frame, text="单位:").grid(row=1, column=3, padx=5, pady=5, sticky='e')
self.purchase_unit = (form_frame, width=15, style="TCombobox")
self.purchase_unit.grid(row=1, column=4, padx=5, pady=5, sticky='w')
self.purchase_unit['values'] = ('个', '件', '箱', '千克', '米', '升')
# 数量
ttk.Label(form_frame, text="数量:").grid(row=2, column=0, padx=5, pady=5, sticky='e')
self.purchase_quantity = ttk.Entry(form_frame, width=15, style="TEntry")
self.purchase_quantity.grid(row=2, column=1, padx=5, pady=5, sticky='w')
# 备注
ttk.Label(form_frame, text="备注:").grid(row=2, column=3, padx=5, pady=5, sticky='e')
self.purchase_note = ttk.Entry(form_frame, width=15, style="TEntry")
self.purchase_note.grid(row=2, column=4, padx=5, pady=5, sticky='w')
# 按钮区域
button_frame = ttk.Frame(self.purchase_frame)
button_frame.pack(fill='x', padx=10, pady=5)
ttk.Button(button_frame, text="添加进货记录", command=self.add_purchase, style="Accent.TButton").pack(side='left', padx=5)
ttk.Button(button_frame, text="修改选中记录", command=self.edit_purchase_record, style="Accent.TButton").pack(side='left', padx=5)
# 表格区域
table_frame = ttk.LabelFrame(self.purchase_frame, text="进货记录", style="TLabelframe")
table_frame.pack(fill='both', expand=True, padx=10, pady=5)
columns = ("id", "date", "product", "spec", "unit", "quantity", "note")
self.purchase_tree = ttk.Treeview(
table_frame, columns=columns, show='headings', height=10, style="Treeview"
)
# 设置列宽
self.purchase_tree.column("id", width=50, anchor='center')
self.purchase_tree.column("date", width=100, anchor='center')
self.purchase_tree.column("product", width=150, anchor='center')
self.purchase_tree.column("spec", width=150, anchor='center')
self.purchase_tree.column("unit", width=50, anchor='center')
self.purchase_tree.column("quantity", width=80, anchor='center')
self.purchase_tree.column("note", width=200, anchor='center')
# 设置表头
self.purchase_tree.heading("id", text="ID")
self.purchase_tree.heading("date", text="到货日期")
self.purchase_tree.heading("product", text="品名")
self.purchase_tree.heading("spec", text="规格")
self.purchase_tree.heading("unit", text="单位")
self.purchase_tree.heading("quantity", text="数量")
self.purchase_tree.heading("note", text="备注")
# 添加滚动条
scrollbar = ttk.Scrollbar(table_frame, orient="vertical", command=self.purchase_tree.yview)
self.purchase_tree.configure(yscrollcommand=scrollbar.set)
scrollbar.pack(side="right", fill="y")
self.purchase_tree.pack(fill="both", expand=True)
# 删除按钮
delete_frame = ttk.Frame(table_frame)
delete_frame.pack(fill='x', pady=5)
ttk.Button(
delete_frame,
text="删除选中记录",
command=self.delete_purchase_record,
style="Accent.TButton"
).pack(side='right', padx=10)
# 加载数据
self.load_purchase_data()
def create_sale_tab(self):
# 表单区域
form_frame = ttk.LabelFrame(self.sale_frame, text="出货信息录入", style="TLabelframe")
form_frame.pack(fill='x', padx=10, pady=5)
# 日期选择
ttk.Label(form_frame, text="领取日期:").grid(row=0, column=0, padx=5, pady=5, sticky='e')
self.sale_date = tk.StringVar()
self.sale_date.set(datetime.now().strftime("%Y-%m-%d"))
date_entry = ttk.Entry(form_frame, textvariable=self.sale_date, width=15, state='readonly', style="TEntry")
date_entry.grid(row=0, column=1, padx=5, pady=5, sticky='w')
ttk.Button(form_frame, text="选择日期", command=lambda: self.select_date(self.sale_date)).grid(row=0, column=2, padx=5, pady=5)
# 选择进货记录按钮
ttk.Button(
form_frame,
text="选择进货记录",
command=self.select_purchase_record,
style="Accent.TButton"
).grid(row=0, column=3, padx=5, pady=5)
# 品名
ttk.Label(form_frame, text="品名:").grid(row=1, column=0, padx=5, pady=5, sticky='e')
self.sale_product = ttk.Entry(form_frame, width=15, state='readonly', style="TEntry")
self.sale_product.grid(row=1, column=1, padx=5, pady=5, sticky='w')
# 规格
ttk.Label(form_frame, text="规格:").grid(row=1, column=3, padx=5, pady=5, sticky='e')
self.sale_spec = ttk.Entry(form_frame, width=15, state='readonly', style="TEntry")
self.sale_spec.grid(row=1, column=4, padx=5, pady=5, sticky='w')
# 单位
ttk.Label(form_frame, text="单位:").grid(row=2, column=0, padx=5, pady=5, sticky='e')
self.sale_unit = ttk.Entry(form_frame, width=15, state='readonly', style="TEntry")
self.sale_unit.grid(row=2, column=1, padx=5, pady=5, sticky='w')
# 数量
ttk.Label(form_frame, text="数量:").grid(row=2, column=3, padx=5, pady=5, sticky='e')
self.sale_quantity = ttk.Entry(form_frame, width=15, style="TEntry")
self.sale_quantity.grid(row=2, column=4, padx=5, pady=5, sticky='w')
# 领用部门
ttk.Label(form_frame, text="领用部门:").grid(row=3, column=0, padx=5, pady=5, sticky='e')
self.sale_department = ttk.Entry(form_frame, width=15, style="TEntry")
self.sale_department.grid(row=3, column=1, padx=5, pady=5, sticky='w')
# 使用人
ttk.Label(form_frame, text="使用人:").grid(row=3, column=3, padx=5, pady=5, sticky='e')
self.sale_user = ttk.Entry(form_frame, width=15, style="TEntry")
self.sale_user.grid(row=3, column=4, padx=5, pady=5, sticky='w')
# 备注
ttk.Label(form_frame, text="备注:").grid(row=4, column=0, padx=5, pady=5, sticky='e')
self.sale_note = ttk.Entry(form_frame, width=15, style="TEntry")
self.sale_note.grid(row=4, column=1, padx=5, pady=5, sticky='w')
# 按钮区域
button_frame = ttk.Frame(self.sale_frame)
button_frame.pack(fill='x', padx=10, pady=5)
ttk.Button(button_frame, text="添加出货记录", command=self.add_sale, style="Accent.TButton").pack(side='left', padx=5)
ttk.Button(button_frame, text="修改选中记录", command=self.edit_sale_record, style="Accent.TButton").pack(side='left', padx=5)
# 表格区域
table_frame = ttk.LabelFrame(self.sale_frame, text="出货记录", style="TLabelframe")
table_frame.pack(fill='both', expand=True, padx=10, pady=5)
columns = ("id", "date", "product", "spec", "unit", "quantity", "department", "user", "note")
self.sale_tree = ttk.Treeview(
table_frame, columns=columns, show='headings', height=10, style="Treeview"
)