python_生成RPA运行数据报告

python 复制代码
import os
def generate_rpa_report_html(
    title,
    key_metrics_data_dict,
    center_contributions,
    center_summary,
    process_structure,
    app_list,
    output_folder
):
    """
    生成自定义的RPA成果汇报HTML文件
    
    参数说明:
    title: 页面主标题
    subtitle: 页面副标题
    total_runs: 全量累计运行次数
    total_hours: 全量累计运行小时数
    total_save_days: 全量累计节省人天
    release_people: 折算释放人数
    center_contributions: 分中心贡献数据,格式示例:
        [
            {"name": "客服中心", "people": "≈ 2.8 人", "days": "650 人天", "percent": "100%"},
            {"name": "经营管理中心", "people": "≈ 1.8 人", "days": "420 人天", "percent": "65%"},
            ...
        ]
    process_structure: 流程类型结构数据,格式示例:
        [
            {"name": "数据类", "percent": "39.0%", "width": "39%", "color": "from-[#165DFF] to-[#4080FF]", "rounded": "rounded-l-lg"},
            {"name": "仓配类", "percent": "25.0%", "width": "25%", "color": "from-[#00B42A] to-[#2DCB54]", "rounded": ""},
            ...
        ]
    app_list: 重点应用清单数据,格式示例:
        [
            {"id":1, "name":"抖音订单自动签收流程", "type":"数据类", "typeBadge":"bg-blue-50 text-blue-600 border-blue-100", "hours":420, "days":"262.5", "efficiency":"1.05"},
            {"id":2, "name":"京东售后单据自动审核", "type":"售后类", "typeBadge":"bg-orange-50 text-orange-600 border-orange-100", "hours":380, "days":"237.5", "efficiency":"0.95"},
            ...
        ]
    """
    # 设置默认值,避免参数为空导致报错
    if center_contributions is None:
        center_contributions = [
            {"name": "客服中心", "people": "≈ 2.8 人", "days": "650 人天", "percent": "100%"},
            {"name": "经营管理中心", "people": "≈ 1.8 人", "days": "420 人天", "percent": "65%"},
            {"name": "供应链中心", "people": "≈ 0.9 人", "days": "210 人天", "percent": "32%"},
            {"name": "财务中心", "people": "≈ 0.6 人", "days": "150 人天", "percent": "23%"},
            {"name": "人力资源中心", "people": "≈ 0.3 人", "days": "80 人天", "percent": "12%"}
        ]
    
    if process_structure is None:
        process_structure = [
            {"name": "数据类", "percent": "39.0%", "width": "39%", "color": "from-[#165DFF] to-[#4080FF]", "rounded": "rounded-l-lg"},
            {"name": "仓配类", "percent": "25.0%", "width": "25%", "color": "from-[#00B42A] to-[#2DCB54]", "rounded": ""},
            {"name": "售后类", "percent": "20.0%", "width": "20%", "color": "from-[#FF7D00] to-[#FF9F40]", "rounded": ""},
            {"name": "财务类", "percent": "16.0%", "width": "16%", "color": "from-[#722ED1] to-[#9F5CE6]", "rounded": "rounded-r-lg"}
        ]
    
    if app_list is None:
        app_list = [
            {"id":1, "name":"抖音订单自动签收流程", "type":"数据类", "typeBadge":"bg-blue-50 text-blue-600 border-blue-100", "hours":420, "days":"262.5", "efficiency":"1.05"},
            {"id":2, "name":"京东售后单据自动审核", "type":"售后类", "typeBadge":"bg-orange-50 text-orange-600 border-orange-100", "hours":380, "days":"237.5", "efficiency":"0.95"},
            {"id":3, "name":"拼多多库存同步监控", "type":"仓配类", "typeBadge":"bg-green-50 text-green-600 border-green-100", "hours":310, "days":"193.8", "efficiency":"0.78"},
            {"id":4, "name":"每日销售报表自动生成", "type":"数据类", "typeBadge":"bg-blue-50 text-blue-600 border-blue-100", "hours":200, "days":"125.0", "efficiency":"0.50"},
            {"id":5, "name":"物流异常件自动预警", "type":"仓配类", "typeBadge":"bg-green-50 text-green-600 border-green-100", "hours":150, "days":"93.8", "efficiency":"0.38"},
            {"id":6, "name":"财务对账自动化", "type":"财务类", "typeBadge":"bg-purple-50 text-purple-600 border-purple-100", "hours":120, "days":"75.0", "efficiency":"0.30"},
            {"id":7, "name":"供应链采购单自动录入", "type":"仓配类", "typeBadge":"bg-green-50 text-green-600 border-green-100", "hours":110, "days":"68.8", "efficiency":"0.28"},
            {"id":8, "name":"员工入职账号自动开通", "type":"数据类", "typeBadge":"bg-blue-50 text-blue-600 border-blue-100", "hours":90, "days":"56.3", "efficiency":"0.23"},
            {"id":9, "name":"税务发票自动查验", "type":"财务类", "typeBadge":"bg-purple-50 text-purple-600 border-purple-100", "hours":85, "days":"53.1", "efficiency":"0.21"},
            {"id":10, "name":"客户评价自动汇总分析", "type":"售后类", "typeBadge":"bg-orange-50 text-orange-600 border-orange-100", "hours":70, "days":"43.8", "efficiency":"0.17"}
        ]
    
    # 构建分中心贡献HTML片段
    center_html = ""
    for item in center_contributions:
        center_html += f"""
        <div class="group">
            <div class="flex justify-between items-center mb-1">
                <span class="text-[13px] font-bold text-gray-700">{item['name']}</span>
                <span class="text-[13px] font-bold px-2 py-0.5 rounded bg-blue-50 text-blue-600 border-blue-100">{item['people']}</span>
            </div>
            <div class="flex items-center gap-3">
                <div class="flex-1">
                    <div class="h-2 w-full rounded-full overflow-hidden bg-gray-100/80 shadow-inner">
                        <div class="h-full rounded-full transition-all duration-1000 ease-out bg-gradient-to-r {item.get('color', 'from-[#165DFF] to-[#4080FF]')} shadow-sm" style="width: {item['percent']};">
                            <div class="w-full h-full bg-gradient-to-b from-white/20 to-transparent"></div>
                        </div>
                    </div>
                </div>
                <span class="text-[12px] font-mono font-medium text-gray-400 w-14 text-right tabular-nums">{item['days']}</span>
            </div>
        </div>
        """
    
    # 构建流程结构HTML片段
    process_html = ""
    process_legend_html = ""  # 新增:定义图例HTML变量
    for idx, item in enumerate(process_structure):
        process_html += f"""
        <div class="
            flex items-center justify-center relative group cursor-pointer bg-gradient-to-r {item['color']} hover:brightness-110 transition-all
            {item['rounded']}
        " style="width: {item['width']};">
            <div class="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent pointer-events-none {item['rounded']}"></div>
            <span class="relative z-10 text-[11px] font-bold text-white drop-shadow-md select-none whitespace-nowrap px-1"></span>
            <div class="opacity-0 group-hover:opacity-100 absolute bottom-full mb-3 bg-gray-800 text-white text-[12px] font-bold px-3 py-1.5 rounded-lg shadow-xl whitespace-nowrap transition-all duration-200 z-50 pointer-events-none transform translate-y-1 group-hover:translate-y-0">
                {item['name']} {item['percent']}
                <div class="absolute -bottom-1 left-1/2 -translate-x-1/2 w-2 h-2 bg-gray-800 rotate-45"></div>
            </div>
        </div>
        """

        # 新增:拼接每个流程类型的图例项
        process_legend_html += f"""
        <div class="flex items-center gap-1">
            <span class="w-3 h-3 bg-gradient-to-r {item['color']} rounded-full ring-1 ring-white"></span>
            <span>{item['name']}</span>
        </div>
        """
    
    # 构建应用列表JSON字符串(用于JS)
    import json
    app_json = json.dumps(app_list, ensure_ascii=False)
    
    # 完整HTML模板
    html_template = f"""
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{title}</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <style>
        body {{ font-family: sans-serif; background-color: #F5F7FA; }}
        * {{ box-sizing: border-box; }}
        ::-webkit-scrollbar {{ width: 6px; height: 6px; }}
        ::-webkit-scrollbar-thumb {{ background: #cbd5e1; border-radius: 3px; }}
        ::-webkit-scrollbar-track {{ background: transparent; }}
        /* 隐藏导出按钮 */
        #export-trigger-btn {{ display: none !important; }}
    </style>
</head>
<body class="min-h-screen">
    <div style="padding: 20px;">
        <div class="min-h-screen flex flex-col font-sans bg-slate-50 pb-8" style="background-color: rgb(245, 247, 250);">
            <header class="px-6 pt-6 pb-4 flex flex-col md:flex-row md:items-center justify-between gap-4">
                <div>
                    <h1 class="gradient-title font-extrabold text-[22px] tracking-tight bg-clip-text text-transparent bg-gradient-to-r from-gray-900 to-gray-600">{title}</h1>
                    <p class="text-[13px] text-gray-500 font-medium flex items-center gap-1 mt-1">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-trending-up w-3.5 h-3.5 text-blue-500" aria-hidden="true">
                            <path d="M16 7h6v6"></path>
                            <path d="m22 7-8.5 8.5-5-5L2 17"></path>
                        </svg>
                        企业自动化降本增效监控中心
                    </p>
                </div>
                <div class="flex gap-3 items-center">
                    <div class="flex gap-0.5 bg-white/60 p-0.5 rounded-lg border border-white shadow-sm backdrop-blur-sm">
                        <button id="tab-all" class="px-3 py-1.5 text-[12px] font-bold transition-all rounded-md bg-white text-[#165DFF] shadow-sm ring-1 ring-gray-100">全景视图</button>
                        <button id="tab-kpi" class="px-3 py-1.5 text-[12px] font-bold transition-all rounded-md text-gray-500 hover:text-gray-700 hover:bg-gray-50/50">关键指标</button>
                        <button id="tab-center" class="px-3 py-1.5 text-[12px] font-bold transition-all rounded-md text-gray-500 hover:text-gray-700 hover:bg-gray-50/50">分中心</button>
                        <button id="tab-apps" class="px-3 py-1.5 text-[12px] font-bold transition-all rounded-md text-gray-500 hover:text-gray-700 hover:bg-gray-50/50">重点应用</button>
                    </div>
                    <button id="export-trigger-btn" class="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-[12px] font-bold text-white shadow-md shadow-blue-500/20 transition-all hover:shadow-blue-500/30 hover:-translate-y-0.5 bg-gradient-to-r from-[#165DFF] to-[#4080FF]">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-file-code w-3.5 h-3.5" aria-hidden="true">
                            <path d="M10 12.5 8 15l2 2.5"></path>
                            <path d="m14 12.5 2 2.5-2 2.5"></path>
                            <path d="M14 2v4a2 2 0 0 0 2 2h4"></path>
                            <path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"></path>
                        </svg>导出 HTML
                    </button>
                </div>
            </header>
            <div class="flex-1 px-6 flex flex-col gap-4">
                <div id="sec-kpi" class="grid grid-cols-1 md:grid-cols-3 gap-3 transition-all duration-1000 ease-in-out transform opacity-100 translate-y-0 scale-100 filter-none">
                    <div class="bg-white rounded-xl shadow-[0_2px_10px_-3px_rgba(6,81,237,0.1)] border border-white/50 p-5 relative overflow-hidden group hover:shadow-md transition-all duration-300 !p-3">
                        <div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-[#165DFF] to-[#4080FF]"></div>
                        <div class="flex flex-col h-full justify-between relative z-10">
                            <div class="flex justify-between items-start">
                                <span class="font-bold text-[13px] text-gray-700">全量累计运行</span>
                                <span class="text-[11px] font-medium text-gray-500 bg-gray-50 px-1.5 py-0.5 rounded border border-gray-100 whitespace-nowrap">{key_metrics_data_dict["total_runs"]}</span>
                            </div>
                            <div class="mt-1 relative h-8">
                                <div class="absolute bottom-0 left-0 flex items-baseline gap-1.5">
                                    <span class="kpi-gradient-text font-black text-[24px] leading-none tracking-tight bg-clip-text text-transparent bg-gradient-to-r from-[#165DFF] to-[#4080FF]" data-color="#165DFF">{key_metrics_data_dict["total_hours"]}</span>
                                    <span class="text-[12px] font-bold text-gray-400">小时</span>
                                </div>
                                <div class="absolute bottom-0 right-0 p-1.5 rounded-md bg-gradient-to-br from-[#E8F3FF] to-[#F5F9FF] text-[#165DFF] shadow-sm">
                                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-clock w-4 h-4" aria-hidden="true">
                                        <circle cx="12" cy="12" r="10"></circle>
                                        <polyline points="12 6 12 12 16 14"></polyline>
                                    </svg>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="bg-white rounded-xl shadow-[0_2px_10px_-3px_rgba(6,81,237,0.1)] border border-white/50 p-5 relative overflow-hidden group hover:shadow-md transition-all duration-300 !p-3">
                        <div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-[#FF7D00] to-[#FF9F40]"></div>
                        <div class="flex flex-col h-full justify-between relative z-10">
                            <div class="flex justify-between items-start">
                                <span class="font-bold text-[13px] text-gray-700">全量累计节省</span>
                            </div>
                            <div class="mt-1 relative h-8">
                                <div class="absolute bottom-0 left-0 flex items-baseline gap-1.5">
                                    <span class="kpi-gradient-text font-black text-[24px] leading-none tracking-tight bg-clip-text text-transparent bg-gradient-to-r from-[#FF7D00] to-[#FF9F40]" data-color="#FF7D00">{key_metrics_data_dict["total_save_days"]}</span>
                                    <span class="text-[12px] font-bold text-gray-400">人天</span>
                                </div>
                                <div class="absolute bottom-0 right-0 p-1.5 rounded-md bg-gradient-to-br from-[#FFF7E6] to-[#FFFBF0] text-[#FF7D00] shadow-sm">
                                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zap w-4 h-4" aria-hidden="true">
                                        <path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"></path>
                                    </svg>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="bg-white rounded-xl shadow-[0_2px_10px_-3px_rgba(6,81,237,0.1)] border border-white/50 p-5 relative overflow-hidden group hover:shadow-md transition-all duration-300 !p-3">
                        <div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-[#00B42A] to-[#2DCB54]"></div>
                        <div class="flex flex-col h-full justify-between relative z-10">
                            <div class="flex justify-between items-start">
                                <span class="font-bold text-[13px] text-gray-700">折算人数</span>
                                <span class="text-[11px] font-medium text-gray-500 bg-gray-50 px-1.5 py-0.5 rounded border border-gray-100 whitespace-nowrap">释放人力资源</span>
                            </div>
                            <div class="mt-1 relative h-8">
                                <div class="absolute bottom-0 left-0 flex items-baseline gap-1.5">
                                    <span class="kpi-gradient-text font-black text-[24px] leading-none tracking-tight bg-clip-text text-transparent bg-gradient-to-r from-[#00B42A] to-[#2DCB54]" data-color="#00B42A">{key_metrics_data_dict["release_people"]}</span>
                                    <span class="text-[12px] font-bold text-gray-400">人</span>
                                </div>
                                <div class="absolute bottom-0 right-0 p-1.5 rounded-md bg-gradient-to-br from-[#E8FFEA] to-[#F2FFF3] text-[#00B42A] shadow-sm">
                                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-users w-4 h-4" aria-hidden="true">
                                        <path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path>
                                        <path d="M16 3.128a4 4 0 0 1 0 7.744"></path>
                                        <path d="M22 21v-2a4 4 0 0 0-3-3.87"></path>
                                        <circle cx="9" cy="7" r="4"></circle>
                                    </svg>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="grid grid-cols-1 lg:grid-cols-12 gap-4">
                    <div id="sec-center" class="lg:col-span-4 transition-all duration-1000 ease-in-out transform opacity-100 translate-y-0 scale-100 filter-none">
                        <div class="bg-white rounded-xl shadow-[0_2px_10px_-3px_rgba(6,81,237,0.1)] border border-white/50 p-5 flex flex-col !p-4">
                            <div class="flex justify-between items-end mb-3">
                                <h3 class="flex items-center gap-2">
                                    <span class="section-dot relative flex h-2.5 w-2.5 flex-shrink-0">
                                        <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75"></span>
                                        <span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-[#165DFF]"></span>
                                    </span>
                                    <span class="font-bold text-[15px] tracking-tight leading-snug" style="color: rgb(29, 33, 41);">分中心贡献</span>
                                </h3>
                                <span class="font-medium text-[12px] px-1.5 py-0.5 rounded-md bg-gray-100" style="color: rgb(134, 144, 156);">全量口径</span>
                            </div>
                            <div class="space-y-3 mb-2">
                                {center_html}
                            </div>
                            <div class="mt-1 p-2.5 border border-dashed border-gray-200 bg-gray-50/80 rounded-lg text-[12px] leading-relaxed text-gray-500">
                                <div class="flex gap-2 items-start">
                                    <div class="mt-1 w-1 h-3 bg-blue-500 rounded-full flex-shrink-0"></div>
                                    <p>
                                        <span class="font-bold text-gray-800">{center_summary.split(' ')[0]}</span> 贡献最大。 价值主线:
                                        <span class="font-bold text-blue-600">{center_summary.split(':')[1]}</span>。
                                    </p>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div id="sec-apps" class="lg:col-span-8 flex flex-col gap-4 transition-all duration-1000 ease-in-out transform opacity-100 translate-y-0 scale-100 filter-none">
                        <div class="bg-white rounded-xl shadow-[0_2px_10px_-3px_rgba(6,81,237,0.1)] border border-white/50 p-5 z-20 !p-4">
                            <div class="flex justify-between items-end mb-3">
                                <h3 class="flex items-center gap-2">
                                    <span class="section-dot relative flex h-2.5 w-2.5 flex-shrink-0">
                                        <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75"></span>
                                        <span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-[#165DFF]"></span>
                                    </span>
                                    <span class="font-bold text-[15px] tracking-tight leading-snug" style="color: rgb(29, 33, 41);">流程类型结构</span>
                                </h3>
                                <span class="font-medium text-[12px] px-1.5 py-0.5 rounded-md bg-gray-100" style="color: rgb(134, 144, 156);">人天占比</span>
                            </div>
                            <div class="py-0">
                                <div class="flex w-full h-6 shadow-sm ring-2 ring-gray-50 rounded-lg bg-gray-100">
                                    {process_html}
                                </div>
                                <!-- 新增:流程类型图例 -->
                                <div class="flex gap-4 mt-3 text-sm">
                                    {process_legend_html}
                                </div>
                            </div>
                        </div>
                        <div class="bg-white rounded-xl shadow-[0_2px_10px_-3px_rgba(6,81,237,0.1)] border border-white/50 p-5 flex flex-col min-h-[300px] !p-4">
                            <div class="flex justify-between items-center mb-2">
                                <div class="flex justify-between items-end mb-3">
                                    <h3 class="flex items-center gap-2">
                                        <span class="section-dot relative flex h-2.5 w-2.5 flex-shrink-0">
                                            <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75"></span>
                                            <span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-[#165DFF]"></span>
                                        </span>
                                        <span class="font-bold text-[15px] tracking-tight leading-snug" style="color: rgb(29, 33, 41);">重点应用清单</span>
                                    </h3>
                                </div>
                                <div class="flex items-center gap-2">
                                    <button id="export-btn-prev" disabled="" class="flex items-center justify-center w-6 h-6 rounded-md bg-white border border-gray-200 text-gray-500 hover:bg-gray-50 hover:text-blue-600 disabled:opacity-50 disabled:cursor-not-allowed transition-all">
                                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-left w-3.5 h-3.5" aria-hidden="true">
                                            <path d="m15 18-6-6 6-6"></path>
                                        </svg>
                                    </button>
                                    <span id="export-page-indicator" class="text-[12px] font-medium text-gray-500 min-w-[30px] text-center">1 / 2</span>
                                    <button id="export-btn-next" class="flex items-center justify-center w-6 h-6 rounded-md bg-white border border-gray-200 text-gray-500 hover:bg-gray-50 hover:text-blue-600 disabled:opacity-50 disabled:cursor-not-allowed transition-all">
                                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-right w-3.5 h-3.5" aria-hidden="true">
                                            <path d="m9 18 6-6-6-6"></path>
                                        </svg>
                                    </button>
                                </div>
                            </div>
                            <div class="overflow-x-auto">
                                <table class="w-full text-left border-separate border-spacing-y-1">
                                    <thead class="bg-white">
                                        <tr>
                                            <th class="pb-2 pl-3 text-[12px] font-extrabold text-gray-400 uppercase tracking-wider">#</th>
                                            <th class="pb-2 text-[12px] font-extrabold text-gray-400 uppercase tracking-wider">应用名称</th>
                                            <th class="pb-2 text-[12px] font-extrabold text-gray-400 uppercase tracking-wider">类型</th>
                                            <th class="pb-2 text-right text-[12px] font-extrabold text-gray-400 uppercase tracking-wider">小时</th>
                                            <th class="pb-2 text-right text-[12px] font-extrabold text-gray-400 uppercase tracking-wider">自动化人天</th>
                                            <th class="pb-2 text-right pr-3 text-[12px] font-extrabold text-gray-400 uppercase tracking-wider">提升人效 (FTE)</th>
                                        </tr>
                                    </thead>
                                    <tbody id="export-table-body">
                                        <!-- 由JS动态渲染 -->
                                    </tbody>
                                </table>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script>
        (function() {{
            // --- 1. 表格数据与翻页逻辑 ---
            const tableData = {app_json};
            const itemsPerPage = 5;
            let currentPage = 1;
            const totalPages = Math.ceil(tableData.length / itemsPerPage);

            function renderTable() {{
                const start = (currentPage - 1) * itemsPerPage;
                const end = start + itemsPerPage;
                const currentData = tableData.slice(start, end);
                const tbody = document.getElementById('export-table-body');
                const pageIndicator = document.getElementById('export-page-indicator');
                const btnPrev = document.getElementById('export-btn-prev');
                const btnNext = document.getElementById('export-btn-next');

                if (pageIndicator) pageIndicator.textContent = currentPage + ' / ' + totalPages;
                
                if (btnPrev) {{
                    btnPrev.disabled = currentPage === 1;
                    btnPrev.style.opacity = currentPage === 1 ? '0.5' : '1';
                    btnPrev.style.cursor = currentPage === 1 ? 'not-allowed' : 'pointer';
                }}
                if (btnNext) {{
                    btnNext.disabled = currentPage === totalPages;
                    btnNext.style.opacity = currentPage === totalPages ? '0.5' : '1';
                    btnNext.style.cursor = currentPage === totalPages ? 'not-allowed' : 'pointer';
                }}

                if (!tbody) return;

                let html = '';
                currentData.forEach(row => {{
                    const isTop3 = row.id <= 3;
                    const bgClass = isTop3 ? 'bg-amber-50' : 'bg-gray-50/50';
                    const badgeClass = isTop3 ? 'bg-amber-500 text-white font-bold shadow-sm' : 'text-gray-400';
                    
                    html += `
                        <tr class="border-b border-gray-50 last:border-0 group hover:bg-red-200 transition-all ${{bgClass}}">
                            <td class="py-2 pl-3 rounded-l-lg border-y border-l border-transparent">
                                <div class="font-mono text-[12px] w-6 h-6 flex items-center justify-center rounded-md ${{badgeClass}}">
                                    ${{String(row.id).padStart(2, '0')}}
                                </div>
                            </td>
                            <td class="py-2 text-[13px] font-bold text-gray-700 border-y border-transparent truncate max-w-[200px]">
                                ${{row.name}}
                            </td>
                            <td class="py-2 border-y border-transparent">
                                <span class="text-[11px] font-bold px-2 py-0.5 rounded border ${{row.typeBadge}}">
                                    ${{row.type}}
                                </span>
                            </td>
                            <td class="py-2 text-right text-[12px] text-gray-500 border-y border-transparent tabular-nums">${{row.hours}}</td>
                            <td class="py-2 text-right text-[12px] font-bold text-gray-800 border-y border-transparent tabular-nums">${{row.days}}</td>
                            <td class="py-2 text-right pr-3 rounded-r-lg text-[14px] font-bold text-[#165DFF] border-y border-r border-transparent relative tabular-nums">
                                ${{row.efficiency}} 人
                            </td>
                        </tr>
                    `;
                }});
                tbody.innerHTML = html;
            }}

            // --- 2. 聚光灯交互逻辑 ---
            const activeClasses = 'opacity-100 translate-y-0 scale-100 filter-none'.split(' ');
            const inactiveClasses = 'opacity-[0.05] blur-[2px] grayscale brightness-0 scale-[0.98] pointer-events-none'.split(' ');
            const commonClasses = 'transition-all duration-1000 ease-in-out transform'.split(' ');

            function setFocus(targetId) {{
                // 获取三个主要区域
                const sections = {{
                    '关键指标': document.getElementById('sec-kpi'),
                    '分中心': document.getElementById('sec-center'),
                    '重点应用': document.getElementById('sec-apps')
                }};

                // 更新标签样式
                const tabs = {{
                    '全景视图': document.getElementById('tab-all'),
                    '关键指标': document.getElementById('tab-kpi'),
                    '分中心': document.getElementById('tab-center'),
                    '重点应用': document.getElementById('tab-apps')
                }};

                // 更新标签UI
                Object.keys(tabs).forEach(key => {{
                    const btn = tabs[key];
                    if (!btn) return;
                    if (key === targetId) {{
                        btn.className = "px-3 py-1.5 text-[12px] font-bold transition-all rounded-md bg-white text-[#165DFF] shadow-sm ring-1 ring-gray-100";
                    }} else {{
                        btn.className = "px-3 py-1.5 text-[12px] font-bold transition-all rounded-md text-gray-500 hover:text-gray-700 hover:bg-gray-50/50";
                    }}
                }});

                // 更新区域样式
                const isAll = targetId === '全景视图';
                
                Object.keys(sections).forEach(key => {{
                    const el = sections[key];
                    if (!el) return;
                    
                    // 先移除状态类
                    el.classList.remove(...activeClasses, ...inactiveClasses);
                    // 确保基础动画类存在
                    el.classList.add(...commonClasses);

                    if (isAll || key === targetId) {{
                        el.classList.add(...activeClasses);
                    }} else {{
                        el.classList.add(...inactiveClasses);
                    }}
                }});
            }}

            // 绑定翻页事件
            const btnPrev = document.getElementById('export-btn-prev');
            const btnNext = document.getElementById('export-btn-next');
            if (btnPrev) btnPrev.onclick = function() {{ if (currentPage > 1) {{ currentPage--; renderTable(); }} }};
            if (btnNext) btnNext.onclick = function() {{ if (currentPage < totalPages) {{ currentPage++; renderTable(); }} }};

            // 绑定标签点击事件
            const tabMap = {{
                'tab-all': '全景视图',
                'tab-kpi': '关键指标',
                'tab-center': '分中心',
                'tab-apps': '重点应用'
            }};
            
            Object.keys(tabMap).forEach(id => {{
                const btn = document.getElementById(id);
                if (btn) {{
                    btn.onclick = function() {{
                        setFocus(tabMap[id]);
                    }};
                }};
            }});
            
            // 初始渲染
            renderTable();
        }})();
    </script>

</body>
</html>
    """

    # 3. 构建文件路径并保存
    # 清理文件名中的特殊字符
    safe_title = "".join(c for c in title if c.isalnum() or c in (' ', '-', '_')).strip()
    if not safe_title:
        safe_title = "RPA报告"
    
    html_filename = f"{safe_title}.html"
    html_file_path = os.path.join(output_folder, html_filename)
    
    # 保存到文件
    with open(html_file_path, "w", encoding="utf-8") as f:
        f.write(html_template)
    
    return "HTML文件已生成完成!, 保存在: " + html_file_path


# 示例调用:生成自定义数据的HTML文件
if __name__ == "__main__":

    title="2027 RPA效能分析报告"
    center_summary="客服中心 贡献最大。 价值主线:"客服/售后提效 + 经营动作降本""
    

    # 自定义关键指标数据
    key_metrics_data_dict={"total_runs": "85,690 次", "total_hours": "28,500", "total_save_days": "1,860.5", "release_people": "≈ 7.8"}

    # 自定义分中心数据
    custom_centers = [
        {"name": "客服中心", "days": "700 人天","people": "≈ 3.0 人", "percent": "50%"},
        {"name": "经营管理中心","days": "460 人天","people": "≈ 2.0 人", "percent": "30%"},
        {"name": "供应链中心", "days": "230 人天","people": "≈ 1.0 人", "percent": "20%"}
    ]
    
    # 自定义流程结构
    custom_process = [{'name': '数据类', 'percent': '64.1%', 'width': '64.1%', 'color': 'from-[#165DFF] to-[#4080FF]', 'rounded': 'rounded-l-lg'}, {'name': '办公类', 'percent': '21.9%', 'width': '21.9%', 'color': 'from-[#FAAD14] to-[#FBC23B]', 'rounded': ''}, {'name': '作业测试', 'percent': '7.4%', 'width': '7.4%', 'color': 'from-[#722ED1] to-[#9F5CE6]', 'rounded': ''}, {'name': '营销类', 'percent': '2.8%', 'width': '2.8%', 'color': 'from-[#FF4D4F] to-[#FF7A7A]', 'rounded': ''}, {'name': '售后类', 'percent': '2.6%', 'width': '2.6%', 'color': 'from-[#FF7D00] to-[#FF9F40]', 'rounded': ''}, {'name': '仓配类', 'percent': '1.2%', 'width': '1.2%', 'color': 'from-[#00B42A] to-[#2DCB54]', 'rounded': 'rounded-r-lg'}]
    
    # 自定义应用列表
    custom_apps = [
            {"id":1, "name":"抖音订单自动签收流程", "type":"数据类", "typeBadge":"bg-blue-50 text-blue-600 border-blue-100", "hours":420, "days":"262.5", "efficiency":"1.05"},
            {"id":2, "name":"京东售后单据自动审核", "type":"售后类", "typeBadge":"bg-orange-50 text-orange-600 border-orange-100", "hours":380, "days":"237.5", "efficiency":"0.95"},
            {"id":3, "name":"拼多多库存同步监控", "type":"仓配类", "typeBadge":"bg-green-50 text-green-600 border-green-100", "hours":310, "days":"193.8", "efficiency":"0.78"},
            {"id":4, "name":"每日销售报表自动生成", "type":"数据类", "typeBadge":"bg-blue-50 text-blue-600 border-blue-100", "hours":200, "days":"125.0", "efficiency":"0.50"},
            {"id":5, "name":"物流异常件自动预警", "type":"仓配类", "typeBadge":"bg-green-50 text-green-600 border-green-100", "hours":150, "days":"93.8", "efficiency":"0.38"},
            {"id":6, "name":"财务对账自动化", "type":"财务类", "typeBadge":"bg-purple-50 text-purple-600 border-purple-100", "hours":120, "days":"75.0", "efficiency":"0.30"},
            {"id":7, "name":"供应链采购单自动录入", "type":"仓配类", "typeBadge":"bg-green-50 text-green-600 border-green-100", "hours":110, "days":"68.8", "efficiency":"0.28"},
            {"id":8, "name":"员工入职账号自动开通", "type":"数据类", "typeBadge":"bg-blue-50 text-blue-600 border-blue-100", "hours":90, "days":"56.3", "efficiency":"0.23"},
            {"id":9, "name":"税务发票自动查验", "type":"财务类", "typeBadge":"bg-purple-50 text-purple-600 border-purple-100", "hours":85, "days":"53.1", "efficiency":"0.21"},
            {"id":10, "name":"客户评价自动汇总分析", "type":"售后类", "typeBadge":"bg-orange-50 text-orange-600 border-orange-100", "hours":70, "days":"43.8", "efficiency":"0.17"}
        ]
    
    output_folder="D:\\Desktop"
    
    # 生成HTML内容
    result_content = generate_rpa_report_html(
        title=title,
        key_metrics_data_dict=key_metrics_data_dict,
        center_contributions=custom_centers,
        center_summary=center_summary,
        process_structure=custom_process,
        app_list=custom_apps,
        output_folder=output_folder
    )

    print(result_content)
相关推荐
嫂子开门我是_我哥2 小时前
第八节:条件判断与循环:解锁Python的逻辑控制能力
开发语言·python
2301_805962932 小时前
树莓派的一些问题记录-1:usbboot仓库
python·gitee
言無咎2 小时前
传统财务RPA陷入性能瓶颈?AI财务机器人用LLM重构智能财税
人工智能·机器人·rpa
深蓝电商API2 小时前
Selenium 动作链 ActionChains 高级用法
爬虫·python·selenium
喵手2 小时前
Python爬虫零基础入门【第八章:项目实战演练·第2节】项目 2:信息聚合站 Demo(列表+详情+增量+质量报告)!
爬虫·python·爬虫实战·python爬虫工程化实战·零基础python爬虫教学·爬虫项目演练·信息聚合站
翱翔的苍鹰2 小时前
多Agent智能体系统设计思路
java·python·深度学习·神经网络·机器学习·tensorflow
言無咎2 小时前
RPA财务机器人已OUT?2026掌金AI以多模态LLM重构财税自动化
人工智能·机器人·rpa
小北方城市网2 小时前
Spring Cloud Gateway 全链路监控与故障自愈实战
spring boot·python·rabbitmq·java-rabbitmq·数据库架构
weixin_440730502 小时前
04python编程笔记-04函数+05面向对象
笔记·python