统信小程序(八)归档目录自动调整

'''

档案归档是否希望自动根据页数分盒归档?本文讲述能自动分卷来实现,分盒后序号、页号都自动从1开始。能自动单元行加粗。能自动分页。下一步计划完善自动插入分页符的功能。

目前的档案归档是大概250页手工分盒,然后手写页号后生成目录,本程序的目的不是替换手工,而是和手工分卷进行相互印证,也可提前规划分卷情况。

本程序功能的简单描述:

根据提供的表头、行内容,实现自动分页、分卷,自动编序号、自动写入页号。并且,通过在A列是否填入序号来选择是否输入某些行。

程序具体要求(地址可以自己修改):

项目是通过tkinter在C:\\Users\\1\\Desktop\\T1\\目录下实现一个商业级python办公软件"目录生成",目的是只要做好模板,就能根据待复制文件生成结果文件。注意界面要美观,默认字体仿宋(已确定字体可用),字号12号,调整为更漂亮的布局,颜色为黄色系。打开文件框的默认打开位置:"/home/huanghe/Desktop/T1/"或者"C:\\Users\\1\\Desktop\\T1\\"在界面下部显示提示:"自动查找文件夹内"1"为开头的xlsx文件第一个工作表作为模板文件,其A1至G7单元格的内容作为待复制文件,其打印区域设置复制出错时则忽略这个错误。把模板文件的内容套进待复制文件中生成新的结果文件,写入时保持待复制文件原格式然后根据要求修改。界面只有打开位置地址栏、打开文件夹(深黄色)、开始(绿色)两个按钮。如果点击打开文件夹按钮可以选择要处理的文件夹。如果不点击打开文件夹按钮,则直接点击开始按钮时自动选择默认打开位置。界面中复选框"带页号中止",默认选中,如果选中,则F列写入值时判断对当前行的页数,如果页数为1则不变,如果页数大于1的数字,则写入:页数+"-"+(页号+页数-1)。意思是显示文件页号的起和止。

逻辑描述:总要求点击开始按钮后,

把模板文件的内容套进待复制文件中生成新的结果文件,保持待复制文件原格式。并保存在当前文件夹下的新建文件夹中,文件夹的名称取自模板文件表中D2单元格的名字。要求:按照A4的尺寸自动分页,表头是模板文件的前6行,每页都有表头,表头中的共几卷、第几卷根据实际情况改,每页的最后一行是"第*页"这样的页码。模板文件的H列是每行内容的页数,结果文件的F列是据此叠加的页号,页号超过250自动分卷,分卷后序号、页号重新计。如果模板文件的D列是包含"单元"内容,则这一行不写入A列序号和F列页号。自动调整行高。

开始总的主程序,顺序完成模板操作和写入程序,具体如下:

以下要求不可遗漏,如果逻辑有冲突则提示。

模板操作:

首先找模板文件,即自动查找文件夹内"1"为开头的xlsx文件中第1个工作表中"C2、B3、D3、F3、B4、D4、F4、F5"单元格的内容逐个写入表头数组。

从模板文件的第7行H列开始向下读取该列所有内容,作为对应行的页数数组。

变量"xh"初始值为1(就是序号),变量"ym"初始值为1(就是当前页在目录的第几页),变量"卷号"初始值为1(就是卷号),变量"yy"初始值250(就是每卷的最大允许页号),变量"HGhj"初始值为0(就是每页的行高合计)。变量"jh"(就是卷号)。变量"yhhj"初始值为1(就是档案在当前卷的第几页就是页号),变量"yhj"初始值为510(就是每页允许磅值)变量"ymhg"初始值22(就是第几页所在行的行高)

执行"模板操作"后执行写入程序的循环操作。

注意:其中结果文件在多次循环操作中多次修改,最后才通过输出程序正式保存。

写入程序:

每个执行写入程序包含多次"页写入"循环,每个"页写入"的循环包括1次复制复制页、1次表头写入和循环行写入的操作。页写入程序的整体运行逻辑依据:该程序是为了将模板内容分页输出,每页程序都是进行先复制复制页操作,来复制一个新的"复制页"写一次表头然后看是说明行还是序号行按要求逐行写入数据,调整行高,如果在该行吸入后判断该分卷则分卷,该分页则分页,然后重新执行写入程序,还是先复制一个新的"复制页"写一次表头再继续延续之前行从已写入内容的下一行继续执行,直至执行完毕,如果判断已执行则结束写入程序,接着执行输出程序得到结果文件即可。以此实现模板文件的内容全部写入结果文件。

复制复制页:其次找到待复制文件,其第一个工作表中有A1至G7单元格的内容的表格整体复制后作为"复制页"作为临时文件,等待循环替换并按照原格式输出。注意:复制单元格样式时应保留原格式,例如字体、字号、是否加粗、行距、边框、居中、自动换行、合并单元格信息。

表头写入:将表头数组的值写入"复制页"的对应位置单元格,注意保留待复制文件原格式,例如字体、字号、是否加粗、行距、边框、居中、是否自动换行、合并单元格等信息,然后加上细边框。

循环行写入:从模板文件得到的第A列至G列从第7行开始逐行往下开始读取内容,直至把每行读完后入完毕,并记住读取模板文件的第几行,以便下一个写入程序循环。首先每行都执行该行的D列单元判断、A列判断、行高判断、分离判断,不断按照从上到下的顺序将B列至G列逐行写入"复制页"B列至G列从第7行开始逐行往下判断后写入,以此类推把以下每行单元格内容写入。(注意延续上一个"页写入"循环最后写入的行继续向下)

D列单元判断:每行D列单元格的写入内容如果字数小于10个字符且包含"单元"则该行作为一个"说明行",该行不再进行A列判断,仅对应写入A到G列单元内容,该行D列单元格格式设置为宋体12号加粗、左右居中、垂直居中。其对应行的F列不写入,其对应的A列也不写入。注意:该行不参与序号、页码的合计及写入,不参与"序号行"的序号xh和页码yhhj计数。

A列判断:如果模板文件中该行D列单元判断不是"说明行",判断A列的单元格如果是数值,就作为"序号行"进行序号写入"复制页"。每个"序号行"的A列均写入序号变量xh,每写入一行xh自动加1。

每个"序号行"的F列写入的页号就是变量yhhj,每个序号行写入结束变量yhhj就叠加本行的页数,得到的新变量yhhj。B、C、D、E、G列写入对应的模板文件中单元格的值并保持原字体、字号、左右居中、垂直居中、自动换行的格式,然后设置该行A到G列单元边框为细边框。

例如:如果第2卷的第1个"序号行"页数是2,它的页号就是yhhj的初始值1(yhhj)因此其F列写入1,并且变量yhhj增加2等于3,因此下一个"序号行"写入的页号就是新的yhhj,也就是3以此类推。

例如:如果A列的单元格不是数字则自动跳过该行寻找下一行,例如:第1卷模板文件的第7行经D列单元判断为"说明行"则按"说明行"要求写入"复制页"第7行。模板文件的第8行经D列单元判断不是为"说明行"且A列内容是1,判断是数字并且此行H列的页数2,因此将xh的值1正常写入"复制页"第8行A列,B列至E列对照写入,F列写入页号值为1(即yhhj初始值为1)然后yhhj增加2=3,接着判断出模板文件第9、10行的内容不为数字且不是"说明行",则不写入,继续进行下一行判断,找到模板文件的第11行同样不为数字但是符合D列单元判断,则为"说明行"写入"复制页"第9行D列、其中A列和F列为空,继续向下判断,找到模板文件的第12行数字且不是"说明行"时,将其B列至E列作为"序号行"写入到"复制页"的第10行,其A列写入了序号xh值2(xh=xh+1,即不断累加),其页号在F列写入3(因为在上一个"序号行"写入后yhhj已变为3)(注意维持原格文字式),以此类推。

分离判断:首先,每行判断是否模板文件的B列至E列内容的所有行均写入了,如果写入完了,则执行结尾操作,之后跳出该行的分离判断不再进行下一步判断,结束写入程序,开始输出程序。否则,每行判断当页码合计yhhj大于yy,或"1"为开头的xlsx文件中第1个工作表中I列(此列为强制分卷标记)的该行有内容为"分卷"时,则执行分卷操作之后跳出该行分离判断不再进行下一步判断,保留读取模板文件的行号继续循环运行写入程序。否则,如果行高合计HGhj≥yhg磅时或I列(此列为强制分卷标记)的该行有内容为"分页"时,执行分页操作,保留读取模板文件的行号继续循环运行写入程序。

结尾操作:将当前写入行的向下保留wc行,行高25,并将这些行单元格边框设置为细边框,之下的所有行内容删除,并在写入行的向下wc+1行的F列单元格写入"第ym页",替换文中ym为变量"ym"的值,格式为宋体12居中,该行行高为变量ymhg。wc的值为:(yhj-HGhj)/25的结果四舍五入取整数。将此"复制页"写入结果文件的x+1行(按从上到下顺序放入结果文件的第一个工作表)。接着将结果文件中所有的"共 1 卷"、"第 - 卷"内容,分别替换为"共 jh 卷"、"第 jh 卷",其中的jh替换为变量"jh"的值(即最后的卷号就是整体总卷号)。然后结束写入程序执行输出程序。

分卷操作:将当前写入行的向下保留wc行,行高25,并将这些行单元格边框设置为细边框,之下的所有行内容删除,并在写入行的向下wc+1行的F列单元格写入"第ym页",替换文中ym为变量"ym"的值,格式为宋体12居中,该行行高为变量ymhg。wc的值为:(yhj-HGhj)/25的结果四舍五入取整数。然后将"复制页"中"第 - 卷"内容,替换为"第 jh 卷",其中的jh替换为变量"jh"的值,然后将序号"xh"、页号合计yhhj的值和页码"ym"重置为1,重置行高合计HGhj的值为0,卷号变量"jh"值加1。将修改后的"复制页"写入结果文件中原有内容向下一行的位置(按从上到下顺序放入结果文件的第一个工作表)。如此完整一页同时完成了卷号增加和页码页重置。接着进入写入程序的下一次循环。

分页操作:清除最后一个写入行以下的表格内容,包括边框,并在写入行的向下一行的F列单元格写入"第ym页",格式为宋体12居中,该行行高为变量ymhg。替换文中ym为变量"ym"的值,随后页码ym值加1,重置行高合计HGhj的值为0。如此完成一页。每页按从上到下顺序放入结果文件的第一个工作表,(意思是一页放不下了,新生成一页)之后的每一页写入程序的循环应在x+1行开始写入。将此"复制页"写入结果文件的x+1行。即如果之前有写入页则从已写入内容的下一行继续写入。接着进入写入程序的下一次循环。

行高判断:行高DHG,用于通过每行的D列单元格确定行高,算法为每15个字一个级别,英文、数字占半个字,标点占一个字的位置。如该行D单元格字数≤15,行高DHG=15*1+10=25,16≤字数≤30算两行,行高DHG=15*2+10=40,,31≤字数≤45算三行,行高DHG=15*3+10=55。以此类推,单位为磅。

行高BHG用于通过每行的B列单元格确定行高,算法为每6个字一个级别,英文、数字占半个字,标点占一个字的位置。如该行B单元格字数≤12,行高DHG=10*1+10=25,13≤字数≤17算两行,行高DHG=15*2+10=40,,31≤字数≤45算三行,行高DHG=15*3+10=55。以此类推,单位为磅。

行高CHG用于通过每行的C列单元格确定行高,算法为每4个字一个级别,英文、数字占半个字,标点占一个字的位置。如该行B单元格字数≤4,行高DHG=10*1+10=25,5≤字数≤8算两行,行高DHG=15*2+10=40,9≤字数≤12算三行,行高DHG=15*3+10=55。以此类推,单位为磅。

该行的行高HG为DHG、BHG、CHG的最大值,具体调整该行行高,行高合计HGhj为本页内逐行叠加行高HG进行累加的结果。

输出程序:写入程序结束后,无需手动操作,程序自动在选中的文件夹即当前文件夹下自动新建一个文件夹(注意是一个文件夹不是多个文件夹)。该文件夹名称是取自"1"为开头的xlsx模板文件中第1个工作表中"C2"单元格内容。将处理结束的结果文件命名为:待复制文件名称(不要扩展名,例如"档案目录")+(+"1"为开头的xlsx模板文件中第1个工作表中"C2"单元格内容+)。然后另存进中新建的文件夹,如果有名字重复的文件夹,则在文件夹名后边增加序号。然后,对保存的结果文件插入分页符,除了最后一个第几页之外,在所有的第几页下插入分页符,

程序生成后逐条核对要求是否符合。

'''

复制代码
import os  # 文件路径操作
import tkinter as tk  # GUI框架
from tkinter import ttk, filedialog, messagebox, scrolledtext  # GUI组件
from openpyxl import load_workbook, Workbook  # Excel操作
from openpyxl.styles import Font, Alignment, Border, Side, PatternFill  # Excel样式
from openpyxl.worksheet.pagebreak import Break  # Excel分页符
from openpyxl.utils import get_column_letter  # Excel列字母转换
import copy  # 深拷贝
import re  # 正则表达式
import threading  # 多线程处理
from datetime import datetime  # 时间处理
import time  # 时间相关
import math  # 数学计算


# 定义专业目录生成器类
class ProfessionalDirectoryGenerator:
    """专业级目录生成器 - 商业级办公软件"""

    def __init__(self, root):
        """初始化目录生成器"""
        # 设置根窗口属性
        self.root = root
        self.root.title("审计目录分页分卷算页码 v6.0")
        self.root.geometry("1000x200")

        # 定义黄色系配色方案
        self.colors = {
            'primary_bg': '#FFFDE7',  # 主背景色
            'secondary_bg': '#FFF9C4',  # 次背景色
            'card_bg': '#FFF59D',  # 卡片背景
            'accent_yellow': '#FFD54F',  # 强调黄色
            'accent_orange': '#FFB74D',  # 橙色
            'success_green': '#8BC34A',  # 成功绿色
            'warning_orange': '#FF9800',  # 警告橙色
            'error_red': '#F44336',  # 错误红色
            'text_dark': '#5D4037',  # 深色文字
            'text_light': '#FFFFFF',  # 浅色文字
            'entry_bg': '#FFFFFF',  # 输入框背景
            'border_color': '#FFB300',  # 边框色
            'button_yellow': '#FFC107',  # 黄色按钮
            'button_green': '#4CAF50',  # 绿色按钮
            'status_bar': '#FFF176',  # 状态栏
        }

        # 定义字体配置
        self.fonts = {
            'title': ("Fangsong Ti", 10, "bold"),  # 标题字体
            'heading': ("Fangsong Ti", 10, "bold"),  # 标题字体
            'normal': ("Fangsong Ti", 10),  # 普通字体
            'small': ("Fangsong Ti", 10),  # 小字体
            'mono': ("Consolas", 10),  # 等宽字体
        }

        # 定义状态变量
        self.is_processing = False  # 是否正在处理
        self.cancel_requested = False  # 是否请求取消
        self.show_page_range = tk.BooleanVar(value=True)  # 新增:显示页号起止复选框

        # 设置默认路径
        if os.name == 'posix':
            self.default_path = "/home/huanghe/Desktop/T1/"  # Linux路径
        else:
            self.default_path = "C:\\Users\\1\\Desktop\\T1\\"  # Windows路径
        # 如果默认路径不存在,使用桌面路径
        if not os.path.exists(self.default_path):
            if os.name == 'posix':
                self.default_path = os.path.expanduser("~/Desktop")  # Linux桌面
            else:
                self.default_path = os.path.join(os.path.expanduser("~"), "Desktop")  # Windows桌面

        # 设置用户界面
        self.setup_ui()

    def setup_ui(self):
        """设置用户界面"""
        # 创建主容器框架
        main_container = tk.Frame(self.root, bg=self.colors['primary_bg'])
        main_container.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)

        # 创建主内容区域
        content_frame = tk.Frame(main_container, bg=self.colors['primary_bg'])
        content_frame.pack(fill=tk.BOTH, expand=True)

        # 创建左侧控制面板
        left_panel = tk.Frame(content_frame, bg=self.colors['primary_bg'])
        left_panel.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 15))

        # 创建工作目录设置框架
        dir_frame = self.create_styled_frame(left_panel, "📂 工作目录设置")

        # 创建路径输入框架
        path_input_frame = tk.Frame(dir_frame, bg=self.colors['secondary_bg'])
        path_input_frame.pack(fill=tk.X, padx=20, pady=20)

        # 创建路径标签
        tk.Label(
            path_input_frame,
            text="工作目录:",
            font=self.fonts['normal'],
            bg=self.colors['secondary_bg'],
            fg=self.colors['text_dark']
        ).pack(side=tk.LEFT, padx=(0, 10))

        # 创建路径变量和输入框
        self.path_var = tk.StringVar(value=self.default_path)
        self.path_entry = tk.Entry(
            path_input_frame,
            textvariable=self.path_var,
            font=self.fonts['normal'],
            bg=self.colors['entry_bg'],
            fg='#000000',
            width=45
        )
        self.path_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10))

        # 创建浏览按钮(深黄色)
        self.browse_btn = tk.Button(
            path_input_frame,
            text="📂 浏览文件夹",
            command=self.browse_folder,
            font=self.fonts['normal'],
            bg='#FFB300',  # 深黄色
            fg=self.colors['text_dark'],
            activebackground='#FFA000',
            activeforeground=self.colors['text_dark'],
            relief=tk.RAISED,
            bd=2,
            padx=20,
            pady=5,
            cursor="hand2"
        )
        self.browse_btn.pack(side=tk.RIGHT)

        # 创建操作控制区
        control_frame = self.create_styled_frame(left_panel, "🎮 操作控制")

        # 创建选项框架
        options_frame = tk.Frame(control_frame, bg=self.colors['card_bg'])
        options_frame.pack(pady=10)

        # 创建复选框:显示页号起止
        self.range_checkbox = tk.Checkbutton(
            options_frame,
            text="显示页号起止",
            variable=self.show_page_range,
            font=self.fonts['normal'],
            bg=self.colors['card_bg'],
            fg=self.colors['text_dark'],
            selectcolor=self.colors['secondary_bg']
        )
        self.range_checkbox.pack(side=tk.LEFT, padx=10)

        # 创建按钮容器
        button_container = tk.Frame(control_frame, bg=self.colors['card_bg'])
        button_container.pack(pady=30)

        # 创建开始按钮(绿色)
        self.start_btn = tk.Button(
            button_container,
            text=" 开始",
            command=self.start_processing,
            font=self.fonts['normal'],
            bg=self.colors['button_green'],
            fg=self.colors['text_light'],
            activebackground='#45a049',
            activeforeground=self.colors['text_light'],
            relief=tk.RAISED,
            bd=2,
            padx=30,
            pady=10,
            cursor="hand2"
        )
        self.start_btn.pack(pady=10)

        # 创建底部信息栏
        bottom_frame = tk.Frame(main_container, bg=self.colors['status_bar'], height=60)
        bottom_frame.pack(fill=tk.X, pady=(20, 0))
        bottom_frame.pack_propagate(False)

        info_text = "提示:自动查找文件夹内'1'开头的.xlsx文件作为模板文件,使用其A1至G7单元格内容作为待复制文件内容"
        info_label = tk.Label(
            bottom_frame,
            text=info_text,
            font=self.fonts['small'],
            bg=self.colors['status_bar'],
            fg=self.colors['text_dark'],
            pady=20,
            wraplength=600
        )
        info_label.pack()

        # 绑定快捷键
        self.root.bind('<Return>', lambda e: self.start_processing())
        self.root.bind('<Escape>', lambda e: self.stop_processing())

        # 记录初始日志
        self.log_message("系统初始化完成", "info")
        self.log_message(f"默认工作目录: {self.default_path}", "info")

    def create_styled_frame(self, parent, title):
        """创建带标题的装饰框"""
        # 创建框架
        frame = tk.Frame(parent, bg=self.colors['card_bg'], relief=tk.RAISED, bd=2)
        frame.pack(fill=tk.BOTH, expand=True, pady=(0, 15))

        # 创建标题标签
        title_label = tk.Label(
            frame,
            text=title,
            font=self.fonts['heading'],
            bg=self.colors['accent_yellow'],
            fg=self.colors['text_dark'],
            padx=20,
            pady=10
        )
        title_label.pack(anchor=tk.W, fill=tk.X)

        return frame

    def browse_folder(self):
        """浏览文件夹"""
        # 打开文件夹选择对话框
        folder_path = filedialog.askdirectory(
            initialdir=self.path_var.get(),
            title="选择工作文件夹"
        )
        # 如果选择了路径,则更新路径变量并记录日志
        if folder_path:
            self.path_var.set(folder_path)
            self.log_message(f"工作目录已设置为: {folder_path}", "info")

    def clear_log(self):
        """清空日志(简化版,移除日志文本框)"""
        # 简化版本,什么都不做
        pass

    def log_message(self, message, level="info"):
        """记录日志消息(简化版,移除日志文本框)"""
        # 简化版本,只打印到控制台
        timestamp = datetime.now().strftime("%H:%M:%S")
        level_icons = {
            "info": "ℹ️",
            "success": "✅",
            "warning": "⚠️",
            "error": "❌",
            "debug": "🔍"
        }
        icon = level_icons.get(level, "•")
        print(f"[{timestamp}] {icon} {message}")

    def update_status(self, message, progress=None):
        """更新状态(简化版,移除状态标签和进度条)"""
        # 简化版本,只打印消息
        print(f"状态更新: {message}")

    def update_stats(self, files=0, rows=0, pages=0, volumes=0):
        """更新统计信息(简化版,移除统计信息框架)"""
        # 简化版本,什么都不做
        pass

    def start_processing(self):
        """开始处理"""
        # 如果已经在处理中,显示警告
        if self.is_processing:
            messagebox.showwarning("警告", "已经在处理中,请等待当前任务完成。")
            return

        # 获取工作目录
        work_dir = self.path_var.get().strip()
        # 如果没有指定工作目录,使用默认路径
        if not work_dir:
            work_dir = self.default_path
            self.path_var.set(work_dir)

        # 检查工作目录是否存在
        if not os.path.exists(work_dir):
            messagebox.showerror("错误", f"指定的文件夹不存在!\n{work_dir}")
            return

        # 重置状态变量
        self.is_processing = True
        self.cancel_requested = False
        # 禁用开始按钮
        self.start_btn.config(state=tk.DISABLED)

        # 记录开始处理的日志
        self.log_message("开始目录生成处理", "info")
        self.update_status("正在查找文件...", 10)

        # 在工作线程中启动处理
        thread = threading.Thread(target=self.process_directory, args=(work_dir,))
        thread.daemon = True
        thread.start()

    def stop_processing(self):
        """停止处理"""
        # 如果正在处理,设置取消标志
        if self.is_processing:
            self.cancel_requested = True
            self.log_message("用户请求停止处理", "warning")

    def complete_processing(self, success=True):
        """完成处理"""

        def update():
            # 重置处理状态
            self.is_processing = False
            # 启用开始按钮
            self.start_btn.config(state=tk.NORMAL)

            # 根据处理结果更新状态
            if success and not self.cancel_requested:
                self.log_message("处理完成", "success")
            elif self.cancel_requested:
                self.log_message("用户请求停止处理", "warning")
            else:
                self.log_message("处理失败", "error")

        # 在主线程中完成处理
        self.root.after(0, update)

    def process_directory(self, work_dir):
        """处理目录"""
        # 记录开始时间
        start_time = time.time()

        try:
            # 记录处理开始日志
            self.log_message(f"开始处理目录: {work_dir}", "info")
            self.update_status("正在查找文件...", 10)

            # 查找模板文件
            template_files = []

            # 遍历工作目录中的文件
            for file in os.listdir(work_dir):
                # 只处理.xlsx文件
                if file.lower().endswith('.xlsx'):
                    file_path = os.path.join(work_dir, file)
                    # 如果文件名以'1'开头,则为模板文件
                    if file.startswith('1'):
                        template_files.append(file_path)

            # 记录找到的文件数量
            self.log_message(f"找到模板文件: {len(template_files)}个", "success")

            # 检查是否有模板文件
            if not template_files:
                self.log_message("错误: 未找到以'1'开头的模板文件!", "error")
                self.complete_processing(False)
                return

            # 处理每个模板文件
            success_count = 0
            total_templates = len(template_files)

            for i, template_file in enumerate(template_files):
                # 检查是否请求取消
                if self.cancel_requested:
                    break

                # 计算当前处理进度
                current = i + 1
                progress = 20 + (current / total_templates * 70)

                # 更新状态和日志
                self.update_status(f"处理文件 {current}/{total_templates}...", progress)
                self.log_message(
                    f"处理文件 {current}: {os.path.basename(template_file)}",
                    "info")

                try:
                    # 处理单个模板文件
                    result_file = self.process_single_template(template_file, work_dir)
                    if result_file:
                        success_count += 1
                        self.log_message(f"✓ 成功生成: {os.path.basename(result_file)}", "success")
                except Exception as e:
                    self.log_message(f"✗ 处理失败: {str(e)}", "error")

            # 计算处理耗时
            elapsed_time = int(time.time() - start_time)

            # 根据处理结果记录日志
            if self.cancel_requested:
                self.log_message(f"处理取消,用时 {elapsed_time}秒,成功生成 {success_count} 个文件", "warning")
            else:
                self.log_message(f"处理完成,用时 {elapsed_time}秒,成功生成 {success_count} 个文件", "success")
                self.update_stats(files=success_count)

            # 完成处理
            self.complete_processing(success_count > 0)

        except Exception as e:
            # 记录错误日志
            self.log_message(f"处理过程中出现未预期错误: {str(e)}", "error")
            self.complete_processing(False)

    def process_single_template(self, template_file, work_dir):
        """处理单个模板文件"""
        try:
            # 1. 模板操作:加载模板文件
            template_wb = load_workbook(template_file, data_only=True)
            template_ws = template_wb.active

            # 读取表头:从特定单元格读取内容
            header_cells = ['C2', 'B3', 'D3', 'F3', 'B4', 'D4', 'F4', 'F5']
            header_array = []
            for cell_addr in header_cells:
                cell = template_ws[cell_addr]
                header_array.append(cell.value if cell.value is not None else "")

            # 读取页数数组:从H列第7行开始读取,确保每个模板行都有对应的页数
            page_numbers = []
            for row in range(7, template_ws.max_row + 1):
                cell_value = template_ws[f'H{row}'].value
                try:
                    page_num = int(round(float(cell_value))) if cell_value is not None else 0
                    page_numbers.append(max(0, page_num))  # 确保不为负数
                except:
                    page_numbers.append(0)

            # 读取强制分卷标记:从I列读取
            force_breaks = {}
            force_page_breaks = {}
            for r in range(7, template_ws.max_row + 1):
                cell_value = template_ws.cell(row=r, column=9).value  # I列
                if cell_value is not None and str(cell_value).strip():
                    cell_str = str(cell_value).strip()
                    if cell_str == "分卷":
                        force_breaks[r] = cell_str
                    elif cell_str == "分页":
                        force_page_breaks[r] = cell_str
            # 获取项目名称:从C2单元格读取(用于结果文件名)
            project_name = str(template_ws['C2'].value or "项目目录").strip()
            # 获取文件夹名称:从D2单元格读取
            folder_name = str(template_ws['D2'].value or project_name).strip()

            # 创建"待复制文件":从模板的A1:G7区域创建
            source_wb = Workbook()
            source_ws = source_wb.active
            source_ws.title = "目录"

            # 从模板复制A1:G7区域到源工作表
            for row in range(1, 8):  # 1 to 7
                for col in range(1, 8):  # A to G
                    source_cell = template_ws.cell(row=row, column=col)
                    target_cell = source_ws.cell(row=row, column=col)

                    # 复制值
                    target_cell.value = source_cell.value

                    # 复制样式
                    if source_cell.has_style:
                        target_cell.font = copy.copy(source_cell.font)
                        target_cell.border = copy.copy(source_cell.border)
                        target_cell.fill = copy.copy(source_cell.fill)
                        target_cell.number_format = source_cell.number_format
                        target_cell.protection = copy.copy(source_cell.protection)
                        target_cell.alignment = copy.copy(source_cell.alignment)

            # 复制合并单元格
            for merged_range in template_ws.merged_cells.ranges:
                # 检查是否在A1:G7范围内
                if merged_range.min_row <= 7 and merged_range.max_row <= 7 and \
                        merged_range.min_col <= 7 and merged_range.max_col <= 7:
                    source_ws.merge_cells(str(merged_range))

            # 复制列宽
            for col in range(1, 8):  # A to G
                col_letter = get_column_letter(col)
                if col_letter in template_ws.column_dimensions:
                    source_ws.column_dimensions[col_letter].width = template_ws.column_dimensions[col_letter].width

            # 复制行高
            for row in range(1, 8):  # 1 to 7
                if row in template_ws.row_dimensions:
                    source_ws.row_dimensions[row].height = template_ws.row_dimensions[row].height

            # 创建结果工作簿
            result_wb = Workbook()
            result_ws = result_wb.active
            result_ws.title = "目录"

            # 执行写入程序
            output_path = self.execute_writing_program(
                template_ws=template_ws,
                source_ws=source_ws,
                result_ws=result_ws,
                header_array=header_array,
                page_numbers=page_numbers,
                force_breaks=force_breaks,
                force_page_breaks=force_page_breaks,
                project_name=project_name,
                folder_name=folder_name,
                source_file=template_file,
                work_dir=work_dir,
                result_wb=result_wb
            )

            return output_path

        except Exception as e:
            raise Exception(f"处理模板文件失败: {str(e)}")

    def execute_writing_program(self, template_ws, source_ws, result_ws, header_array,
                                page_numbers, force_breaks, force_page_breaks, project_name, folder_name, source_file,
                                work_dir, result_wb):
        """执行写入程序 - 核心逻辑(修正版)"""
        try:
            # 初始化变量
            xh = 1  # 序号
            ym = 1  # 目录的第几页
            jh = 1  # 当前卷号
            zjh = 1  # 总卷号
            yy = 250  # 每卷的最大允许页号
            HGhj = 0  # 每页的行高合计
            yhhj = 1  # 档案在当前卷的第几页就是页号
            yhj = 462  # 每页允许磅值
            self.ymhg = 15  # 页码的行高 - 修复:定义为实例变量

            # 准备模板数据
            template_data = []
            for row in range(7, template_ws.max_row + 1):
                row_data = []
                for col in range(1, 8):  # A到G列
                    cell_value = template_ws.cell(row=row, column=col).value
                    row_data.append(cell_value)
                template_data.append(row_data)

            total_rows = len(template_data)
            self.log_message(f"读取模板数据完成,共 {total_rows} 行", "info")

            # 先分析所有行的类型:说明行/序号行/其他
            row_types = []
            for i, row_data in enumerate(template_data):
                d_content = str(row_data[3] or "").strip()  # D列
                is_unit_row = self.is_unit_row(d_content)

                if is_unit_row:
                    # 优先判断为说明行
                    row_types.append(('unit', i))
                else:
                    a_content = row_data[0]
                    is_number_row = self.is_number_row(a_content)
                    if is_number_row:
                        row_types.append(('number', i))
                    else:
                        row_types.append(('other', i))

            # 过滤掉其他类型的行,只保留说明行和序号行
            filtered_rows = [(rtype, idx) for rtype, idx in row_types if rtype in ['unit', 'number']]

            # 写入程序主循环
            template_index = 0  # 模板数据索引
            result_start_row = 1  # 结果文件开始行
            rows_written = 0  # 已写入行数
            pages_written = 0  # 已写入页数
            volumes_written = 1  # 已写入卷数

            # 主循环:处理所有模板数据
            while template_index < len(filtered_rows):
                if self.cancel_requested:
                    break

                self.log_message(f"开始第 {pages_written + 1} 页写入", "info")

                # 1. 复制复制页
                current_page_ws = self.create_copy_page(source_ws)

                # 2. 表头写入
                self.write_header_to_page(current_page_ws, header_array)

                # 3. 初始化当前页的写入位置
                current_page_row = 7  # 从第7行开始写入

                # 当前页的变量
                current_page_HGhj = 0

                # 循环行写入
                while template_index < len(filtered_rows):
                    if self.cancel_requested:
                        break

                    row_type, original_idx = filtered_rows[template_index]
                    row_data = template_data[original_idx]
                    template_row_num = original_idx + 7

                    if row_type == 'unit':
                        # 说明行处理
                        self.write_unit_row(current_page_ws, current_page_row, str(row_data[3]))

                        # 计算行高并累计(参与行高判断)
                        row_height = self.calculate_row_height(row_data)
                        current_page_ws.row_dimensions[current_page_row].height = row_height
                        current_page_HGhj += row_height
                        HGhj += row_height

                        # 分离判断(参与分离判断)
                        force_break = force_breaks.get(template_row_num, "")
                        force_page_break = force_page_breaks.get(template_row_num, "")  # 使用传入的参数

                        # 检查是否写完所有数据(结尾操作)
                        if template_index == len(filtered_rows) - 1:
                            # 结尾操作
                            wc = self.calculate_wc(yhj, HGhj)
                            self.perform_end_operation(
                                current_page_ws, current_page_row,
                                wc, ym, jh
                            )

                            # 复制当前页到结果文件
                            self.copy_page_to_result(current_page_ws, result_ws, result_start_row)
                            result_start_row += self.get_page_height(current_page_ws)
                            pages_written += 1

                            template_index += 1
                            break

                        # 检查是否需要强制分卷
                        elif force_break == "分卷":  # 修改:检查是否为分卷
                            # 分卷操作
                            wc = self.calculate_wc(yhj, HGhj)
                            self.perform_volume_break(
                                current_page_ws, current_page_row,
                                wc, ym, jh
                            )

                            # 复制当前页到结果文件
                            self.copy_page_to_result(current_page_ws, result_ws, result_start_row)
                            result_start_row += self.get_page_height(current_page_ws)
                            pages_written += 1
                            volumes_written += 1

                            # 重置变量
                            xh = 1
                            yhhj = 1
                            ym = 1
                            jh += 1
                            zjh = jh  # 更新总卷号
                            HGhj = 0

                            template_index += 1
                            break

                        # 检查是否需要强制分页
                        elif force_page_break == "分页":  # 新增:检查是否为分页
                            # 分页操作
                            wc = self.calculate_wc(yhj, HGhj)
                            self.perform_page_break(
                                current_page_ws, current_page_row,
                                wc, ym, jh
                            )

                            # 复制当前页到结果文件
                            self.copy_page_to_result(current_page_ws, result_ws, result_start_row)
                            result_start_row += self.get_page_height(current_page_ws)
                            pages_written += 1

                            # 更新变量
                            ym += 1
                            HGhj = 0

                            template_index += 1
                            break

                        # 检查是否需要自动分卷
                        elif yhhj > yy:
                            # 分卷操作
                            wc = self.calculate_wc(yhj, HGhj)
                            self.perform_volume_break(
                                current_page_ws, current_page_row,
                                wc, ym, jh
                            )

                            # 复制当前页到结果文件
                            self.copy_page_to_result(current_page_ws, result_ws, result_start_row)
                            result_start_row += self.get_page_height(current_page_ws)
                            pages_written += 1
                            volumes_written += 1

                            # 重置变量
                            xh = 1
                            yhhj = 1
                            ym = 1

                            jh += 1
                            zjh = jh  # 更新总卷号
                            HGhj = 0

                            template_index += 1
                            break

                        # 检查是否需要自动分页
                        elif HGhj >= yhj:
                            # 计算wc值
                            wc = self.calculate_wc(yhj, HGhj)
                            # 分页操作 - 传递计算后的wc值
                            self.perform_page_break(
                                current_page_ws, current_page_row,
                                wc, ym, jh
                            )

                            # 复制当前页到结果文件
                            self.copy_page_to_result(current_page_ws, result_ws, result_start_row)
                            result_start_row += self.get_page_height(current_page_ws)
                            pages_written += 1

                            # 更新变量
                            ym += 1
                            HGhj = 0

                            template_index += 1
                            break

                        # 继续当前页
                        current_page_row += 1
                        rows_written += 1
                        template_index += 1

                    elif row_type == 'number':
                        # 序号行处理
                        # 获取当前行对应的页数 - 使用original_idx作为索引
                        page_increment = 0
                        if original_idx < len(page_numbers):
                            page_increment = page_numbers[original_idx]

                        # 计算实际应该显示的页号(当前累计值)
                        actual_page_number = yhhj

                        # 序号行处理 - 传递页数增量信息
                        self.write_number_row(
                            current_page_ws, current_page_row, row_data,
                            xh, actual_page_number, page_increment  # 传递累加后的页号和页数增量
                        )

                        # 更新序号 - 只在序号行时更新
                        xh += 1
                        rows_written += 1

                        # 计算行高
                        row_height = self.calculate_row_height(row_data)

                        # 设置行高并累计
                        current_page_ws.row_dimensions[current_page_row].height = row_height
                        current_page_HGhj += row_height
                        HGhj += row_height

                        # 累加页号到累计值中 - 直接使用original_idx作为索引
                        if original_idx < len(page_numbers):
                            yhhj += page_increment

                        # 分离判断
                        force_break = force_breaks.get(template_row_num, "")
                        force_page_break = force_page_breaks.get(template_row_num, "")  # 使用传入的参数

                        # 检查是否写完所有数据
                        if template_index == len(filtered_rows) - 1:
                            # 结尾操作
                            wc = self.calculate_wc(yhj, HGhj)
                            self.perform_end_operation(
                                current_page_ws, current_page_row,
                                wc, ym, jh
                            )

                            # 复制当前页到结果文件
                            self.copy_page_to_result(current_page_ws, result_ws, result_start_row)
                            result_start_row += self.get_page_height(current_page_ws)
                            pages_written += 1

                            template_index += 1
                            break

                        # 检查是否需要强制分卷
                        elif force_break == "分卷":  # 修改:检查是否为分卷
                            # 分卷操作
                            wc = self.calculate_wc(yhj, HGhj)
                            self.perform_volume_break(
                                current_page_ws, current_page_row,
                                wc, ym, jh
                            )

                            # 复制当前页到结果文件
                            self.copy_page_to_result(current_page_ws, result_ws, result_start_row)
                            result_start_row += self.get_page_height(current_page_ws)
                            pages_written += 1
                            volumes_written += 1

                            # 重置变量
                            xh = 1
                            yhhj = 1
                            ym = 1

                            jh += 1
                            zjh = jh  # 更新总卷号
                            HGhj = 0

                            template_index += 1
                            break

                        # 检查是否需要强制分页
                        elif force_page_break == "分页":  # 新增:检查是否为分页
                            # 分页操作
                            wc = self.calculate_wc(yhj, HGhj)
                            self.perform_page_break(
                                current_page_ws, current_page_row,
                                wc, ym, jh
                            )

                            # 复制当前页到结果文件
                            self.copy_page_to_result(current_page_ws, result_ws, result_start_row)
                            result_start_row += self.get_page_height(current_page_ws)
                            pages_written += 1

                            # 更新变量
                            ym += 1
                            HGhj = 0

                            template_index += 1
                            break

                        # 检查是否需要自动分卷
                        elif yhhj > yy:
                            # 分卷操作
                            wc = self.calculate_wc(yhj, HGhj)
                            self.perform_volume_break(
                                current_page_ws, current_page_row,
                                wc, ym, jh
                            )

                            # 复制当前页到结果文件
                            self.copy_page_to_result(current_page_ws, result_ws, result_start_row)
                            result_start_row += self.get_page_height(current_page_ws)
                            pages_written += 1
                            volumes_written += 1

                            # 重置变量
                            xh = 1
                            yhhj = 1
                            ym = 1

                            jh += 1
                            zjh = jh  # 更新总卷号
                            HGhj = 0

                            template_index += 1
                            break

                        # 检查是否需要自动分页
                        elif HGhj >= yhj:
                            # 计算wc值
                            wc = self.calculate_wc(yhj, HGhj)
                            # 分页操作 - 传递计算后的wc值
                            self.perform_page_break(
                                current_page_ws, current_page_row,
                                wc, ym, jh
                            )

                            # 复制当前页到结果文件
                            self.copy_page_to_result(current_page_ws, result_ws, result_start_row)
                            result_start_row += self.get_page_height(current_page_ws)
                            pages_written += 1

                            # 更新变量
                            ym += 1
                            HGhj = 0

                            template_index += 1
                            break

                        else:
                            # 继续当前页
                            current_page_row += 1
                            template_index += 1

                    else:
                        # 其他类型行,跳过
                        template_index += 1

                    # 更新进度
                    if template_index % 10 == 0:
                        progress = 30 + (template_index / len(filtered_rows) * 60)
                        self.update_status(f"写入数据: {template_index}/{len(filtered_rows)}", progress)
                        self.update_stats(0, rows_written, pages_written, volumes_written)

            # 替换总卷号(在所有数据处理完成后)
            # 遍历整个结果工作表,替换所有zjh占位符为总卷号
            for row in result_ws.iter_rows():
                for cell in row:
                    if cell.value and isinstance(cell.value, str):
                        new_value = str(cell.value)
                        # 替换zjh(总卷号,不区分大小写)
                        if 'zjh' in new_value.lower():
                            new_value = new_value.replace('zjh', str(zjh))
                            new_value = new_value.replace('ZJH', str(zjh))
                            new_value = new_value.replace('Zjh', str(zjh))
                        if new_value != str(cell.value):
                            cell.value = new_value
                            # 替换 "第 - 卷" 为 "第 jh 卷"
                        if '第 - 卷' in new_value:
                            new_value = new_value.replace('第 - 卷', f'第 {jh} 卷')
                            # 替换 "共 1 卷" 为 "共 zjh 卷"
                        if '共 1 卷' in new_value:
                            new_value = new_value.replace('共 1 卷', f'共 {zjh} 卷')
                        if new_value != str(cell.value):
                            cell.value = new_value
            # 更新统计
            self.update_stats(0, rows_written, pages_written, volumes_written)

            # 保存文件
            output_path = self.save_output_file(work_dir, folder_name, project_name, source_file, result_wb)

            self.log_message(f"写入完成: {rows_written}行,{pages_written}页,{volumes_written}卷", "success")

            return output_path

        except Exception as e:
            raise Exception(f"写入程序执行失败: {str(e)}")

    def replace_volume_numbers_fixed(self, worksheet, current_jh, total_jh):
        """替换卷号 - 修正版(确保zjh被替换)"""
        for row in worksheet.iter_rows():
            for cell in row:
                if cell.value and isinstance(cell.value, str):
                    # 替换jh和zjh(不区分大小写)
                    new_value = str(cell.value)

                    # 先替换zjh(总卷号)
                    if 'zjh' in new_value.lower():
                        new_value = new_value.replace('zjh', str(total_jh))
                        new_value = new_value.replace('ZJH', str(total_jh))
                        new_value = new_value.replace('Zjh', str(total_jh))

                    # 然后替换jh(当前卷号)- 注意避免替换已替换的zjh部分
                    if 'jh' in new_value.lower():
                        # 使用正则表达式确保只替换单独的jh
                        import re
                        new_value = re.sub(r'(?<!z)(?<!Z)jh', str(current_jh), new_value, flags=re.IGNORECASE)

                    if new_value != str(cell.value):
                        cell.value = new_value

    def is_unit_row(self, d_content):
        """判断是否为说明行"""
        if not d_content:
            return False

        # 字数小于10个字符且包含"单元"
        char_count = len(d_content)
        if char_count < 10 and "单元" in d_content:
            return True
        return False

    def is_number_row(self, a_content):
        """判断是否为数字行"""
        if a_content is None:
            return False

        try:
            float(str(a_content))
            return True
        except:
            return False

    def create_copy_page(self, source_ws):
        """创建复制页(安全复制样式和列宽)"""
        # 创建新的工作簿和工作表
        copy_wb = Workbook()
        copy_ws = copy_wb.active

        # 复制单元格内容和样式
        for row in source_ws.iter_rows():
            for source_cell in row:
                target_cell = copy_ws.cell(
                    row=source_cell.row,
                    column=source_cell.column,
                    value=source_cell.value
                )

                # 安全复制样式(避免StyleProxy问题)
                self.safe_copy_style(source_cell, target_cell)

        # 复制合并单元格
        for merged_range in source_ws.merged_cells.ranges:
            copy_ws.merge_cells(str(merged_range))

        # 复制列宽(修正版)
        for col in range(1, source_ws.max_column + 1):
            col_letter = get_column_letter(col)
            if col_letter in source_ws.column_dimensions:
                source_col_dim = source_ws.column_dimensions[col_letter]
                # 直接设置目标列的宽度
                copy_ws.column_dimensions[col_letter].width = source_col_dim.width

        # 复制行高
        for row in range(1, source_ws.max_row + 1):
            if row in source_ws.row_dimensions:
                source_row_dim = source_ws.row_dimensions[row]
                if source_row_dim.height is not None:
                    copy_ws.row_dimensions[row].height = source_row_dim.height

        # 复制打印区域,如果有的话
        try:
            if source_ws.print_area:
                copy_ws.print_area = source_ws.print_area
        except:
            # 忽略打印区域复制错误
            pass

        return copy_ws

    def safe_copy_style(self, source_cell, target_cell):
        """安全复制样式(避免StyleProxy和MergedCell问题)"""
        try:
            # 检查是否为合并单元格
            if hasattr(source_cell, 'merged') and source_cell.merged:
                return

            # 复制字体
            if hasattr(source_cell, 'font') and source_cell.font:
                try:
                    target_cell.font = Font(
                        name=source_cell.font.name,
                        size=source_cell.font.size,
                        bold=source_cell.font.bold,
                        italic=source_cell.font.italic,
                        vertAlign=source_cell.font.vertAlign,
                        underline=source_cell.font.underline,
                        strike=source_cell.font.strike,
                        color=source_cell.font.color
                    )
                except:
                    # 如果复制失败,使用默认字体
                    target_cell.font = Font(name='宋体', size=11)

            # 复制对齐方式
            if hasattr(source_cell, 'alignment') and source_cell.alignment:
                try:
                    target_cell.alignment = Alignment(
                        horizontal=source_cell.alignment.horizontal,
                        vertical=source_cell.alignment.vertical,
                        text_rotation=source_cell.alignment.textRotation,
                        wrap_text=source_cell.alignment.wrapText,
                        shrink_to_fit=source_cell.alignment.shrinkToFit,
                        indent=source_cell.alignment.indent
                    )
                except:
                    target_cell.alignment = Alignment(horizontal='left', vertical='center')

            # 复制边框
            if hasattr(source_cell, 'border') and source_cell.border:
                try:
                    border = Border(
                        left=Side(style=source_cell.border.left.style,
                                  color=source_cell.border.left.color),
                        right=Side(style=source_cell.border.right.style,
                                   color=source_cell.border.right.color),
                        top=Side(style=source_cell.border.top.style,
                                 color=source_cell.border.top.color),
                        bottom=Side(style=source_cell.border.bottom.style,
                                    color=source_cell.border.bottom.color)
                    )
                    target_cell.border = border
                except:
                    pass

            # 复制填充
            if hasattr(source_cell, 'fill') and source_cell.fill:
                try:
                    target_cell.fill = PatternFill(
                        fill_type=source_cell.fill.fill_type,
                        start_color=source_cell.fill.start_color,
                        end_color=source_cell.fill.end_color
                    )
                except:
                    pass
        except Exception as e:
            # 捕获所有异常,确保即使遇到合并单元格也能继续执行
            pass

    def write_header_to_page(self, worksheet, header_array):
        """将表头写入复制页"""
        header_mapping = [
            ('C2', 0), ('B3', 1), ('D3', 2), ('F3', 3),
            ('B4', 4), ('D4', 5), ('F4', 6), ('F5', 7)
        ]

        for cell_addr, idx in header_mapping:
            if idx < len(header_array):
                cell = worksheet[cell_addr]
                # 保持原格式,只更新值
                cell.value = header_array[idx]

    def write_unit_row(self, worksheet, row, d_content):
        """写入说明行"""
        # D列内容
        cell_d = worksheet.cell(row=row, column=4)
        cell_d.value = d_content

        # 从模板的第7行获取D列的原始格式
        ref_cell = worksheet.cell(row=7, column=4)
        if ref_cell.font:
            cell_d.font = copy.copy(ref_cell.font)
        if ref_cell.alignment:
            cell_d.alignment = copy.copy(ref_cell.alignment)
        # 添加细边框
        cell_d.border = Border(
            left=Side(style='thin'),
            right=Side(style='thin'),
            top=Side(style='thin'),
            bottom=Side(style='thin')
        )

        # A列和F列为空
        worksheet.cell(row=row, column=1, value=None)
        worksheet.cell(row=row, column=6, value=None)

        # B列、C列、E列和G列也设为空
        worksheet.cell(row=row, column=2, value=None)
        worksheet.cell(row=row, column=3, value=None)
        worksheet.cell(row=row, column=5, value=None)
        worksheet.cell(row=row, column=7, value=None)

        # 行高由主循环计算并设置,这里不再设置
        # 为A到G列设置细边框和自动换行
        for col in range(1, 8):  # A到G列
            cell = worksheet.cell(row=row, column=col)
            cell.font = Font(name='宋体', size=12, bold=True)
            cell.border = Border(
                left=Side(style='thin'),
                right=Side(style='thin'),
                top=Side(style='thin'),
                bottom=Side(style='thin')
            )
            # 设置自动换行
            if cell.alignment:
                cell.alignment = Alignment(
                    horizontal=cell.alignment.horizontal,
                    vertical=cell.alignment.vertical,
                    wrap_text=True
                )
            else:
                cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)

    def write_number_row(self, worksheet, row, row_data, xh, actual_page_number, page_increment=0):
        """写入序号行 - 修正版"""
        # A列:序号
        cell_a = worksheet.cell(row=row, column=1)
        cell_a.value = xh
        # 设置A列格式
        cell_a.font = Font(name='宋体', size=11)
        cell_a.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)
        cell_a.border = Border(
            left=Side(style='thin'),
            right=Side(style='thin'),
            top=Side(style='thin'),
            bottom=Side(style='thin')
        )

        # B-G列:内容(从row_data[1]到row_data[6],对应B、C、D、E、F、G列)
        # 注意:row_data[0]是模板的A列内容(已经处理为序号),这里从B列开始
        # 修复:正确的列映射
        for col in range(2, 8):  # B到G列(2-7)
            data_index = col - 1  # 因为A列已经单独处理,所以从索引0开始对应B列
            if data_index < len(row_data):  # 确保数据索引不超出范围
                value = row_data[data_index]  # col-1因为row_data包含A到G列
                cell = worksheet.cell(row=row, column=col)
                cell.value = value

                # 设置对应列格式
                if col == 2:  # B列
                    cell.font = Font(name='宋体', size=10)
                else:  # C, D, E, F, G列
                    cell.font = Font(name='宋体', size=11)

                cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)
                cell.border = Border(
                    left=Side(style='thin'),
                    right=Side(style='thin'),
                    top=Side(style='thin'),
                    bottom=Side(style='thin')
                )

        # F列特殊处理:页号 - 使用实际累加的页号
        cell_f = worksheet.cell(row=row, column=6)

        # 根据"显示页号起止"复选框状态决定F列的值
        if self.show_page_range.get():  # 如果复选框被选中
            # 将页数增量转换为整数,避免小数点
            page_inc_int = int(round(page_increment)) if page_increment else 0

            if page_inc_int > 1:
                # 计算终止页号(必须为正整数)
                end_page = int(actual_page_number) + page_inc_int - 1
                cell_f.value = f"{int(actual_page_number)}-{end_page}"
            else:
                cell_f.value = int(actual_page_number)
        else:
            cell_f.value = int(actual_page_number)

        # 设置F列格式
        cell_f.font = Font(name='宋体', size=11)
        cell_f.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)
        cell_f.border = Border(
            left=Side(style='thin'),
            right=Side(style='thin'),
            top=Side(style='thin'),
            bottom=Side(style='thin')
        )

        # 确保G列(第7列)也有内容和格式
        if 7 <= len(row_data):  # 确保有G列数据
            cell_g = worksheet.cell(row=row, column=7)
            if cell_g.value is None:  # 如果还没设置值
                cell_g.value = row_data[6]  # G列数据
                cell_g.font = Font(name='宋体', size=11)
                cell_g.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)
                cell_g.border = Border(
                    left=Side(style='thin'),
                    right=Side(style='thin'),
                    top=Side(style='thin'),
                    bottom=Side(style='thin')
                )

        # 为所有单元格确保边框和自动换行
        for col in range(1, 8):  # A到G列
            cell = worksheet.cell(row=row, column=col)
            if cell.border is None or not cell.border:
                cell.border = Border(
                    left=Side(style='thin'),
                    right=Side(style='thin'),
                    top=Side(style='thin'),
                    bottom=Side(style='thin')
                )
            # 确保自动换行
            if cell.alignment:
                cell.alignment = Alignment(
                    horizontal=cell.alignment.horizontal,
                    vertical=cell.alignment.vertical,
                    wrap_text=True
                )
            else:
                cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)
    def calculate_row_height(self, row_data):
        """计算行高"""
        # D列内容
        d_content = str(row_data[3] or "")
        d_char_count = self.calculate_char_count(d_content)

        # B列内容
        b_content = str(row_data[1] or "")
        b_char_count = self.calculate_char_count(b_content)

        # C列内容
        c_content = str(row_data[2] or "")
        c_char_count = self.calculate_char_count(c_content)

        # 计算D列行高
        if d_char_count <= 15:
            d_height = 25
        elif d_char_count <= 30:
            d_height = 40
        elif d_char_count <= 45:
            d_height = 55
        else:
            levels = math.ceil(d_char_count / 15)
            d_height = 15 * levels + 10

        # 计算B列行高
        if b_char_count <= 12:
            b_height = 25
        elif b_char_count <= 17:
            b_height = 40
        elif b_char_count <= 22:
            b_height = 55
        else:
            levels = math.ceil(b_char_count / 6)
            b_height = 10 * levels + 15

        # 计算C列行高
        if c_char_count <= 4:
            c_height = 25
        else:
            levels = math.ceil(c_char_count / 4)
            c_height = 10 * levels + 10

        # 取最大值,至少25磅
        return max(d_height, b_height, c_height, 25)

    def calculate_char_count(self, text):
        """计算有效字符数"""
        if not text:
            return 0

        char_count = 0
        for char in str(text):
            if char.isascii() and char.isalnum():
                char_count += 0.5
            elif char in '.,;:!?()[]{}"\'-':
                char_count += 1  # 标点符号占一个字的位置
            else:
                char_count += 1

        return char_count

    def calculate_wc(self, yhj, HGhj):
        """计算wc值:(yhj-HGhj)/25的结果四舍五入取整数"""
        wc = max(0, round((yhj - HGhj) / 25))
        return int(wc)

    def get_page_height(self, worksheet):
        """获取工作表的高度(实际使用的行数)"""
        # 从最后一行向上查找,找到第一个有内容的行
        for row in range(worksheet.max_row, 0, -1):
            for col in range(1, worksheet.max_column + 1):
                cell = worksheet.cell(row=row, column=col)
                if cell.value is not None:
                    return row
        return 0

    def perform_end_operation(self, worksheet, current_row, wc, ym, jh):
        """结尾操作"""
        # 保留当前写入行向下wc行的内容和格式,清除wc行以下的内容和格式

        # 计算保留的范围:当前行 + 1 到 current_row + wc
        # 计算需要清除的起始行:current_row + wc + 1
        delete_start_row = current_row + wc + 1

        # 保留的wc行设置行高25并添加细边框
        for row in range(current_row + 1, delete_start_row):
            worksheet.row_dimensions[row].height = 25
            for col in range(1, 8):  # A到G列
                cell = worksheet.cell(row=row, column=col)
                cell.border = Border(
                    left=Side(style='thin'),
                    right=Side(style='thin'),
                    top=Side(style='thin'),
                    bottom=Side(style='thin')
                )

        # 清除delete_start_row及以下的行内容和格式
        for row in range(delete_start_row, worksheet.max_row + 1):
            for col in range(1, worksheet.max_column + 1):
                cell = worksheet.cell(row=row, column=col)
                # 清除值和格式
                cell.value = None
                cell.font = Font()
                cell.alignment = Alignment()
                cell.border = Border()
                cell.fill = PatternFill()

        # 写入页码(在写入行的向下wc+1行的F列)
        page_row = current_row + wc + 1
        page_cell = worksheet.cell(row=page_row, column=6)
        page_cell.value = f"第{ym}页"
        page_cell.font = Font(name='宋体', size=12)
        page_cell.alignment = Alignment(horizontal='center')
        worksheet.row_dimensions[page_row].height = self.ymhg

        # 替换文中的页码和卷号占位符
        for row in worksheet.iter_rows():
            for cell in row:
                if cell.value and isinstance(cell.value, str):
                    new_value = str(cell.value)
                    # 替换页码占位符
                    if 'yh' in new_value.lower():
                        new_value = new_value.replace('yh', str(ym))
                        new_value = new_value.replace('YH', str(ym))
                        new_value = new_value.replace('Yh', str(ym))
                    # 替换 "第 - 卷" 为 "第 jh 卷"
                    if '第 - 卷' in new_value:
                        new_value = new_value.replace('第 - 卷', f'第 {jh} 卷')
                    # 替换 "共 1 卷" 为 "共 jh 卷"
                    if '共 1 卷' in new_value:
                        new_value = new_value.replace('共 1 卷', f'共 {jh} 卷')
                    if new_value != str(cell.value):
                        cell.value = new_value
    def perform_volume_break(self, worksheet, current_row, wc, ym, jh):
        """分卷操作"""
        # 保留当前写入行向下wc行的内容和格式,清除wc行以下的内容和格式

        # 计算保留的范围:当前行 + 1 到 current_row + wc
        # 计算需要清除的起始行:current_row + wc + 1
        delete_start_row = current_row + wc + 1

        # 保留的wc行设置行高25并添加细边框
        for row in range(current_row + 1, delete_start_row):
            worksheet.row_dimensions[row].height = 25
            for col in range(1, 8):  # A到G列
                cell = worksheet.cell(row=row, column=col)
                cell.border = Border(
                    left=Side(style='thin'),
                    right=Side(style='thin'),
                    top=Side(style='thin'),
                    bottom=Side(style='thin')
                )

        # 清除delete_start_row及以下的行内容和格式
        for row in range(delete_start_row, worksheet.max_row + 1):
            for col in range(1, worksheet.max_column + 1):
                cell = worksheet.cell(row=row, column=col)
                # 清除值和格式
                cell.value = None
                cell.font = Font()
                cell.alignment = Alignment()
                cell.border = Border()
                cell.fill = PatternFill()

        # 为保留的行添加细边框
        for row in range(current_row + 1, delete_start_row):
            for col in range(1, worksheet.max_column + 1):
                cell = worksheet.cell(row=row, column=col)
                cell.border = Border(
                    left=Side(style='thin'),
                    right=Side(style='thin'),
                    top=Side(style='thin'),
                    bottom=Side(style='thin')
                )

        # 写入页码
        page_row = current_row + wc + 1
        page_cell = worksheet.cell(row=page_row, column=6)
        page_cell.value = f"第{ym}页"
        page_cell.font = Font(name='宋体', size=12)
        page_cell.alignment = Alignment(horizontal='center')
        worksheet.row_dimensions[page_row].height = self.ymhg  # 修复:使用实例变量


        # 替换文中的yh占位符为当前页码
        for row in worksheet.iter_rows():
            for cell in row:
                if cell.value and isinstance(cell.value, str):
                    new_value = str(cell.value)
                    if 'yh' in new_value.lower():
                        new_value = new_value.replace('yh', str(ym))
                        new_value = new_value.replace('YH', str(ym))
                        new_value = new_value.replace('Yh', str(ym))
                    if new_value != str(cell.value):
                        cell.value = new_value
        # 替换文中的ym占位符为当前页码
        for row in worksheet.iter_rows():
            for cell in row:
                if cell.value and isinstance(cell.value, str):
                    new_value = str(cell.value)
                    # 替换ym(页码)
                    if 'ym' in new_value.lower():
                        new_value = new_value.replace('ym', str(ym))
                        new_value = new_value.replace('YM', str(ym))
                        new_value = new_value.replace('Ym', str(ym))
                    # 替换 "第 - 卷" 为 "第 jh 卷"
                    if '第 - 卷' in new_value:
                        new_value = new_value.replace('第 - 卷', f'第 {jh} 卷')
                    # 替换 "共 1 卷" 为 "共 jh 卷"

                    if new_value != str(cell.value):
                        cell.value = new_value
    def perform_page_break(self, worksheet, current_row, wc, ym, jh):
        """分页操作"""
        # 保留当前写入行向下wc行的内容和格式,清除wc行以下的内容和格式

        # 计算保留的范围:当前行 + 1 到 current_row + wc
        # 计算需要清除的起始行:current_row + wc + 1
        delete_start_row = current_row + wc + 1

        # 保留的wc行设置行高25并添加细边框
        for row in range(current_row + 1, delete_start_row):
            worksheet.row_dimensions[row].height = 25
            for col in range(1, 8):  # A到G列
                cell = worksheet.cell(row=row, column=col)
                cell.border = Border(
                    left=Side(style='thin'),
                    right=Side(style='thin'),
                    top=Side(style='thin'),
                    bottom=Side(style='thin')
                )

        # 清除delete_start_row及以下的行内容和格式
        for row in range(delete_start_row, worksheet.max_row + 1):
            for col in range(1, worksheet.max_column + 1):
                cell = worksheet.cell(row=row, column=col)
                # 清除值和格式
                cell.value = None
                cell.font = Font()
                cell.alignment = Alignment()
                cell.border = Border()
                cell.fill = PatternFill()

        # 为保留的行添加细边框
        for row in range(current_row + 1, delete_start_row):
            for col in range(1, worksheet.max_column + 1):
                cell = worksheet.cell(row=row, column=col)
                cell.border = Border(
                    left=Side(style='thin'),
                    right=Side(style='thin'),
                    top=Side(style='thin'),
                    bottom=Side(style='thin')
                )

        # 写入页码
        page_row = current_row + wc + 1
        page_cell = worksheet.cell(row=page_row, column=6)
        page_cell.value = f"第{ym}页"
        page_cell.font = Font(name='宋体', size=12)
        page_cell.alignment = Alignment(horizontal='center')
        worksheet.row_dimensions[page_row].height = self.ymhg  # 修复:使用实例变量

        # 替换卷号(在当前页中)
        self.replace_volume_numbers_in_worksheet(worksheet, jh)

        # 替换文中的yh占位符为当前页码
        for row in worksheet.iter_rows():
            for cell in row:
                if cell.value and isinstance(cell.value, str):
                    new_value = str(cell.value)
                    if 'yh' in new_value.lower():
                        new_value = new_value.replace('yh', str(ym))
                        new_value = new_value.replace('YH', str(ym))
                        new_value = new_value.replace('Yh', str(ym))
                    if new_value != str(cell.value):
                        cell.value = new_value
                    # 替换 "第 - 卷" 为 "第 jh 卷"
                    if '第 - 卷' in new_value:
                        new_value = new_value.replace('第 - 卷', f'第 {jh} 卷')

                    if new_value != str(cell.value):
                        cell.value = new_value
    def copy_page_to_result(self, source_ws, target_ws, start_row):
        """将复制页复制到结果文件"""
        # 计算源工作表的行数
        source_max_row = source_ws.max_row
        source_max_col = source_ws.max_column

        # 复制每一行
        for row in range(1, source_max_row + 1):
            for col in range(1, source_max_col + 1):
                source_cell = source_ws.cell(row=row, column=col)
                target_cell = target_ws.cell(row=start_row + row - 1, column=col)

                # 复制值和样式
                target_cell.value = source_cell.value
                self.safe_copy_style(source_cell, target_cell)

        # 复制合并单元格(需要调整行号)
        for merged_range in source_ws.merged_cells.ranges:
            # 解析合并范围
            min_row, min_col, max_row, max_col = merged_range.min_row, merged_range.min_col, merged_range.max_row, merged_range.max_col
            # 调整行号
            new_min_row = min_row + start_row - 1
            new_max_row = max_row + start_row - 1
            # 合并单元格
            target_ws.merge_cells(start_row=new_min_row, start_column=min_col,
                                  end_row=new_max_row, end_column=max_col)

        # 复制列宽
        for col in range(1, source_ws.max_column + 1):
            col_letter = get_column_letter(col)
            if col_letter in source_ws.column_dimensions:
                source_col_dim = source_ws.column_dimensions[col_letter]
                if source_col_dim.width is not None:
                    target_ws.column_dimensions[col_letter].width = source_col_dim.width

        # 复制行高(调整行号)
        for row in range(1, source_ws.max_row + 1):
            if row in source_ws.row_dimensions:
                source_row_dim = source_ws.row_dimensions[row]
                if source_row_dim.height is not None:
                    target_row = start_row + row - 1
                    target_ws.row_dimensions[target_row].height = source_row_dim.height

        # 复制分页符(调整行号)
        try:
            for break_info in source_ws.row_breaks:
                # 检查 break_info 的类型
                if isinstance(break_info, tuple):
                    # 如果是元组,假设第一个元素是行号
                    target_row = break_info[0] + start_row - 1
                elif hasattr(break_info, 'id'):
                    # 如果是对象,使用 id 属性
                    target_row = break_info.id + start_row - 1
                else:
                    # 其他情况,跳过
                    continue
                target_ws.row_breaks.append(Break(id=target_row))
        except Exception as e:
            # 捕获分页符复制错误,不影响整体处理
            pass

    def replace_volume_numbers(self, worksheet, jh):
        """替换卷号(在整个工作表中)"""
        for row in worksheet.iter_rows():
            for cell in row:
                if cell.value and isinstance(cell.value, str):
                    # 替换jh和zjh(不区分大小写)
                    new_value = str(cell.value)
                    new_value = re.sub(r'\bjh\b', str(jh), new_value, flags=re.IGNORECASE)
                    new_value = re.sub(r'\bzjh\b', str(jh), new_value, flags=re.IGNORECASE)
                    if new_value != str(cell.value):
                        cell.value = new_value

    def replace_volume_numbers_in_worksheet(self, worksheet, current_jh):
        """替换卷号(在单个工作表中)"""
        for row in worksheet.iter_rows():
            for cell in row:
                if cell.value and isinstance(cell.value, str):
                    # 替换jh(当前卷号,不区分大小写)
                    new_value = str(cell.value)
                    new_value = re.sub(r'\bjh\b', str(current_jh), new_value, flags=re.IGNORECASE)
                    if new_value != str(cell.value):
                        cell.value = new_value

    def add_page_breaks_to_result_file(self, workbook):
        """
        在结果文件中对所有"第几页"单元格下方添加分页符(除了最后一个)
        """
        worksheet = workbook.active

        # 找到所有包含"第几页"的单元格位置
        page_cells = []
        for row in worksheet.iter_rows():
            for cell in row:
                if cell.value and isinstance(cell.value, str) and "第" in cell.value and "页" in cell.value:
                    # 检查是否是"第X页"格式
                    match = re.search(r'第(\d+)页', cell.value)
                    if match:
                        page_cells.append((cell.row, cell.column))

        if not page_cells:
            return  # 没有找到页面标记

        # 对除了最后一个页面标记的所有页面标记下方添加分页符
        for page_row, page_col in page_cells[:-1]:  # 排除最后一个
            # 在页面标记的下一行添加分页符(我调整+1为+0)
            next_row = page_row + 0
            if next_row <= worksheet.max_row:  # 确保不超过工作表边界
                worksheet.row_breaks.append(Break(id=next_row))

    def save_output_file(self, work_dir, folder_name, project_name, source_file, result_wb):
        """保存输出文件"""
        # 清理文件夹名称
        clean_folder_name = re.sub(r'[<>:"/\\|?*]', '_', folder_name)
        clean_folder_name = clean_folder_name.strip()

        # 创建输出文件夹
        output_dir = os.path.join(work_dir, clean_folder_name)

        counter = 1
        original_dir = output_dir
        while os.path.exists(output_dir):
            output_dir = f"{original_dir}_{counter}"
            counter += 1

        os.makedirs(output_dir, exist_ok=True)

        # 生成文件名:待复制文件名称(模板C2内容)
        source_name = os.path.splitext(os.path.basename(source_file))[0]
        result_name = f"{source_name}({project_name}).xlsx"
        result_path = os.path.join(output_dir, result_name)

        # 处理文件名冲突
        counter = 1
        base_name, ext = os.path.splitext(result_name)
        while os.path.exists(result_path):
            result_path = os.path.join(output_dir, f"{base_name}_{counter}{ext}")
            counter += 1

        # 在保存前添加分页符到结果文件
        self.add_page_breaks_to_result_file(result_wb)

        # 设置打印区域
        result_ws = result_wb.active
        result_ws.print_area = 'A1:G{}'.format(result_ws.max_row)

        # 保存文件
        result_wb.save(result_path)

        self.log_message(f"文件已保存到: {result_path}", "success")

        return result_path


def main():
    """主函数"""
    root = tk.Tk()

    # 设置窗口居中
    window_width = 1000
    window_height = 800
    screen_width = root.winfo_screenwidth()
    screen_height = root.winfo_screenheight()
    center_x = int(screen_width / 2 - window_width / 2)
    center_y = int(screen_height / 2 - window_height / 2)
    root.geometry(f'{window_width}x{window_height}+{center_x}+{center_y}')

    # 设置最小尺寸
    root.minsize(800, 600)

    # 创建应用
    app = ProfessionalDirectoryGenerator(root)

    # 运行主循环
    root.mainloop()


if __name__ == "__main__":
    main()
相关推荐
鸠摩智首席音效师2 小时前
如何确定 Linux 下的文件系统类型 {Ext3, Ext4, XFS} ?
linux·运维·服务器
喵手2 小时前
Python爬虫实战:把“菜鸟教程”的知识树连根拔起(递归/遍历实战)(附 CSV 导出)!
爬虫·python·爬虫实战·python爬虫工程化实战·零基础python爬虫教学·菜鸟教程数据采集·采集菜鸟教程于csv
比奇堡鱼贩2 小时前
python第二次作业--函数
linux·运维·windows
七夜zippoe2 小时前
gRPC高性能RPC框架实战:从Protocol Buffers到流式传输的完整指南
网络·python·网络协议·rpc·protocol
claem2 小时前
Mac端 Python脚本创建与理解
开发语言·python·macos
RisunJan2 小时前
Linux命令-lilo(安装核心载入开机管理程序)
linux·运维·服务器
飞凌嵌入式2 小时前
1块集成了4核Cortex-A7高性能CPU、1颗RISC-V MCU、多种高速总线、还兼容树莓派的T153低成本开发板
linux·arm开发·嵌入式硬件·risc-v
舰长1152 小时前
ubuntu16 在防火墙禁止“允许Traceroute探测”
linux·运维·服务器
lixzest2 小时前
目标检测算法应用工程师 面试高频题 + 标准答案
python·yolo·目标检测·计算机视觉