Python 写的 智慧记 进销存 辅助 程序 导入导出 excel 可打印

 图样:

 

就可以导入了

上代码

import tkinter as tk
from tkinter import ttk
import sqlite3
from datetime import datetime
from tkinter import messagebox, filedialog
import pandas as pd
import re

class OrderSystem:
    def __init__(self, root):
        self.root = root
        self.root.title("订单记录系统")
        
        # 创建数据库连接
        self.conn = sqlite3.connect('orders.db')
        self.create_table()
        
        # 创建界面
        self.create_ui()
        
        # 添加搜索框架
        self.create_search_frame()
        
        # 添加更多功能按钮
        self.add_function_buttons()
        
        # 加载所有订单数据
        self.load_all_orders()
        
        # 设置默认值
        self.set_default_values()
        
    def create_table(self):
        cursor = self.conn.cursor()
        cursor.execute('''
        CREATE TABLE IF NOT EXISTS orders (
            order_date TEXT,
            order_number TEXT,
            customer TEXT,
            product TEXT,
            unit TEXT,
            quantity REAL,
            price REAL,
            discount REAL,
            final_price REAL,
            total REAL,
            remarks TEXT,
            discount_amount REAL,
            discount_total REAL,
            delivery TEXT,
            payment_received REAL,
            end_customer TEXT,
            notes TEXT,
            business TEXT
        )
        ''')
        self.conn.commit()
        
    def create_ui(self):
        # 创建主框架来容纳左右两部分
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill="both", expand=True)
        
        # 创建左侧框架
        left_frame = ttk.Frame(main_frame)
        left_frame.pack(side="left", fill="both", expand=True)
        
        # 创建右侧框架
        right_frame = ttk.Frame(main_frame)
        right_frame.pack(side="right", fill="y", padx=5)
        
        # 创建输入框架(放在左侧)
        input_frame = ttk.LabelFrame(left_frame, text="订单信息")
        input_frame.pack(padx=5, pady=5, fill="x")
        
        # 修改订单分类框架
        order_group_frame = ttk.LabelFrame(right_frame, text="订单查询")
        order_group_frame.pack(padx=5, pady=5, fill="both", expand=True)
        
        # 添加筛选框
        filter_frame = ttk.Frame(order_group_frame)
        filter_frame.pack(fill="x", padx=5, pady=5)
        
        # 单据���号筛选
        ttk.Label(filter_frame, text="单据编号:").grid(row=0, column=0, padx=5)
        self.order_number_filter = ttk.Combobox(filter_frame, width=15)
        self.order_number_filter.grid(row=0, column=1, padx=5)
        
        # 客户名称筛选
        ttk.Label(filter_frame, text="客户名称:").grid(row=0, column=2, padx=5)
        self.customer_filter = ttk.Combobox(filter_frame, width=15)
        self.customer_filter.grid(row=0, column=3, padx=5)
        
        # 筛选按钮
        ttk.Button(filter_frame, text="筛选", command=self.filter_orders).grid(row=0, column=4, padx=5)
        ttk.Button(filter_frame, text="重置", command=self.reset_filter).grid(row=0, column=5, padx=5)
        ttk.Button(filter_frame, text="打印", command=self.print_filtered_data).grid(row=0, column=6, padx=5)
        
        # 绑定下拉框事件
        self.order_number_filter.bind('<KeyRelease>', self.update_order_number_list)
        self.customer_filter.bind('<KeyRelease>', self.update_customer_list)
        
        # 修改订单分类的树形视图列
        self.group_tree = ttk.Treeview(order_group_frame, columns=[
            "order_number", "customer", "product", "unit", 
            "quantity", "price", "total", "remarks"
        ], show="headings", height=15)
        
        # 设置列标题和宽度
        columns = [
            ("order_number", "单据编号", 100),
            ("customer", "客户名称", 100),
            ("product", "品名规格", 120),
            ("unit", "单位", 50),
            ("quantity", "数量", 60),
            ("price", "原价", 80),
            ("total", "金额", 80),
            ("remarks", "备注", 100)
        ]
        
        for col, heading, width in columns:
            self.group_tree.heading(col, text=heading)
            self.group_tree.column(col, width=width)
        
        self.group_tree.pack(padx=5, pady=5, fill="both", expand=True)
        
        # 添加滚动条
        group_scrollbar = ttk.Scrollbar(order_group_frame, orient="vertical", command=self.group_tree.yview)
        group_scrollbar.pack(side="right", fill="y")
        self.group_tree.configure(yscrollcommand=group_scrollbar.set)
        
        # 绑定点击事件
        self.group_tree.bind('<<TreeviewSelect>>', self.on_group_select)
        
        # 修改输入字段列表,确保与数据库字段完全匹配
        self.entries = {}
        fields = [
            ("order_date", "单据日期"), 
            ("order_number", "单据编号"),
            ("customer", "客户名称"), 
            ("product", "品名规格"),
            ("unit", "单位"),
            ("quantity", "数量"),
            ("price", "原价"), 
            ("discount", "单行折扣率(%)"),
            ("final_price", "折后价"), 
            ("total", "金额"),
            ("remarks", "备注"), 
            ("discount_amount", "整单折扣率(%)"),
            ("discount_total", "折后金额"), 
            ("delivery", "运费"),
            ("payment_received", "本单已收"), 
            ("end_customer", "结算账户"),
            ("notes", "说明"), 
            ("business", "营业员")
        ]
        
        for row, (field, label) in enumerate(fields):
            ttk.Label(input_frame, text=label).grid(row=row//2, column=(row%2)*2, padx=5, pady=2)
            self.entries[field] = ttk.Entry(input_frame)
            self.entries[field].grid(row=row//2, column=(row%2)*2+1, padx=5, pady=2, sticky="ew")
            
        # 添加按钮
        self.btn_frame = ttk.Frame(self.root)
        self.btn_frame.pack(pady=5)
        
        ttk.Button(self.btn_frame, text="保存", command=self.save_order).pack(side="left", padx=5)
        ttk.Button(self.btn_frame, text="清空", command=self.clear_fields).pack(side="left", padx=5)
        ttk.Button(self.btn_frame, text="导入Excel", command=self.import_from_excel).pack(side="left", padx=5)
        ttk.Button(self.btn_frame, text="导出模板", command=self.export_template).pack(side="left", padx=5)
        
        # 修改表格显示,显示所有列
        self.tree = ttk.Treeview(self.root, columns=[
            "order_date", "order_number", "customer", "product", "unit",
            "quantity", "price", "discount", "final_price", "total",
            "remarks", "discount_amount", "discount_total", "delivery",
            "payment_received", "end_customer", "notes", "business"
        ], show="headings")
        
        # 修改列标题定义,显示所有列
        columns = [
            ("order_date", "单据日期"), 
            ("order_number", "单据编号"),
            ("customer", "客户名称"), 
            ("product", "品名规格"),
            ("unit", "单位"),
            ("quantity", "数量"),
            ("price", "原价"), 
            ("discount", "单行折扣率(%)"),
            ("final_price", "折后价"), 
            ("total", "金额"),
            ("remarks", "备注"),
            ("discount_amount", "整单折扣率(%)"),
            ("discount_total", "折后金额"),
            ("delivery", "运费"),
            ("payment_received", "本单已收"),
            ("end_customer", "结算账户"),
            ("notes", "说明"),
            ("business", "营业员")
        ]
        
        for col, heading in columns:
            self.tree.heading(col, text=heading)
            self.tree.column(col, width=100)
            
        self.tree.pack(padx=5, pady=5, fill="both", expand=True)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(self.root, orient="vertical", command=self.tree.yview)
        scrollbar.pack(side="right", fill="y")
        self.tree.configure(yscrollcommand=scrollbar.set)
        
        # 添加自动计算绑定
        self.entries['quantity'].bind('<KeyRelease>', self.calculate_total)
        self.entries['price'].bind('<KeyRelease>', self.calculate_total)
        self.entries['discount'].bind('<KeyRelease>', self.calculate_total)
        
        # 在订单分类框架底部添加合计标签
        self.total_label = ttk.Label(order_group_frame, text="合计金额: ¥0.00")
        self.total_label.pack(pady=5)

    def calculate_total(self, event=None):
        """计算折后价和金额"""
        try:
            quantity = float(self.entries['quantity'].get() or 0)
            price = float(self.entries['price'].get() or 0)
            discount = float(self.entries['discount'].get() or 100)
            
            # 计算折后价
            final_price = price * discount / 100
            self.entries['final_price'].delete(0, tk.END)
            self.entries['final_price'].insert(0, f"{final_price:.2f}")
            
            # 计算金额
            total = quantity * final_price
            self.entries['total'].delete(0, tk.END)
            self.entries['total'].insert(0, f"{total:.2f}")
        except ValueError:
            pass

    def create_search_frame(self):
        search_frame = ttk.LabelFrame(self.root, text="搜索")
        search_frame.pack(padx=5, pady=5, fill="x")
        
        ttk.Label(search_frame, text="搜索条件:").pack(side="left", padx=5)
        self.search_entry = ttk.Entry(search_frame)
        self.search_entry.pack(side="left", padx=5, fill="x", expand=True)
        
        ttk.Button(search_frame, text="搜索", command=self.search_orders).pack(side="left", padx=5)
        
    def add_function_buttons(self):
        # 在原有btn_frame中添加更多按钮
        ttk.Button(self.btn_frame, text="编辑", command=self.edit_selected).pack(side="left", padx=5)
        ttk.Button(self.btn_frame, text="删除", command=self.delete_selected).pack(side="left", padx=5)
        ttk.Button(self.btn_frame, text="导出Excel", command=self.export_to_excel).pack(side="left", padx=5)
        ttk.Button(self.btn_frame, text="统计报表", command=self.show_statistics).pack(side="left", padx=5)

    def validate_data(self):
        """数据验证"""
        errors = []
        
        # 验证日期格式
        date = self.entries['order_date'].get().strip()
        if not date:
            errors.append("单据日期不能为空")
        elif not re.match(r'^\d{4}-\d{2}-\d{2}$', date):
            errors.append("单据日期格式错误,应为 YYYY-MM-DD")
        
        # 验证必填字段
        required_fields = {
            'order_number': '单据编号',
            'customer': '客户名称',
            'product': '品名规格',
            'unit': '单位',
            'quantity': '数量',
            'price': '原价'
        }
        
        for field, name in required_fields.items():
            value = self.entries[field].get().strip()
            if not value:
                errors.append(f"{name}不能为空")
        
        # 验证数字字段
        number_fields = {
            'quantity': '数量',
            'price': '原价',
            'discount': '单行折扣率(%)',
            'final_price': '折后价',
            'total': '金额',
            'discount_amount': '整单折扣率(%)',
            'discount_total': '折后金额',
            'payment_received': '本单已收'
        }
        
        for field, name in number_fields.items():
            value = self.entries[field].get().strip()
            if value:  # 如果有值才验证
                try:
                    num = float(value)
                    if field in ['quantity', 'price'] and num <= 0:
                        errors.append(f"{name}必须大于0")
                    elif num < 0:
                        errors.append(f"{name}不能为负数")
                except ValueError:
                    errors.append(f"{name}必须是数字")
        
        if errors:
            messagebox.showerror("验证错误", "\n".join(errors))
            return False
        return True

    def save_order(self):
        """保存订单数据"""
        if not self.validate_data():
            return
        
        try:
            # 获取所有输入值
            values = []
            fields_order = [
                'order_date', 'order_number', 'customer', 'product', 'unit',
                'quantity', 'price', 'discount', 'final_price', 'total',
                'remarks', 'discount_amount', 'discount_total', 'delivery',
                'payment_received', 'end_customer', 'notes', 'business'
            ]
            
            for field in fields_order:
                value = self.entries[field].get().strip()
                
                # 对数字字段进行转换
                if field in ['quantity', 'price', 'discount', 'final_price', 'total', 
                            'discount_amount', 'discount_total', 'payment_received']:
                    try:
                        value = float(value) if value else 0.0
                    except ValueError:
                        value = 0.0
                elif not value:  # 对非数字字段,如果为空则设为空字符串
                    value = ''
                
                values.append(value)
            
            # 检查单据编号是否重复
            cursor = self.conn.cursor()
            cursor.execute('SELECT COUNT(*) FROM orders WHERE order_number = ?', (values[1],))
            if cursor.fetchone()[0] > 0:
                if not messagebox.askyesno("警告", "单据编号已存在是否继续保存?"):
                    return
            
            # 插入数据
            try:
                cursor.execute('''
                INSERT INTO orders VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
                ''', values)
                self.conn.commit()
                
                # 更新表格显示
                self.tree.insert("", "end", values=values)
                
                # 清空输入框并设置默认值
                self.set_default_values()
                
                # 显示成功消息
                messagebox.showinfo("成功", "订单保存成功!")
                
            except sqlite3.Error as e:
                self.conn.rollback()
                messagebox.showerror("数据库错误", f"保存失败:{str(e)}")
                return
                
        except Exception as e:
            messagebox.showerror("错误", f"保存过程中出错:{str(e)}")
            return
        
        self.update_order_groups()

    def clear_fields(self):
        """清空所有输入框"""
        for field in self.entries:
            self.entries[field].delete(0, tk.END)

    def __del__(self):
        self.conn.close()

    def search_orders(self):
        search_text = self.search_entry.get().strip()
        if not search_text:
            self.load_all_orders()
            return
            
        cursor = self.conn.cursor()
        cursor.execute('''
        SELECT * FROM orders 
        WHERE order_date LIKE ? OR order_number LIKE ? OR customer LIKE ? OR product LIKE ?
        ''', [f'%{search_text}%'] * 4)
        
        self.tree.delete(*self.tree.get_children())
        for row in cursor.fetchall():
            self.tree.insert("", "end", values=row)

    def edit_selected(self):
        selected = self.tree.selection()
        if not selected:
            messagebox.showwarning("提示", "请先选择一条记录")
            return
            
        item = self.tree.item(selected[0])
        values = item['values']
        
        # 填充表单
        for field, value in zip(self.entries.keys(), values):
            self.entries[field].delete(0, tk.END)
            self.entries[field].insert(0, str(value))

    def delete_selected(self):
        selected = self.tree.selection()
        if not selected:
            messagebox.showwarning("提示", "请先选择一条记录")
            return
            
        if messagebox.askyesno("确认", "确定要删除中的记录吗?"):
            item = self.tree.item(selected[0])
            order_number = item['values'][1]
            
            cursor = self.conn.cursor()
            cursor.execute('DELETE FROM orders WHERE order_number = ?', (order_number,))
            self.conn.commit()
            
            self.tree.delete(selected[0])
        
        self.update_order_groups()

    def export_to_excel(self):
        """导出数据到Excel"""
        try:
            # 先获取保存路径
            filename = filedialog.asksaveasfilename(
                defaultextension=".xlsx",
                filetypes=[("Excel files", "*.xlsx")]
            )
            if not filename:
                return
            
            # 获取数据
            cursor = self.conn.cursor()
            cursor.execute('SELECT * FROM orders')
            data = cursor.fetchall()
            
            # 准备列名
            columns = [
                '单据日期', '单据编号', '客户名称', '品名规格',
                '单位', '数量', '原价', '单行折扣率(%)', '折后价', 
                '金额', '备注', '整单折扣率(%)', '折后金额', '运费',
                '本单已收', '结算账户', '说明', '营业员'
            ]
            
            # 创建DataFrame
            df = pd.DataFrame(data, columns=columns)
            
            # 直接导出
            df.to_excel(filename, index=False)
            messagebox.showinfo("成功", "数据已导出到Excel文件")
            
        except PermissionError:
            messagebox.showerror("错误", "无法保存文件,请确保:\n1. 文件未被其他程序打开\n2. 您有写入权限")
        except Exception as e:
            messagebox.showerror("错误", f"导出过程中出错:{str(e)}")

    def show_statistics(self):
        stats_window = tk.Toplevel(self.root)
        stats_window.title("统计报表")
        
        cursor = self.conn.cursor()
        
        # 客户统计
        cursor.execute('''
        SELECT customer, 
               COUNT(*) as order_count,
               SUM(total) as total_amount,
               SUM(payment_received) as total_received
        FROM orders 
        GROUP BY customer
        ''')
        
        # 创建统计表格
        tree = ttk.Treeview(stats_window, columns=["customer", "count", "amount", "received"], show="headings")
        tree.heading("customer", text="客户")
        tree.heading("count", text="订单数")
        tree.heading("amount", text="总金额")
        tree.heading("received", text="已收金额")
        
        for row in cursor.fetchall():
            tree.insert("", "end", values=row)
            
        tree.pack(padx=5, pady=5, fill="both", expand=True)

    def load_all_orders(self):
        """加载所有订单到表格"""
        cursor = self.conn.cursor()
        cursor.execute('SELECT * FROM orders')
        
        self.tree.delete(*self.tree.get_children())
        for row in cursor.fetchall():
            self.tree.insert("", "end", values=row)
        
        # 更新筛选下拉列表
        self.update_filter_lists()
        self.update_order_groups()

    def import_from_excel(self):
        """从Excel文件导入数据"""
        filename = filedialog.askopenfilename(
            filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")]
        )
        if not filename:
            return
        
        try:
            # 读取Excel文件
            df = pd.read_excel(filename)
            
            # 数字字段列表
            numeric_columns = [
                '数量', '原价', '单行折扣率(%)', '折后价', '金额',
                '整单折扣率(%)', '折后金额', '运费', '本单已收'
            ]
            
            # 转换数字列的数据类型
            for col in numeric_columns:
                if col in df.columns:
                    df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0)
            
            # 检查必需的列是否存在
            required_columns = [
                '单据日期', '单据编号', '客户名称', '品名规格', '单位', 
                '数量', '原价', '单行折扣率(%)', '折后价', '金额',
                '备注', '整单折扣率(%)', '折后金额', '运费',
                '本单已收', '结算账户', '说明', '营业员'
            ]
            
            missing_columns = [col for col in required_columns if col not in df.columns]
            if missing_columns:
                messagebox.showerror("错误", f"Excel文件缺少以下列:\n{', '.join(missing_columns)}")
                return
            
            # 创建预览窗口
            preview_window = tk.Toplevel(self.root)
            preview_window.title("导入数据预览")
            preview_window.geometry("800x600")
            
            # 创建预览表格
            preview_tree = ttk.Treeview(preview_window, columns=required_columns[:10], show="headings")
            
            # 设置列标题
            for col in required_columns[:10]:
                preview_tree.heading(col, text=col)
                preview_tree.column(col, width=100)
            
            # 添加数据到预��表格
            for _, row in df.iterrows():
                values = [row[col] for col in required_columns[:10]]
                preview_tree.insert("", "end", values=values)
            
            # 添加滚动条
            scrollbar = ttk.Scrollbar(preview_window, orient="vertical", command=preview_tree.yview)
            scrollbar.pack(side="right", fill="y")
            preview_tree.configure(yscrollcommand=scrollbar.set)
            preview_tree.pack(padx=5, pady=5, fill="both", expand=True)
            
            # 添加按钮框
            btn_frame = ttk.Frame(preview_window)
            btn_frame.pack(pady=5)
            
            def confirm_import():
                try:
                    # 将数据插入数据库
                    cursor = self.conn.cursor()
                    
                    # 定义字段映射
                    field_mapping = {
                        '单据日期': 'order_date',
                        '单据编号': 'order_number',
                        '客户名称': 'customer',
                        '品名规格': 'product',
                        '单位': 'unit',
                        '数量': 'quantity',
                        '原价': 'price',
                        '单行折扣率(%)': 'discount',
                        '折后价': 'final_price',
                        '金额': 'total',
                        '备注': 'remarks',
                        '整单折扣率(%)': 'discount_amount',
                        '折后金额': 'discount_total',
                        '运费': 'delivery',
                        '本单已收': 'payment_received',
                        '结算账户': 'end_customer',
                        '说明': 'notes',
                        '营业员': 'business'
                    }
                    
                    # 获取数据字段顺序
                    db_fields = [
                        'order_date', 'order_number', 'customer', 'product', 'unit',
                        'quantity', 'price', 'discount', 'final_price', 'total',
                        'remarks', 'discount_amount', 'discount_total', 'delivery',
                        'payment_received', 'end_customer', 'notes', 'business'
                    ]
                    
                    for _, row in df.iterrows():
                        values = []
                        for field in db_fields:
                            # 从Excel列名映射到数据库字段
                            excel_col = [k for k, v in field_mapping.items() if v == field][0]
                            value = row[excel_col]
                            
                            # 处理数值
                            if pd.isna(value):
                                value = 0 if field in ['quantity', 'price', 'discount', 'final_price', 
                                                     'total', 'discount_amount', 'discount_total', 
                                                     'payment_received'] else ''
                            elif isinstance(value, (int, float)):
                                value = float(value)
                            else:
                                value = str(value)
                            values.append(value)
                        
                        try:
                            cursor.execute('''
                            INSERT INTO orders VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
                            ''', values)
                        except sqlite3.Error as e:
                            self.conn.rollback()
                            messagebox.showerror("错误", f"插入数据时出错:{str(e)}\n行数据{values}")
                            return
                    
                    self.conn.commit()
                    self.load_all_orders()
                    messagebox.showinfo("成功", "数据导入成功!")
                    preview_window.destroy()
                    
                except Exception as e:
                    self.conn.rollback()
                    messagebox.showerror("错误", f"导入过程中出错:{str(e)}")
            
            def cancel_import():
                preview_window.destroy()
            
            # 添加确认和取消按钮
            ttk.Button(btn_frame, text="确认导入", command=confirm_import).pack(side="left", padx=5)
            ttk.Button(btn_frame, text="取消", command=cancel_import).pack(side="left", padx=5)
            
            # 显示导入数据的总数
            ttk.Label(preview_window, text=f"共 {len(df)} 条数据").pack(pady=5)
            
        except Exception as e:
            messagebox.showerror("错误", f"导入过程中出错:{str(e)}")
        
        self.update_order_groups()

    def export_template(self):
        """导出Excel模板"""
        filename = filedialog.asksaveasfilename(
            defaultextension=".xlsx",
            filetypes=[("Excel files", "*.xlsx")],
            initialfile="订单导出模板.xlsx"
        )
        if not filename:
            return
        
        try:
            # 创建示例数据 - 使用相同的列名
            sample_data = {
                '单据日期': ['2024-01-01'],
                '单据编号': ['XSD202401001'],
                '客户名称': ['示例客户'],
                '品名规格': ['示例产品'],
                '单位': ['个'],
                '数量': [1],
                '原价': [100],
                '单行折扣率(%)': [100],
                '折后价': [100],
                '金额': [100],
                '备注': ['备注示例'],
                '整单折扣率(%)': [0],
                '折后金额': [100],
                '运费': [0],
                '本单已收': [0],
                '结算账户': ['结算账户示例'],
                '说明': ['说明示例'],
                '营业员': ['营业员示例']
            }
            
            # 创建DataFrame
            df = pd.DataFrame(sample_data)
            
            # 创建Excel写入器
            with pd.ExcelWriter(filename, engine='openpyxl') as writer:
                # 写入数据
                df.to_excel(writer, index=False, sheet_name='订单数据')
                
                # 获取工作表
                worksheet = writer.sheets['订单数据']
                
                # 设置列宽
                for column in worksheet.columns:
                    max_length = 0
                    column = [cell for cell in column]
                    for cell in column:
                        try:
                            if len(str(cell.value)) > max_length:
                                max_length = len(str(cell.value))
                        except:
                            pass
                    adjusted_width = (max_length + 2)
                    worksheet.column_dimensions[column[0].column_letter].width = adjusted_width
                
                # 设置样式
                from openpyxl.styles import PatternFill, Font, Alignment, Border, Side
                
                # 定义样式
                header_fill = PatternFill(start_color='CCE5FF', end_color='CCE5FF', fill_type='solid')
                header_font = Font(bold=True)
                center_aligned = Alignment(horizontal='center', vertical='center')
                border = Border(
                    left=Side(style='thin'),
                    right=Side(style='thin'),
                    top=Side(style='thin'),
                    bottom=Side(style='thin')
                )
                
                # 应用表头样式
                for cell in worksheet[1]:
                    cell.fill = header_fill
                    cell.font = header_font
                    cell.alignment = center_aligned
                    cell.border = border
                
                # 应用数据行样式
                for row in worksheet.iter_rows(min_row=2):
                    for cell in row:
                        cell.alignment = center_aligned
                        cell.border = border
            
            messagebox.showinfo("成功", "模板导出成功!\n请按照模板格式准备数据后再进行导。")
            
        except Exception as e:
            messagebox.showerror("错误", f"导出模板时出错:{str(e)}")

    def set_default_values(self):
        """设置默认值"""
        # 清空所有输入框
        self.clear_fields()
        
        # 只设置日期和折扣率的默认值
        today = datetime.now().strftime('%Y-%m-%d')
        self.entries['order_date'].insert(0, today)  # 默认日期为今天
        self.entries['discount'].insert(0, '100')    # 默认折扣率为100%
        
        # 生成新的单据编号
        cursor = self.conn.cursor()
        cursor.execute('''
        SELECT MAX(order_number) FROM orders 
        WHERE order_number LIKE ?
        ''', [f'XSD{today.replace("-", "")}%'])
        
        last_number = cursor.fetchone()[0]
        if last_number:
            try:
                # 从最后一个单号提取序号并加1
                seq = int(last_number[-3:]) + 1
                new_number = f'XSD{today.replace("-", "")}{seq:03d}'
            except ValueError:
                new_number = f'XSD{today.replace("-", "")}001'
        else:
            new_number = f'XSD{today.replace("-", "")}001'
        
        self.entries['order_number'].insert(0, new_number)  # 设置新单据编号

    def update_order_groups(self):
        """更新订单分类显示"""
        cursor = self.conn.cursor()
        cursor.execute('''
        SELECT order_number, customer, product, unit, 
               quantity, price, total, remarks,
               SUM(total) OVER () as total_sum
        FROM orders 
        ORDER BY order_number DESC
        ''')
        
        # 清空现有数据
        self.group_tree.delete(*self.group_tree.get_children())
        
        total_sum = 0
        # 插入新数据
        for row in cursor.fetchall():
            formatted_row = list(row[:8])  # 只取前8列显示
            # 格式化数字列
            formatted_row[4] = f"{row[4]:.2f}"  # 数量
            formatted_row[5] = f"¥{row[5]:.2f}"  # 原价
            formatted_row[6] = f"¥{row[6]:.2f}"  # 金额
            self.group_tree.insert("", "end", values=formatted_row)
            total_sum = row[8]  # 获取合计金额
        
        # 更新合计标签
        self.total_label.config(text=f"合计金额: ¥{total_sum:,.2f}")

    def on_group_select(self, event):
        """当选择订单分类时的处理"""
        selected = self.group_tree.selection()
        if not selected:
            return
        
        # 获取选中的单据编号
        order_number = self.group_tree.item(selected[0])['values'][0]
        
        # 在主表格中查找并选中对应的记录
        for item in self.tree.get_children():
            if self.tree.item(item)['values'][1] == order_number:  # 假设单据编号是第二列
                self.tree.selection_set(item)
                self.tree.see(item)  # 确保选中的项可见
                break

    def filter_orders(self):
        """根据筛选条件过滤订单"""
        order_number = self.order_number_filter.get().strip()
        customer = self.customer_filter.get().strip()
        
        cursor = self.conn.cursor()
        
        # 构建查询条件
        query = '''
        SELECT order_number, customer, product, unit, 
               quantity, price, total, remarks,
               SUM(total) OVER () as total_sum
        FROM orders 
        WHERE 1=1
        '''
        params = []
        
        if order_number:
            query += " AND order_number LIKE ?"
            params.append(f"%{order_number}%")
        
        if customer:
            query += " AND customer LIKE ?"
            params.append(f"%{customer}%")
        
        query += " ORDER BY order_number DESC"
        
        cursor.execute(query, params)
        
        # 清空现有数据
        self.group_tree.delete(*self.group_tree.get_children())
        
        total_sum = 0
        # 插入新数据
        for row in cursor.fetchall():
            formatted_row = list(row[:8])  # 只取前8列显示
            # 格式化数字列
            formatted_row[4] = f"{row[4]:.2f}"  # 数量
            formatted_row[5] = f"¥{row[5]:.2f}"  # 原价
            formatted_row[6] = f"¥{row[6]:.2f}"  # 金额
            self.group_tree.insert("", "end", values=formatted_row)
            total_sum = row[8]  # 获取合计金额
        
        # 更新合计标签
        self.total_label.config(text=f"合计金额: ¥{total_sum:,.2f}")

    def reset_filter(self):
        """重置筛选条件"""
        self.order_number_filter.delete(0, tk.END)
        self.customer_filter.delete(0, tk.END)
        self.update_order_groups()

    def update_order_number_list(self, event=None):
        """更新单据编号下拉列表"""
        search_text = self.order_number_filter.get().strip()
        cursor = self.conn.cursor()
        
        if search_text:
            cursor.execute('''
            SELECT DISTINCT order_number FROM orders 
            WHERE order_number LIKE ? 
            ORDER BY order_number DESC
            ''', [f'%{search_text}%'])
        else:
            cursor.execute('''
            SELECT DISTINCT order_number FROM orders 
            ORDER BY order_number DESC
            ''')
        
        order_numbers = [row[0] for row in cursor.fetchall()]
        if order_numbers:
            self.order_number_filter['values'] = order_numbers
            if search_text:
                self.order_number_filter.event_generate('<Down>')

    def update_customer_list(self, event=None):
        """更新客户名称下拉列表"""
        search_text = self.customer_filter.get().strip()
        cursor = self.conn.cursor()
        
        if search_text:
            cursor.execute('''
            SELECT DISTINCT customer FROM orders 
            WHERE customer LIKE ? 
            ORDER BY customer
            ''', [f'%{search_text}%'])
        else:
            cursor.execute('''
            SELECT DISTINCT customer FROM orders 
            ORDER BY customer
            ''')
        
        customers = [row[0] for row in cursor.fetchall()]
        if customers:
            self.customer_filter['values'] = customers
            if search_text:
                self.customer_filter.event_generate('<Down>')

    def update_filter_lists(self):
        """更新所有筛选下拉列表"""
        cursor = self.conn.cursor()
        
        # 更新单据编号列表
        cursor.execute('SELECT DISTINCT order_number FROM orders ORDER BY order_number DESC')
        self.order_number_filter['values'] = [row[0] for row in cursor.fetchall()]
        
        # 更新客户名称列表
        cursor.execute('SELECT DISTINCT customer FROM orders ORDER BY customer')
        self.customer_filter['values'] = [row[0] for row in cursor.fetchall()]

    def print_filtered_data(self):
        """打印筛选后的数据"""
        try:
            # 获取当前筛选条件下的数据
            order_number = self.order_number_filter.get().strip()
            customer = self.customer_filter.get().strip()
            
            # 构建查询条件
            query = '''
            SELECT order_number, customer, product, unit, 
                   quantity, price, total, remarks,
                   SUM(total) OVER () as total_sum
            FROM orders 
            WHERE 1=1
            '''
            params = []
            
            if order_number:
                query += " AND order_number LIKE ?"
                params.append(f"%{order_number}%")
            
            if customer:
                query += " AND customer LIKE ?"
                params.append(f"%{customer}%")
            
            query += " ORDER BY order_number DESC"
            
            cursor = self.conn.cursor()
            cursor.execute(query, params)
            rows = cursor.fetchall()
            
            if not rows:
                messagebox.showinfo("提示", "没有数据可打印")
                return
            
            # 生成HTML内容
            html_content = f"""
            <!DOCTYPE html>
            <html>
            <head>
                <meta charset="utf-8">
                <title>订单查询结果</title>
                <style>
                    body {{ font-family: SimSun, serif; }}
                    table {{ border-collapse: collapse; width: 100%; margin-top: 10px; }}
                    th, td {{ 
                        border: 1px solid black; 
                        padding: 8px; 
                        text-align: center; 
                    }}
                    th {{ background-color: #f2f2f2; }}
                    .total {{ 
                        text-align: right;
                        padding: 10px;
                        font-weight: bold;
                    }}
                    .header-info {{
                        margin: 10px 0;
                        padding: 10px;
                        border: 1px solid #ddd;
                        background-color: #f9f9f9;
                    }}
                    .header-info p {{
                        margin: 5px 0;
                    }}
                    @media print {{
                        .no-print {{ display: none; }}
                        body {{ margin: 0; }}
                        table {{ page-break-inside: auto; }}
                        tr {{ page-break-inside: avoid; }}
                    }}
                </style>
            </head>
            <body>
                <h2 style="text-align: center;">订单查询结果</h2>
                <div class="header-info">
                    <p><strong>单据编号:</strong>{order_number if order_number else "全部"}</p>
                    <p><strong>客户名称:</strong>{customer if customer else "全部"}</p>
                    <p><strong>打印时间:</strong>{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
                </div>
                <table>
                    <tr>
                        <th>品名规格</th>
                        <th>单位</th>
                        <th>数量</th>
                        <th>原价</th>
                        <th>金额</th>
                        <th>备注</th>
                    </tr>
            """
            
            # 添加数据行
            for row in rows:
                html_content += f"""
                    <tr>
                        <td>{row[2]}</td>
                        <td>{row[3]}</td>
                        <td>{row[4]:.2f}</td>
                        <td>¥{row[5]:.2f}</td>
                        <td>¥{row[6]:.2f}</td>
                        <td>{row[7]}</td>
                    </tr>
                """
            
            # 添加合计行
            total_sum = rows[0][8] if rows else 0
            html_content += f"""
                </table>
                <div class="total">
                    合计金额: ¥{total_sum:,.2f}
                </div>
                <div class="no-print" style="margin-top: 20px; text-align: center;">
                    <button onclick="window.print()">打印</button>
                </div>
            </body>
            </html>
            """
            
            # 保存HTML文件
            temp_file = "订单查询结果.html"
            with open(temp_file, "w", encoding="utf-8") as f:
                f.write(html_content)
            
            # 在默认浏览器中打开HTML文件
            import webbrowser
            webbrowser.open(temp_file)
            
        except Exception as e:
            messagebox.showerror("错误", f"打印过程中出错:{str(e)}")

if __name__ == "__main__":
    root = tk.Tk()
    app = OrderSystem(root)
    root.mainloop()

 程序简要说明

这是一个订单管理系统,主要功能如下:

1. 基本功能:
   - 订单录入和保存
   - 订单查询和筛选
   - 数据导入导出(Excel格式)
   - 打印功能(HTML格式)

2. 界面布局:
   - 左侧:订单信息录入表单
   - 中间:订单数据列表
   - 右侧:订单查询面板(带筛选和统计)

3. 主要特点:
   - 自动生成单据编号(格式:XSD + 日期 + 3位序号)
   - 支持数字字段自动计算(数量、单价、折扣等)
   - 提供下拉选择和手动输入的组合筛选
   - 实时显示筛选结果的合计金额

4. 数据管理:
   - 使用SQLite数据库存储数据
   - 支持Excel导入导出
   - 提供数据验证和错误处理

5. 使用方法:
   ```python
   if __name__ == "__main__":
       root = tk.Tk()
       app = OrderSystem(root)
       root.mainloop()
   ```

6. 依赖库:
   - tkinter:GUI界面
   - sqlite3:数据库操作
   - pandas:Excel处理
   - datetime:日期处理
   - webbrowser:打印功能

这个程序适合小型企业或个人用于日常订单管理和记录。
 


1.1 1.1 版本

添加以下功能:

1. 程序启动时需要输入密码才能进入  

2. 默认密码为 "admin"  

3. 可以通过界面修改登录密码  

4. 数据库使用相同的密码进行加密保护

5. 密码以哈希形式存储在 config.json 文件中  (放入 db目录里)

使用说明:

1. 首次运行时,默认密码为 "admin"  

2. 登录后可以点击"修改密码"按钮更改密码

3. 新密码会同时用于程序登录和数据库加密

4. 如果忘记密码,需要删除 config.json 文件,程序会重置为默认密码  

注意事项:

1. 修改密码后,请务必记住新密码,因为它同时用于程序登录和数据库加密  

2. 建议定期备份数据库文件(数据库也放在 db目录里)

login.py

import tkinter as tk
from tkinter import ttk, messagebox
import hashlib
import json
import os

class LoginWindow:
    def __init__(self):
        self.window = tk.Tk()
        self.window.title("登录")
        self.window.geometry("300x150")
        self.login_success = False  # 添加登录状态标志
        
        # 居中显示
        self.window.update_idletasks()
        width = self.window.winfo_width()
        height = self.window.winfo_height()
        x = (self.window.winfo_screenwidth() // 2) - (width // 2)
        y = (self.window.winfo_screenheight() // 2) - (height // 2)
        self.window.geometry(f"{width}x{height}+{x}+{y}")
        
        # 创建登录框架
        frame = ttk.Frame(self.window, padding="20")
        frame.pack(fill="both", expand=True)
        
        ttk.Label(frame, text="请输入密码:").pack(pady=5)
        
        self.password_var = tk.StringVar()
        self.password_entry = ttk.Entry(frame, show="*", textvariable=self.password_var)
        self.password_entry.pack(pady=5, fill="x")
        
        btn_frame = ttk.Frame(frame)
        btn_frame.pack(pady=10)
        
        ttk.Button(btn_frame, text="登录", command=self.login).pack(side="left", padx=5)
        ttk.Button(btn_frame, text="修改密码", command=self.change_password).pack(side="left", padx=5)
        
        self.password_entry.bind('<Return>', lambda e: self.login())
        self.password_entry.focus()
        
        self.load_password()
        
    def load_password(self):
        """加载保存的密码哈希"""
        try:
            if os.path.exists('db/config.json'):
                with open('db/config.json', 'r') as f:
                    config = json.load(f)
                    self.password_hash = config.get('password_hash', self.hash_password('admin'))
            else:
                # 默认密码为 'admin'
                self.password_hash = self.hash_password('admin')
                self.save_password()
        except Exception:
            self.password_hash = self.hash_password('admin')
            
    def save_password(self):
        """保存密码哈希到配置文件"""
        config = {'password_hash': self.password_hash}
        with open('db/config.json', 'w') as f:
            json.dump(config, f)
            
    def hash_password(self, password):
        """对密码进行哈希处理"""
        return hashlib.sha256(password.encode()).hexdigest()
        
    def login(self):
        """验证密码"""
        if self.hash_password(self.password_var.get()) == self.password_hash:
            self.login_success = True  # 设置登录成功标志
            self.window.destroy()
            return True
        else:
            messagebox.showerror("错误", "密码错误!")
            self.password_var.set("")
            return False
            
    def change_password(self):
        """修改密码"""
        change_window = tk.Toplevel(self.window)
        change_window.title("修改密码")
        change_window.geometry("300x400")
        
        # 居中显示
        change_window.update_idletasks()
        width = change_window.winfo_width()
        height = change_window.winfo_height()
        x = (change_window.winfo_screenwidth() // 2) - (width // 2)
        y = (change_window.winfo_screenheight() // 2) - (height // 2)
        change_window.geometry(f"{width}x{height}+{x}+{y}")
        
        frame = ttk.Frame(change_window, padding="20")
        frame.pack(fill="both", expand=True)
        
        # 当前密码
        ttk.Label(frame, text="当前密码:").pack(pady=5)
        current_password = ttk.Entry(frame, show="*")
        current_password.pack(pady=5, fill="x")
        
        # 新密码
        ttk.Label(frame, text="新密码:").pack(pady=5)
        new_password = ttk.Entry(frame, show="*")
        new_password.pack(pady=5, fill="x")
        
        # 确认新密码
        ttk.Label(frame, text="确认新密码:").pack(pady=5)
        confirm_password = ttk.Entry(frame, show="*")
        confirm_password.pack(pady=5, fill="x")
        
        def do_change():
            if self.hash_password(current_password.get()) != self.password_hash:
                messagebox.showerror("错误", "当前密码错误!")
                return
                
            if new_password.get() != confirm_password.get():
                messagebox.showerror("错误", "两次输入的新密码不一致!")
                return
                
            if not new_password.get():
                messagebox.showerror("错误", "新密码不能为空!")
                return
                
            self.password_hash = self.hash_password(new_password.get())
            self.save_password()
            messagebox.showinfo("成功", "密码修改成功!")
            change_window.destroy()
            
        ttk.Button(frame, text="确认修改", command=do_change).pack(pady=10)
        
    def run(self):
        """运行登录窗口"""
        self.window.protocol("WM_DELETE_WINDOW", self.on_closing)  # 添加窗口关闭事件处理
        self.window.mainloop()
        return self.login_success  # 返回登录状态
        
    def on_closing(self):
        """窗口关闭事件处理"""
        if messagebox.askokcancel("退出", "确定要退出程序吗?"):
            self.login_success = False
            self.window.destroy() 

 主程序.py

import tkinter as tk
from tkinter import ttk
import sqlite3
from datetime import datetime
from tkinter import messagebox, filedialog
import pandas as pd
import re
import json
import sys
from login import LoginWindow

class OrderSystem:
    def __init__(self, root):
        self.root = root
        self.root.title("订单记录系统")
        
        # 创建数据库连接
        self.db_password = self.get_db_password()  # 获取数据库密码
        self.conn = self.create_db_connection()
        self.create_table()
        
        # 创建界面
        self.create_ui()
        
        # 添加搜索框架
        self.create_search_frame()
        
        # 添加更多功能按钮
        self.add_function_buttons()
        
        # 加载所有订单数据
        self.load_all_orders()
        
        # 设置默认值
        self.set_default_values()
        
    def get_db_password(self):
        """从配置文件获取数据库密码"""
        try:
            with open('db/config.json', 'r') as f:
                config = json.load(f)
                return config.get('password_hash', '')
        except Exception:
            return ''

    def create_db_connection(self):
        """创建加密的数据库连接"""
        try:
            conn = sqlite3.connect('orders.db')
            # 设置数据库密码
            conn.execute(f"PRAGMA key = '{self.db_password}'")
            return conn
        except sqlite3.Error as e:
            messagebox.showerror("错误", f"数据库连接失败:{str(e)}")
            sys.exit(1)
        
    def create_table(self):
        cursor = self.conn.cursor()
        cursor.execute('''
        CREATE TABLE IF NOT EXISTS orders (
            order_date TEXT,
            order_number TEXT,
            customer TEXT,
            product TEXT,
            unit TEXT,
            quantity REAL,
            price REAL,
            discount REAL,
            final_price REAL,
            total REAL,
            remarks TEXT,
            discount_amount REAL,
            discount_total REAL,
            delivery TEXT,
            payment_received REAL,
            end_customer TEXT,
            notes TEXT,
            business TEXT
        )
        ''')
        self.conn.commit()
        
    def create_ui(self):
        # 创建主框架来容纳左右两部分
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill="both", expand=True)
        
        # 创建左侧框架
        left_frame = ttk.Frame(main_frame)
        left_frame.pack(side="left", fill="both", expand=True)
        
        # 创建右侧框架
        right_frame = ttk.Frame(main_frame)
        right_frame.pack(side="right", fill="y", padx=5)
        
        # 创建输入框架(放在左侧)
        input_frame = ttk.LabelFrame(left_frame, text="订单信息")
        input_frame.pack(padx=5, pady=5, fill="x")
        
        # 修改订单分类框架
        order_group_frame = ttk.LabelFrame(right_frame, text="订单查询")
        order_group_frame.pack(padx=5, pady=5, fill="both", expand=True)
        
        # 添加筛选框
        filter_frame = ttk.Frame(order_group_frame)
        filter_frame.pack(fill="x", padx=5, pady=5)
        
        # 单据编号筛选
        ttk.Label(filter_frame, text="单据编号:").grid(row=0, column=0, padx=5)
        self.order_number_filter = ttk.Combobox(filter_frame, width=15)
        self.order_number_filter.grid(row=0, column=1, padx=5)
        
        # 客户名称筛选
        ttk.Label(filter_frame, text="客户名称:").grid(row=0, column=2, padx=5)
        self.customer_filter = ttk.Combobox(filter_frame, width=15)
        self.customer_filter.grid(row=0, column=3, padx=5)
        
        # 筛选按钮
        ttk.Button(filter_frame, text="筛选", command=self.filter_orders).grid(row=0, column=4, padx=5)
        ttk.Button(filter_frame, text="重置", command=self.reset_filter).grid(row=0, column=5, padx=5)
        ttk.Button(filter_frame, text="打印", command=self.print_filtered_data).grid(row=0, column=6, padx=5)
        
        # 绑定下拉框事件
        self.order_number_filter.bind('<KeyRelease>', self.update_order_number_list)
        self.customer_filter.bind('<KeyRelease>', self.update_customer_list)
        
        # 修改订单分类的树形视图列
        self.group_tree = ttk.Treeview(order_group_frame, columns=[
            "order_number", "customer", "product", "unit", 
            "quantity", "price", "total", "remarks"
        ], show="headings", height=15)
        
        # 设置列标题和宽度
        columns = [
            ("order_number", "单据编号", 100),
            ("customer", "客户名称", 100),
            ("product", "品名规格", 120),
            ("unit", "单位", 50),
            ("quantity", "数量", 60),
            ("price", "原价", 80),
            ("total", "金额", 80),
            ("remarks", "备注", 100)
        ]
        
        for col, heading, width in columns:
            self.group_tree.heading(col, text=heading)
            self.group_tree.column(col, width=width)
        
        self.group_tree.pack(padx=5, pady=5, fill="both", expand=True)
        
        # 添加滚动条
        group_scrollbar = ttk.Scrollbar(order_group_frame, orient="vertical", command=self.group_tree.yview)
        group_scrollbar.pack(side="right", fill="y")
        self.group_tree.configure(yscrollcommand=group_scrollbar.set)
        
        # 绑定点击事件
        self.group_tree.bind('<<TreeviewSelect>>', self.on_group_select)
        
        # 修改输入字段列表,确保与数据库字段完全匹配
        self.entries = {}
        fields = [
            ("order_date", "单据日期"), 
            ("order_number", "单据编号"),
            ("customer", "客户名称"), 
            ("product", "品名规格"),
            ("unit", "单位"),
            ("quantity", "数量"),
            ("price", "原价"), 
            ("discount", "单行折扣率(%)"),
            ("final_price", "折后价"), 
            ("total", "金额"),
            ("remarks", "备注"), 
            ("discount_amount", "整单折扣率(%)"),
            ("discount_total", "折后金额"), 
            ("delivery", "运费"),
            ("payment_received", "本单已收"), 
            ("end_customer", "结算账户"),
            ("notes", "说明"), 
            ("business", "营业员")
        ]
        
        for row, (field, label) in enumerate(fields):
            ttk.Label(input_frame, text=label).grid(row=row//2, column=(row%2)*2, padx=5, pady=2)
            self.entries[field] = ttk.Entry(input_frame)
            self.entries[field].grid(row=row//2, column=(row%2)*2+1, padx=5, pady=2, sticky="ew")
            
        # 添加按钮
        self.btn_frame = ttk.Frame(self.root)
        self.btn_frame.pack(pady=5)
        
        ttk.Button(self.btn_frame, text="保存", command=self.save_order).pack(side="left", padx=5)
        ttk.Button(self.btn_frame, text="清空", command=self.clear_fields).pack(side="left", padx=5)
        ttk.Button(self.btn_frame, text="导入Excel", command=self.import_from_excel).pack(side="left", padx=5)
        ttk.Button(self.btn_frame, text="导出模板", command=self.export_template).pack(side="left", padx=5)
        
        # 修改表格显示,显示所有列
        self.tree = ttk.Treeview(self.root, columns=[
            "order_date", "order_number", "customer", "product", "unit",
            "quantity", "price", "discount", "final_price", "total",
            "remarks", "discount_amount", "discount_total", "delivery",
            "payment_received", "end_customer", "notes", "business"
        ], show="headings")
        
        # 修改列标题定义,显示所有列
        columns = [
            ("order_date", "单据日期"), 
            ("order_number", "单据编号"),
            ("customer", "客户名称"), 
            ("product", "品名规格"),
            ("unit", "单位"),
            ("quantity", "数量"),
            ("price", "原价"), 
            ("discount", "单行折扣率(%)"),
            ("final_price", "折后价"), 
            ("total", "金额"),
            ("remarks", "备注"),
            ("discount_amount", "整单折扣率(%)"),
            ("discount_total", "折后金额"),
            ("delivery", "运费"),
            ("payment_received", "本单已收"),
            ("end_customer", "结算账户"),
            ("notes", "说明"),
            ("business", "营业员")
        ]
        
        for col, heading in columns:
            self.tree.heading(col, text=heading)
            self.tree.column(col, width=100)
            
        self.tree.pack(padx=5, pady=5, fill="both", expand=True)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(self.root, orient="vertical", command=self.tree.yview)
        scrollbar.pack(side="right", fill="y")
        self.tree.configure(yscrollcommand=scrollbar.set)
        
        # 添加自动计算绑定
        self.entries['quantity'].bind('<KeyRelease>', self.calculate_total)
        self.entries['price'].bind('<KeyRelease>', self.calculate_total)
        self.entries['discount'].bind('<KeyRelease>', self.calculate_total)
        
        # 在订单分类框架底部添加合计标签
        self.total_label = ttk.Label(order_group_frame, text="合计金额: ¥0.00")
        self.total_label.pack(pady=5)

    def calculate_total(self, event=None):
        """计算折后价和金额"""
        try:
            quantity = float(self.entries['quantity'].get() or 0)
            price = float(self.entries['price'].get() or 0)
            discount = float(self.entries['discount'].get() or 100)
            
            # 计算折后价
            final_price = price * discount / 100
            self.entries['final_price'].delete(0, tk.END)
            self.entries['final_price'].insert(0, f"{final_price:.2f}")
            
            # 计算金额
            total = quantity * final_price
            self.entries['total'].delete(0, tk.END)
            self.entries['total'].insert(0, f"{total:.2f}")
        except ValueError:
            pass

    def create_search_frame(self):
        search_frame = ttk.LabelFrame(self.root, text="搜索")
        search_frame.pack(padx=5, pady=5, fill="x")
        
        ttk.Label(search_frame, text="搜索条件:").pack(side="left", padx=5)
        self.search_entry = ttk.Entry(search_frame)
        self.search_entry.pack(side="left", padx=5, fill="x", expand=True)
        
        ttk.Button(search_frame, text="搜索", command=self.search_orders).pack(side="left", padx=5)
        
    def add_function_buttons(self):
        # 在原有btn_frame中添加更多按钮
        ttk.Button(self.btn_frame, text="编辑", command=self.edit_selected).pack(side="left", padx=5)
        ttk.Button(self.btn_frame, text="删除", command=self.delete_selected).pack(side="left", padx=5)
        ttk.Button(self.btn_frame, text="导出Excel", command=self.export_to_excel).pack(side="left", padx=5)
        ttk.Button(self.btn_frame, text="统计报表", command=self.show_statistics).pack(side="left", padx=5)

    def validate_data(self):
        """数据验证"""
        errors = []
        
        # 验证日期格式
        date = self.entries['order_date'].get().strip()
        if not date:
            errors.append("单据日期不能空")
        elif not re.match(r'^\d{4}-\d{2}-\d{2}$', date):
            errors.append("单据日期格式错误,应为 YYYY-MM-DD")
        
        # 验证必填字段
        required_fields = {
            'order_number': '单据编号',
            'customer': '客户名称',
            'product': '品名规格',
            'unit': '单位',
            'quantity': '数量',
            'price': '原价'
        }
        
        for field, name in required_fields.items():
            value = self.entries[field].get().strip()
            if not value:
                errors.append(f"{name}不能为空")
        
        # 验证数字字段
        number_fields = {
            'quantity': '数量',
            'price': '原价',
            'discount': '单行折扣率(%)',
            'final_price': '折后价',
            'total': '金额',
            'discount_amount': '整单折扣率(%)',
            'discount_total': '折后金额',
            'payment_received': '本单已收'
        }
        
        for field, name in number_fields.items():
            value = self.entries[field].get().strip()
            if value:  # 如果有值才验证
                try:
                    num = float(value)
                    if field in ['quantity', 'price'] and num <= 0:
                        errors.append(f"{name}必须大于0")
                    elif num < 0:
                        errors.append(f"{name}不能为负数")
                except ValueError:
                    errors.append(f"{name}必须是数字")
        
        if errors:
            messagebox.showerror("验证错误", "\n".join(errors))
            return False
        return True

    def save_order(self):
        """保存订单数据"""
        if not self.validate_data():
            return
        
        try:
            # 获取所有输入值
            values = []
            fields_order = [
                'order_date', 'order_number', 'customer', 'product', 'unit',
                'quantity', 'price', 'discount', 'final_price', 'total',
                'remarks', 'discount_amount', 'discount_total', 'delivery',
                'payment_received', 'end_customer', 'notes', 'business'
            ]
            
            for field in fields_order:
                value = self.entries[field].get().strip()
                
                # 对数字字段进行转换
                if field in ['quantity', 'price', 'discount', 'final_price', 'total', 
                            'discount_amount', 'discount_total', 'payment_received']:
                    try:
                        value = float(value) if value else 0.0
                    except ValueError:
                        value = 0.0
                elif not value:  # 对非数字字段,如果为空则设为空字��串
                    value = ''
                
                values.append(value)
            
            # 检查单据编号是否重复
            cursor = self.conn.cursor()
            cursor.execute('SELECT COUNT(*) FROM orders WHERE order_number = ?', (values[1],))
            if cursor.fetchone()[0] > 0:
                if not messagebox.askyesno("警告", "单据编号已存在是否继续保存?"):
                    return
            
            # 插入数据
            try:
                cursor.execute('''
                INSERT INTO orders VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
                ''', values)
                self.conn.commit()
                
                # 更新表格显示
                self.tree.insert("", "end", values=values)
                
                # 清空输入框并设置默认值
                self.set_default_values()
                
                # 显示成功消息
                messagebox.showinfo("成功", "订单保存成功!")
                
            except sqlite3.Error as e:
                self.conn.rollback()
                messagebox.showerror("数据库错误", f"保存失败:{str(e)}")
                return
                
        except Exception as e:
            messagebox.showerror("错误", f"保存过程中出错:{str(e)}")
            return
        
        self.update_order_groups()

    def clear_fields(self):
        """清空所有输入框"""
        for field in self.entries:
            self.entries[field].delete(0, tk.END)

    def __del__(self):
        self.conn.close()

    def search_orders(self):
        search_text = self.search_entry.get().strip()
        if not search_text:
            self.load_all_orders()
            return
            
        cursor = self.conn.cursor()
        cursor.execute('''
        SELECT * FROM orders 
        WHERE order_date LIKE ? OR order_number LIKE ? OR customer LIKE ? OR product LIKE ?
        ''', [f'%{search_text}%'] * 4)
        
        self.tree.delete(*self.tree.get_children())
        for row in cursor.fetchall():
            self.tree.insert("", "end", values=row)

    def edit_selected(self):
        selected = self.tree.selection()
        if not selected:
            messagebox.showwarning("提示", "请先选择一条记录")
            return
            
        item = self.tree.item(selected[0])
        values = item['values']
        
        # 填充表单
        for field, value in zip(self.entries.keys(), values):
            self.entries[field].delete(0, tk.END)
            self.entries[field].insert(0, str(value))

    def delete_selected(self):
        selected = self.tree.selection()
        if not selected:
            messagebox.showwarning("提示", "请先选择一条记录")
            return
            
        if messagebox.askyesno("确认", "确定要删除中的记录吗?"):
            item = self.tree.item(selected[0])
            order_number = item['values'][1]
            
            cursor = self.conn.cursor()
            cursor.execute('DELETE FROM orders WHERE order_number = ?', (order_number,))
            self.conn.commit()
            
            self.tree.delete(selected[0])
        
        self.update_order_groups()

    def export_to_excel(self):
        """导出数据到Excel"""
        try:
            # 先获取保存路径
            filename = filedialog.asksaveasfilename(
                defaultextension=".xlsx",
                filetypes=[("Excel files", "*.xlsx")]
            )
            if not filename:
                return
            
            # 获取数据
            cursor = self.conn.cursor()
            cursor.execute('SELECT * FROM orders')
            data = cursor.fetchall()
            
            # 准备列名
            columns = [
                '单据日期', '单据编号', '客户名称', '品名规格',
                '单位', '数量', '原价', '单行折扣率(%)', '折后价', 
                '金额', '备注', '整单折扣率(%)', '折后金额', '运费',
                '本单已收', '结算账户', '说明', '营业员'
            ]
            
            # 创建DataFrame
            df = pd.DataFrame(data, columns=columns)
            
            # 直接导出
            df.to_excel(filename, index=False)
            messagebox.showinfo("成功", "数据已导出到Excel文件")
            
        except PermissionError:
            messagebox.showerror("错误", "无法保存文件,请确保:\n1. 文件未被其他程序打开\n2. 您有写入权限")
        except Exception as e:
            messagebox.showerror("错误", f"导出过程中出错:{str(e)}")

    def show_statistics(self):
        stats_window = tk.Toplevel(self.root)
        stats_window.title("统计报表")
        
        cursor = self.conn.cursor()
        
        # 客户统计
        cursor.execute('''
        SELECT customer, 
               COUNT(*) as order_count,
               SUM(total) as total_amount,
               SUM(payment_received) as total_received
        FROM orders 
        GROUP BY customer
        ''')
        
        # 创建统计表格
        tree = ttk.Treeview(stats_window, columns=["customer", "count", "amount", "received"], show="headings")
        tree.heading("customer", text="客户")
        tree.heading("count", text="订单数")
        tree.heading("amount", text="总金额")
        tree.heading("received", text="已收金额")
        
        for row in cursor.fetchall():
            tree.insert("", "end", values=row)
            
        tree.pack(padx=5, pady=5, fill="both", expand=True)

    def load_all_orders(self):
        """加载所有订单到表格"""
        cursor = self.conn.cursor()
        cursor.execute('SELECT * FROM orders')
        
        self.tree.delete(*self.tree.get_children())
        for row in cursor.fetchall():
            self.tree.insert("", "end", values=row)
        
        # 更新筛选下拉列表
        self.update_filter_lists()
        self.update_order_groups()

    def import_from_excel(self):
        """从Excel文件导入数据"""
        filename = filedialog.askopenfilename(
            filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")]
        )
        if not filename:
            return
        
        try:
            # 读取Excel文件
            df = pd.read_excel(filename)
            
            # 数字字段列表
            numeric_columns = [
                '数量', '原价', '单行折扣率(%)', '折后价', '金额',
                '整单折扣率(%)', '折后金额', '运费', '本单已收'
            ]
            
            # 转换数字列的数据类型
            for col in numeric_columns:
                if col in df.columns:
                    df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0)
            
            # 检查必需的列是否存在
            required_columns = [
                '单据日期', '单据编号', '客户名称', '品名规格', '单位', 
                '数量', '原价', '单行折扣率(%)', '折后价', '金额',
                '备注', '整单折扣率(%)', '折后金额', '运费',
                '本单已收', '结算账户', '说明', '营业员'
            ]
            
            missing_columns = [col for col in required_columns if col not in df.columns]
            if missing_columns:
                messagebox.showerror("错误", f"Excel文件缺少以下列:\n{', '.join(missing_columns)}")
                return
            
            # 创建预览窗口
            preview_window = tk.Toplevel(self.root)
            preview_window.title("导入数据预览")
            preview_window.geometry("800x600")
            
            # 创建预览表格
            preview_tree = ttk.Treeview(preview_window, columns=required_columns[:10], show="headings")
            
            # 设置列标题
            for col in required_columns[:10]:
                preview_tree.heading(col, text=col)
                preview_tree.column(col, width=100)
            
            # 添加数据到预览表格
            for _, row in df.iterrows():
                values = [row[col] for col in required_columns[:10]]
                preview_tree.insert("", "end", values=values)
            
            # 添加滚动条
            scrollbar = ttk.Scrollbar(preview_window, orient="vertical", command=preview_tree.yview)
            scrollbar.pack(side="right", fill="y")
            preview_tree.configure(yscrollcommand=scrollbar.set)
            preview_tree.pack(padx=5, pady=5, fill="both", expand=True)
            
            # 添加按钮框
            btn_frame = ttk.Frame(preview_window)
            btn_frame.pack(pady=5)
            
            def confirm_import():
                try:
                    # 将数据���入数据库
                    cursor = self.conn.cursor()
                    
                    # 定义字段映射
                    field_mapping = {
                        '单据日期': 'order_date',
                        '单据编号': 'order_number',
                        '客户名称': 'customer',
                        '品名规格': 'product',
                        '单位': 'unit',
                        '数量': 'quantity',
                        '原价': 'price',
                        '单行折扣率(%)': 'discount',
                        '折后价': 'final_price',
                        '金额': 'total',
                        '备注': 'remarks',
                        '整单折扣率(%)': 'discount_amount',
                        '折后金额': 'discount_total',
                        '运费': 'delivery',
                        '本单已收': 'payment_received',
                        '结算账户': 'end_customer',
                        '说明': 'notes',
                        '营业员': 'business'
                    }
                    
                    # 获取数据字段顺序
                    db_fields = [
                        'order_date', 'order_number', 'customer', 'product', 'unit',
                        'quantity', 'price', 'discount', 'final_price', 'total',
                        'remarks', 'discount_amount', 'discount_total', 'delivery',
                        'payment_received', 'end_customer', 'notes', 'business'
                    ]
                    
                    for _, row in df.iterrows():
                        values = []
                        for field in db_fields:
                            # 从Excel列名映射到数据库字段
                            excel_col = [k for k, v in field_mapping.items() if v == field][0]
                            value = row[excel_col]
                            
                            # 处理数值
                            if pd.isna(value):
                                value = 0 if field in ['quantity', 'price', 'discount', 'final_price', 
                                                     'total', 'discount_amount', 'discount_total', 
                                                     'payment_received'] else ''
                            elif isinstance(value, (int, float)):
                                value = float(value)
                            else:
                                value = str(value)
                            values.append(value)
                        
                        try:
                            cursor.execute('''
                            INSERT INTO orders VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
                            ''', values)
                        except sqlite3.Error as e:
                            self.conn.rollback()
                            messagebox.showerror("错误", f"插入数据时出错:{str(e)}\n行数据{values}")
                            return
                    
                    self.conn.commit()
                    self.load_all_orders()
                    messagebox.showinfo("成功", "数据导入成功!")
                    preview_window.destroy()
                    
                except Exception as e:
                    self.conn.rollback()
                    messagebox.showerror("错误", f"导入过程中出错:{str(e)}")
            
            def cancel_import():
                preview_window.destroy()
            
            # 添加确认和取消按钮
            ttk.Button(btn_frame, text="确认导入", command=confirm_import).pack(side="left", padx=5)
            ttk.Button(btn_frame, text="取消", command=cancel_import).pack(side="left", padx=5)
            
            # 显示导入数据的总数
            ttk.Label(preview_window, text=f"共 {len(df)} 条数据").pack(pady=5)
            
        except Exception as e:
            messagebox.showerror("错误", f"导入过程中出错:{str(e)}")
        
        self.update_order_groups()

    def export_template(self):
        """导出Excel模板"""
        filename = filedialog.asksaveasfilename(
            defaultextension=".xlsx",
            filetypes=[("Excel files", "*.xlsx")],
            initialfile="订单导出模板.xlsx"
        )
        if not filename:
            return
        
        try:
            # 创建示例数据 - 使用相同的列名
            sample_data = {
                '单据日期': ['2024-01-01'],
                '单据编号': ['XSD202401001'],
                '客户名称': ['示例客户'],
                '品名规格': ['示例产品'],
                '单位': ['个'],
                '数量': [1],
                '原价': [100],
                '单行折扣率(%)': [100],
                '折后价': [100],
                '金额': [100],
                '备注': ['备注示例'],
                '整单折扣率(%)': [0],
                '折后金额': [100],
                '运费': [0],
                '本单已收': [0],
                '结算账户': ['结算账户示例'],
                '说明': ['说明示例'],
                '营业员': ['营业员示例']
            }
            
            # 创建DataFrame
            df = pd.DataFrame(sample_data)
            
            # 创建Excel写入器
            with pd.ExcelWriter(filename, engine='openpyxl') as writer:
                # 写入数据
                df.to_excel(writer, index=False, sheet_name='订单数据')
                
                # 获取工作表
                worksheet = writer.sheets['订单数据']
                
                # 设置列宽
                for column in worksheet.columns:
                    max_length = 0
                    column = [cell for cell in column]
                    for cell in column:
                        try:
                            if len(str(cell.value)) > max_length:
                                max_length = len(str(cell.value))
                        except:
                            pass
                    adjusted_width = (max_length + 2)
                    worksheet.column_dimensions[column[0].column_letter].width = adjusted_width
                
                # 设置样式
                from openpyxl.styles import PatternFill, Font, Alignment, Border, Side
                
                # 定义样式
                header_fill = PatternFill(start_color='CCE5FF', end_color='CCE5FF', fill_type='solid')
                header_font = Font(bold=True)
                center_aligned = Alignment(horizontal='center', vertical='center')
                border = Border(
                    left=Side(style='thin'),
                    right=Side(style='thin'),
                    top=Side(style='thin'),
                    bottom=Side(style='thin')
                )
                
                # 应用表头样式
                for cell in worksheet[1]:
                    cell.fill = header_fill
                    cell.font = header_font
                    cell.alignment = center_aligned
                    cell.border = border
                
                # 应用数据行样式
                for row in worksheet.iter_rows(min_row=2):
                    for cell in row:
                        cell.alignment = center_aligned
                        cell.border = border
            
            messagebox.showinfo("成功", "模板导出成功!\n请按照模板格式准备数据后再进行导。")
            
        except Exception as e:
            messagebox.showerror("错误", f"导出模板时出错:{str(e)}")

    def set_default_values(self):
        """设置默认值"""
        # 清空所有输入框
        self.clear_fields()
        
        # 只设置日期和折扣率的默认值
        today = datetime.now().strftime('%Y-%m-%d')
        self.entries['order_date'].insert(0, today)  # 默认日期为今天
        self.entries['discount'].insert(0, '100')    # 默认折扣率为100%
        
        # 生成新的单据编号
        cursor = self.conn.cursor()
        cursor.execute('''
        SELECT MAX(order_number) FROM orders 
        WHERE order_number LIKE ?
        ''', [f'XSD{today.replace("-", "")}%'])
        
        last_number = cursor.fetchone()[0]
        if last_number:
            try:
                # 从最后一个单号提取序号并加1
                seq = int(last_number[-3:]) + 1
                new_number = f'XSD{today.replace("-", "")}{seq:03d}'
            except ValueError:
                new_number = f'XSD{today.replace("-", "")}001'
        else:
            new_number = f'XSD{today.replace("-", "")}001'
        
        self.entries['order_number'].insert(0, new_number)  # 设置新单据编号

    def update_order_groups(self):
        """更新订单分类显示"""
        cursor = self.conn.cursor()
        cursor.execute('''
        SELECT order_number, customer, product, unit, 
               quantity, price, total, remarks,
               SUM(total) OVER () as total_sum
        FROM orders 
        ORDER BY order_number DESC
        ''')
        
        # 清空现有数据
        self.group_tree.delete(*self.group_tree.get_children())
        
        total_sum = 0
        # 插入新数据
        for row in cursor.fetchall():
            formatted_row = list(row[:8])  # 只取前8列显示
            # 格式化数字列
            formatted_row[4] = f"{row[4]:.2f}"  # 数量
            formatted_row[5] = f"¥{row[5]:.2f}"  # 原价
            formatted_row[6] = f"¥{row[6]:.2f}"  # 金额
            self.group_tree.insert("", "end", values=formatted_row)
            total_sum = row[8]  # 获取合计金额
        
        # 更新合计标签
        self.total_label.config(text=f"合计金额: ¥{total_sum:,.2f}")

    def on_group_select(self, event):
        """当选择订单分类时的处理"""
        selected = self.group_tree.selection()
        if not selected:
            return
        
        # 获取选中的单据编号
        order_number = self.group_tree.item(selected[0])['values'][0]
        
        # 在主表格中查找并选中对应的记录
        for item in self.tree.get_children():
            if self.tree.item(item)['values'][1] == order_number:  # 假设单据编号是第二列
                self.tree.selection_set(item)
                self.tree.see(item)  # 确保选中的项可见
                break

    def filter_orders(self):
        """根据筛选条件过滤订单"""
        order_number = self.order_number_filter.get().strip()
        customer = self.customer_filter.get().strip()
        
        cursor = self.conn.cursor()
        
        # 构建查询条件
        query = '''
        SELECT order_number, customer, product, unit, 
               quantity, price, total, remarks,
               SUM(total) OVER () as total_sum
        FROM orders 
        WHERE 1=1
        '''
        params = []
        
        if order_number:
            query += " AND order_number LIKE ?"
            params.append(f"%{order_number}%")
        
        if customer:
            query += " AND customer LIKE ?"
            params.append(f"%{customer}%")
        
        query += " ORDER BY order_number DESC"
        
        cursor.execute(query, params)
        
        # 清空现有数据
        self.group_tree.delete(*self.group_tree.get_children())
        
        total_sum = 0
        # 插入新数据
        for row in cursor.fetchall():
            formatted_row = list(row[:8])  # 只取前8列显示
            # 格式化数字列
            formatted_row[4] = f"{row[4]:.2f}"  # 数量
            formatted_row[5] = f"¥{row[5]:.2f}"  # 原价
            formatted_row[6] = f"¥{row[6]:.2f}"  # 金额
            self.group_tree.insert("", "end", values=formatted_row)
            total_sum = row[8]  # 获取合计金额
        
        # 更新合计标签
        self.total_label.config(text=f"合计金额: ¥{total_sum:,.2f}")

    def reset_filter(self):
        """重置筛选条件"""
        self.order_number_filter.delete(0, tk.END)
        self.customer_filter.delete(0, tk.END)
        self.update_order_groups()

    def update_order_number_list(self, event=None):
        """更新单据编号下拉列表"""
        search_text = self.order_number_filter.get().strip()
        cursor = self.conn.cursor()
        
        if search_text:
            cursor.execute('''
            SELECT DISTINCT order_number FROM orders 
            WHERE order_number LIKE ? 
            ORDER BY order_number DESC
            ''', [f'%{search_text}%'])
        else:
            cursor.execute('''
            SELECT DISTINCT order_number FROM orders 
            ORDER BY order_number DESC
            ''')
        
        order_numbers = [row[0] for row in cursor.fetchall()]
        if order_numbers:
            self.order_number_filter['values'] = order_numbers
            if search_text:
                self.order_number_filter.event_generate('<Down>')

    def update_customer_list(self, event=None):
        """更新客户名称下拉列表"""
        search_text = self.customer_filter.get().strip()
        cursor = self.conn.cursor()
        
        if search_text:
            cursor.execute('''
            SELECT DISTINCT customer FROM orders 
            WHERE customer LIKE ? 
            ORDER BY customer
            ''', [f'%{search_text}%'])
        else:
            cursor.execute('''
            SELECT DISTINCT customer FROM orders 
            ORDER BY customer
            ''')
        
        customers = [row[0] for row in cursor.fetchall()]
        if customers:
            self.customer_filter['values'] = customers
            if search_text:
                self.customer_filter.event_generate('<Down>')

    def update_filter_lists(self):
        """更新所有筛选下拉列表"""
        cursor = self.conn.cursor()
        
        # 更新单据编号列表
        cursor.execute('SELECT DISTINCT order_number FROM orders ORDER BY order_number DESC')
        self.order_number_filter['values'] = [row[0] for row in cursor.fetchall()]
        
        # 更新客户名称列表
        cursor.execute('SELECT DISTINCT customer FROM orders ORDER BY customer')
        self.customer_filter['values'] = [row[0] for row in cursor.fetchall()]

    def print_filtered_data(self):
        """打印筛选后的数据"""
        try:
            # 获取当前筛选条件下的数据
            order_number = self.order_number_filter.get().strip()
            customer = self.customer_filter.get().strip()
            
            # 构建查询条件
            query = '''
            SELECT order_number, customer, product, unit, 
                   quantity, price, total, remarks,
                   SUM(total) OVER () as total_sum
            FROM orders 
            WHERE 1=1
            '''
            params = []
            
            if order_number:
                query += " AND order_number LIKE ?"
                params.append(f"%{order_number}%")
            
            if customer:
                query += " AND customer LIKE ?"
                params.append(f"%{customer}%")
            
            query += " ORDER BY order_number DESC"
            
            cursor = self.conn.cursor()
            cursor.execute(query, params)
            rows = cursor.fetchall()
            
            if not rows:
                messagebox.showinfo("提示", "没有数据可打印")
                return
            
            # 生成HTML内容
            html_content = f"""
            <!DOCTYPE html>
            <html>
            <head>
                <meta charset="utf-8">
                <title>订单查询结果</title>
                <style>
                    body {{ font-family: SimSun, serif; }}
                    table {{ border-collapse: collapse; width: 100%; margin-top: 10px; }}
                    th, td {{ 
                        border: 1px solid black; 
                        padding: 8px; 
                        text-align: center; 
                    }}
                    th {{ background-color: #f2f2f2; }}
                    .total {{ 
                        text-align: right;
                        padding: 10px;
                        font-weight: bold;
                    }}
                    .header-info {{
                        margin: 10px 0;
                        padding: 10px;
                        border: 1px solid #ddd;
                        background-color: #f9f9f9;
                    }}
                    .header-info p {{
                        margin: 5px 0;
                    }}
                    @media print {{
                        .no-print {{ display: none; }}
                        body {{ margin: 0; }}
                        table {{ page-break-inside: auto; }}
                        tr {{ page-break-inside: avoid; }}
                    }}
                </style>
            </head>
            <body>
                <h2 style="text-align: center;">订单查询结果</h2>
                <div class="header-info">
                    <p><strong>单据编号:</strong>{order_number if order_number else "全部"}</p>
                    <p><strong>客户名称:</strong>{customer if customer else "全部"}</p>
                    <p><strong>��印时间:</strong>{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
                </div>
                <table>
                    <tr>
                        <th>品名规格</th>
                        <th>单位</th>
                        <th>数量</th>
                        <th>原价</th>
                        <th>金额</th>
                        <th>备注</th>
                    </tr>
            """
            
            # 添加数据行
            for row in rows:
                html_content += f"""
                    <tr>
                        <td>{row[2]}</td>
                        <td>{row[3]}</td>
                        <td>{row[4]:.2f}</td>
                        <td>¥{row[5]:.2f}</td>
                        <td>¥{row[6]:.2f}</td>
                        <td>{row[7]}</td>
                    </tr>
                """
            
            # 添加合计行
            total_sum = rows[0][8] if rows else 0
            html_content += f"""
                </table>
                <div class="total">
                    合计金额: ¥{total_sum:,.2f}
                </div>
                <div class="no-print" style="margin-top: 20px; text-align: center;">
                    <button onclick="window.print()">打印</button>
                </div>
            </body>
            </html>
            """
            
            # 保存HTML文件
            temp_file = "订单查询结果.html"
            with open(temp_file, "w", encoding="utf-8") as f:
                f.write(html_content)
            
            # 在默认浏览器中打开HTML文件
            import webbrowser
            webbrowser.open(temp_file)
            
        except Exception as e:
            messagebox.showerror("错误", f"打印过程中出错:{str(e)}")

if __name__ == "__main__":
    # 显示登录窗口
    login = LoginWindow()
    if login.run():  # 只有在登录成功时才继续
        root = tk.Tk()
        app = OrderSystem(root)
        root.mainloop()
    else:
        sys.exit(0)  # 登录失败或关闭登录窗口时退出程序

多个文件 打包成EXE 

我可以帮你使用 PyInstaller 将这两个 Python 文件打包成一个单独的 exe 文件。以下是具体步骤:

1. 首先安装 PyInstaller:
pip install pyinstaller

2. 创建一个 spec 文件,命名为 `zhi.spec`:

# -*- mode: python ; coding: utf-8 -*-

block_cipher = None

a = Analysis(
    ['zhi.py'],  # 主程序文件
    pathex=[],
    binaries=[],
    datas=[('login.py', '.'), ('config.json', '.')],  # 添加其他需要的文件
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)

pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    [],
    name='订单记录系统',  # exe文件名
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=False,  # 设置为False不显示控制台窗口
    disable_windowed_traceback=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
    icon='icon.ico',  # 如果有图标文件的话
)

3. 在命令行中执行打包命令:

pyinstaller --clean zhi.spec
 

如果你想要更简单的方式,可以直接使用以下命令:

pyinstaller --noconfirm --onefile --windowed --icon=icon.ico --add-data "login.py;." --add-data "config.json;." zhi.py
 

注意事项:
1. 确保所有需要的依赖包都已安装
2. 如果使用了图标,需要准备一个 .ico 文件
3. 打包后的文件会在 `dist` 目录下
4. 建议在打包前测试程序是否正常运行

完整的打包步骤:

1. 准备文件:
your_project_folder/
    ├── zhi.py
    ├── login.py
    ├── config.json
    ├── icon.ico (可选)
    └── zhi.spec

2. 安装必要的包:

pip install pyinstaller
pip install pandas
pip install sqlite3

3. 执行打包命令:
pyinstaller --clean zhi.spec

4. 检查生成的文件:
- 打包完成后,在 `dist` 目录下会生成一个名为"订单记录系统.exe"的文件

-记得在"订单记录系统.exe" 同时目录 建设一个 db目录
- 这个 exe 文件包含了所有必要的依赖和资源文件
- 可以直接双击运行,不需要安装 Python 环境

1.1 版本,其它 2025年后。。。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/943095.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习案列】车牌自动识别系统:基于YOLO11的高效实现

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

SpringBoot(二)—— yaml配置文件

接上篇&#xff0c;我们对SpringBoot有了基本的了解&#xff0c;接下来探究配置文件。 目录 二、配置文件 1. SpringBoot热部署 2. 配置文件 2.1 配置文件的作用 2.2 YAML 配置文件 2.3 YAML 与 XML 比较 3. YAML语法 3.1 键值对 3.2 值的写法 3.3 对象/Map&#x…

NFV架构

通信&#xff08;CT&#xff09;的NFV技术是借鉴了IT行业的云计算概念&#xff0c;实际大规模应用在4G时代。 区别是增加了以下几点 1、NFVI是openstack的电信增强版本&#xff0c;除了nova cinder nuetru等增加了电信专用组件。 2、设计增加了mano&#xff0c;包括了VIM、NFVO…

关于Edge浏览器的设置

这里记录几条个人比较习惯的使用浏览器方式的设置&#xff0c;主要是edge浏览器 1. 黑背景色 修改整个浏览器的背景色为黑色&#xff0c;而不是主题&#xff0c;只有边框颜色改变地址栏输入edge://flags/#enable-force-dark&#xff0c;将Default 改为 Enabled&#xff1b;如…

Elasticsearch:什么是查询语言?

查询语言定义 查询语言包括数据库查询语言 (database query language - DQL)&#xff0c;是一种用于查询和从数据库检索信息的专用计算机语言。它充当用户和数据库之间的接口&#xff0c;使用户能够管理来自数据库管理系统 (database management system - DBMS) 的数据。 最广…

Android使用PorterDuffXfermode模式PorterDuff.Mode.SRC_OUT橡皮擦实现马赛克效果,Kotlin(3)

Android使用PorterDuffXfermode模式PorterDuff.Mode.SRC_OUT橡皮擦实现马赛克效果&#xff0c;Kotlin&#xff08;3&#xff09; import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.Canvas impor…

《信管通低代码信息管理系统开发平台》Linux环境安装说明

1 简介 信管通低代码信息管理系统应用平台提供多环境软件产品开发服务&#xff0c;包括单机、局域网和互联网。我们专注于适用国产硬件和操作系统应用软件开发应用。为事业单位和企业提供行业软件定制开发&#xff0c;满足其独特需求。无论是简单的应用还是复杂的系统&#xff…

jetson Orin nx + yolov8 TensorRT 加速量化 环境配置

参考【Jetson】Jetson Orin NX纯系统配置环境-CSDN博客 一 系统环境配置&#xff1a; 1.更换源&#xff1a; sudo vi /etc/apt/sources.list.d/nvidia-l4t-apt-source.list2.更新源&#xff1a; sudo apt upgradesudo apt updatesudo apt dist-upgrade sudo apt-get updat…

Burp炮台实现(动态ip发包)

基本步骤 1.使用 zmap 爬取大量代理ip 2.使用py1脚本初步筛选可用ip 3.利用py2脚本再次筛选对目标网站可用ip&#xff08;不带payload安全检测&#xff09; 4.配置 burp 插件并加载收集到的代理池 5.加载payload&#xff0c;开始爆破 Zmap kali安装 sudo apt update apt …

springboot495基于java的物资综合管理系统的设计与实现(论文+源码)_kaic

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统物资综合管理系统信息管理难度大&#xff0c;容错率低&am…

STM32-笔记16-定时器中断点灯

一、实验目的 使用定时器 2 进行中断点灯&#xff0c;500ms LED 灯翻转一次。 二&#xff0c;定时器溢出时间计算 Tout&#xff1a;定时器溢出时间 Ft&#xff1a;定时器的时钟源频率 ARR&#xff1a;自动重装载寄存器的值 PSC&#xff1a;预分频器寄存器的值 例如&#xff0c…

【MySQL】 SQL优化讲解

一、优化前的思考 在定位到慢查询后&#xff0c;面试官常问如何优化或分析慢查询的SQL语句。若存在聚合查询、多表查询&#xff0c;可尝试优化SQL语句结构&#xff0c;如多表查询可新增临时表&#xff1b;若表数据量过大&#xff0c;可添加索引&#xff0c;但添加索引后仍慢则…

C/C++ 数据结构与算法【栈和队列】 栈+队列详细解析【日常学习,考研必备】带图+详细代码

一、介绍 栈和队列是限定插入和删除只能在表的“端点”进行的线性表&#xff0c;是线性表的子集&#xff0c;是插入和删除位置受限的线性表。 &#xff08;操作受限的线性表&#xff09; 二、栈 1&#xff09;概念&#xff1a; 栈(stack)是一个特殊的线性表&#xff0c;是限…

【HarmonyOS应用开发——ArkTS语言】购物商城的实现【合集】

目录 &#x1f60b;环境配置&#xff1a;华为HarmonyOS开发者 &#x1f4fa;演示效果&#xff1a; &#x1f4d6;实验步骤及方法&#xff1a; 1. 在src/main/ets文件中创建components文件夹并在其中创建Home.ets和HomeProduct.ets文件。​ 2. 在Home.ets文件中定义 Home 组…

连锁餐饮行业数据可视化分析方案

引言 随着连锁餐饮行业的迅速发展&#xff0c;市场竞争日益激烈。企业需要更加精准地把握运营状况、消费者需求和市场趋势&#xff0c;以制定科学合理的决策&#xff0c;提升竞争力和盈利能力。可视化数据分析可以帮助连锁餐饮企业整合多源数据&#xff0c;通过直观、动态的可…

学习threejs,THREE.RingGeometry 二维平面圆环几何体

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.RingGeometry 圆环几…

计算机网络实验室建设方案

一、计算机网络实验室拓扑结构 计算机网络综合实验室解决方案&#xff0c;是面向高校网络相关专业开展教学实训的综合实训基地解决方案。教学实训系统采用 B&#xff0f;S架构&#xff0c;通过公有云教学实训平台在线学习模式&#xff0c;轻松实现网络系统建设与运维技术的教学…

ThinkPHP 8开发环境安装

【图书介绍】《ThinkPHP 8高效构建Web应用》-CSDN博客 《ThinkPHP 8高效构建Web应用 夏磊 编程与应用开发丛书 清华大学出版社》【摘要 书评 试读】- 京东图书 1. 安装PHP8 Windows系统用户可以前往https://windows.php.net/downloads/releases/archives/下载PHP 8.0版本&am…

Nmap基础入门及常用命令汇总

Nmap基础入门 免责声明&#xff1a;本文单纯分享技术&#xff0c;请大家使用过程中遵守法律法规~ 介绍及安装 nmap是网络扫描和主机检测的工具。作为一个渗透测试人员&#xff0c;必不可少的就是获取信息。那么nmap就是我们从互联网上获取信息的途径&#xff0c;我们可以扫描互…

使用openvino加速部署paddleocr文本方向分类模型(C++版)

使用openvino加速部署paddleocr文本方向分类模型(C++版) 大体流程方向分类器在openvino上的部署代码C++大体流程 原始图像: 先resize 再归一化 方向分类器在openvino上的部署代码C++ #include <iostream> #include <string>#include <vector> #i…