From 10a07487380fbc47672a3cf3365d20c14fab46f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E5=B8=88?= Date: Tue, 9 Jun 2026 02:16:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20HTML=E7=9C=8B=E6=9D=BF=E2=86=92?= =?UTF-8?q?=E5=8F=AF=E7=BC=96=E8=BE=91PPTX=20=C2=B7=20python-pptx=20=C2=B7?= =?UTF-8?q?=20CHEC=2016:9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cleaned/危大方案编审进度看板.pptx | Bin 0 -> 33665 bytes src/gen_pptx.py | 216 ++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 data/2026-06-08/cleaned/危大方案编审进度看板.pptx create mode 100644 src/gen_pptx.py diff --git a/data/2026-06-08/cleaned/危大方案编审进度看板.pptx b/data/2026-06-08/cleaned/危大方案编审进度看板.pptx new file mode 100644 index 0000000000000000000000000000000000000000..c5a6ecbdb81267ed5059c57036537087ac86808c GIT binary patch literal 33665 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�Z0Yiu-e@+hrJ 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 literal 0 HcmV?d00001 diff --git a/src/gen_pptx.py b/src/gen_pptx.py new file mode 100644 index 0000000..79ff277 --- /dev/null +++ b/src/gen_pptx.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 +"""HTML看板 → 可编辑PPTX (python-pptx) · CHEC规范 16:9""" +from pptx import Presentation +from pptx.util import Inches, Pt, Emu, Cm +from pptx.dml.color import RGBColor +from pptx.enum.text import PP_ALIGN, MSO_ANCHOR +from pptx.enum.shapes import MSO_SHAPE + +# ── CHEC品牌色 ── +BLUE = RGBColor(0x1A, 0x3A, 0x5C) +GOLD = RGBColor(0xC8, 0x96, 0x2E) +RED = RGBColor(0xD9, 0x4E, 0x34) +GRAY = RGBColor(0x88, 0x99, 0xAA) +BG = RGBColor(0xEB, 0xF0, 0xF7) +WHITE = RGBColor(0xFF, 0xFF, 0xFF) +BLACK = RGBColor(0x33, 0x33, 0x33) +GREEN = RGBColor(0x2E, 0x7D, 0x32) +BORDER = RGBColor(0xDB, 0xE2, 0xEA) + +prs = Presentation() +prs.slide_width = Inches(13.333) # 16:9 +prs.slide_height = Inches(7.5) + +def add_rect(slide, left, top, w, h, fill_color=None, line_color=None): + shape = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, left, top, w, h) + shape.line.fill.background() + if fill_color: + shape.fill.solid() + shape.fill.fore_color.rgb = fill_color + if line_color: + shape.line.color.rgb = line_color + shape.line.width = Pt(0.5) + else: + shape.line.fill.background() + return shape + +def add_text_box(slide, left, top, w, h, text, font_size=12, color=BLACK, bold=False, font_name='Microsoft YaHei', align=PP_ALIGN.LEFT): + txBox = slide.shapes.add_textbox(left, top, w, h) + txBox.text_frame.word_wrap = True + p = txBox.text_frame.paragraphs[0] + p.text = text + p.font.size = Pt(font_size) + p.font.color.rgb = color + p.font.bold = bold + p.font.name = font_name + p.alignment = align + return txBox + +def add_kpi_card(slide, left, top, w, h, number, label, num_color=BLUE, num_size=44): + """统一KPI卡片""" + card = add_rect(slide, left, top, w, h, fill_color=BG) + card.shadow.inherit = False + num_box = add_text_box(slide, left, top + Inches(0.08), w, Inches(0.55), str(number), + font_size=num_size, color=num_color, bold=True, align=PP_ALIGN.CENTER) + add_text_box(slide, left, top + Inches(0.58), w, Inches(0.2), label, + font_size=9, color=GRAY, align=PP_ALIGN.CENTER) + +# ═══════════════════════════════════════ +# Slide 1: 关键指标 +# ═══════════════════════════════════════ +s1 = prs.slides.add_slide(prs.slide_layouts[6]) # blank + +# Nav bar +nav = add_rect(s1, Inches(0), Inches(0), Inches(13.333), Inches(0.44), fill_color=BLUE) +add_text_box(s1, Inches(0.5), Inches(0.08), Inches(2), Inches(0.3), '★ 关键指标', font_size=11, color=GOLD, bold=True) +add_text_box(s1, Inches(2.5), Inches(0.08), Inches(2), Inches(0.3), '国别分布', font_size=11, color=GRAY) + +# Title +add_rect(s1, Inches(0.5), Inches(0.65), Inches(0.06), Inches(0.3), fill_color=GOLD) +add_text_box(s1, Inches(0.7), Inches(0.6), Inches(8), Inches(0.4), '危大方案编审进度看板', font_size=22, color=BLUE, bold=True) +add_text_box(s1, Inches(8.5), Inches(0.68), Inches(4.5), Inches(0.3), '中国港湾中东区域公司 技术部 · 2026年6月 · 数据截止 2026-06-08', font_size=10, color=GRAY, align=PP_ALIGN.RIGHT) + +# === ROW 1: Module 1 & 2 === +y_row1 = Inches(1.2) +mod_w = Inches(5.9) + +# Module 1: 年度认定 +m1 = add_rect(s1, Inches(0.5), y_row1, mod_w, Inches(1.7), fill_color=WHITE, line_color=BORDER) +add_rect(s1, Inches(0.5), y_row1, mod_w, Inches(0.38), fill_color=BLUE) +add_text_box(s1, Inches(0.7), y_row1 + Inches(0.05), Inches(3), Inches(0.28), '1. 年度认定', font_size=12, color=WHITE, bold=True) +add_text_box(s1, Inches(2.2), y_row1 + Inches(0.06), Inches(2.5), Inches(0.25), '中港科技便〔2026〕6号', font_size=9, color=GOLD) + +add_kpi_card(s1, Inches(0.7), y_row1 + Inches(0.5), Inches(1.2), Inches(0.95), '43', '安全专项', num_size=44) +add_text_box(s1, Inches(0.7), y_row1 + Inches(1.48), Inches(1.2), Inches(0.18), '覆盖 7 个项目', font_size=9, color=GRAY, align=PP_ALIGN.CENTER) + +# Donut chart legend +add_text_box(s1, Inches(2.3), y_row1 + Inches(0.6), Inches(1.5), Inches(0.3), '■ 一般类 27', font_size=12, color=BLUE) +add_text_box(s1, Inches(2.3), y_row1 + Inches(0.9), Inches(1.5), Inches(0.3), '■ 超规类 16', font_size=12, color=GOLD) + +# Module 2: OA有效登记 +x_m2 = Inches(6.93) +m2 = add_rect(s1, x_m2, y_row1, mod_w, Inches(1.7), fill_color=WHITE, line_color=BORDER) +add_rect(s1, x_m2, y_row1, mod_w, Inches(0.38), fill_color=BLUE) +add_text_box(s1, x_m2 + Inches(0.2), y_row1 + Inches(0.05), Inches(3), Inches(0.28), '2. OA有效登记', font_size=12, color=WHITE, bold=True) +add_text_box(s1, x_m2 + Inches(1.8), y_row1 + Inches(0.06), Inches(2), Inches(0.25), '排除已作废', font_size=9, color=GOLD) + +add_kpi_card(s1, x_m2 + Inches(0.2), y_row1 + Inches(0.5), Inches(1.2), Inches(0.95), '52', '有效登记', num_size=44) +add_text_box(s1, x_m2 + Inches(0.2), y_row1 + Inches(1.48), Inches(1.2), Inches(0.18), '登记率 121%', font_size=9, color=GRAY, align=PP_ALIGN.CENTER) +add_text_box(s1, x_m2 + Inches(2.3), y_row1 + Inches(0.6), Inches(1.5), Inches(0.3), '■ 一般类 30', font_size=12, color=BLUE) +add_text_box(s1, x_m2 + Inches(2.3), y_row1 + Inches(0.9), Inches(1.5), Inches(0.3), '■ 超规类 22', font_size=12, color=GOLD) + +# === ROW 2: Module 3 & 4 === +y_row2 = Inches(3.1) + +# Module 3: 国别 +m3 = add_rect(s1, Inches(0.5), y_row2, mod_w, Inches(2.0), fill_color=WHITE, line_color=BORDER) +add_rect(s1, Inches(0.5), y_row2, mod_w, Inches(0.36), fill_color=BLUE) +add_text_box(s1, Inches(0.7), y_row2 + Inches(0.04), Inches(4), Inches(0.28), '3. 按国别分布 · 分层条形图', font_size=11, color=WHITE, bold=True) + +# Bar chart - simplified table +countries = [('阿拉伯联合酋长国', 27, 18, 45), ('沙特阿拉伯', 3, 3, 6), ('卡塔尔', 0, 1, 1)] +y_bar = y_row2 + Inches(0.45) +for i, (name, gen, chao, tot) in enumerate(countries): + y = y_bar + Inches(i * 0.45) + add_text_box(s1, Inches(0.6), y, Inches(1.8), Inches(0.2), name, font_size=10, color=BLACK, align=PP_ALIGN.RIGHT) + bw = max(gen * 0.07, 0.05) if gen > 0 else 0 + if bw > 0: + add_rect(s1, Inches(2.5), y + Inches(0.02), Inches(bw), Inches(0.22), fill_color=BLUE) + cw = max(chao * 0.07, 0.05) if chao > 0 else 0 + if cw > 0: + add_rect(s1, Inches(2.5) + Inches(bw), y + Inches(0.02), Inches(cw), Inches(0.22), fill_color=GOLD) + add_text_box(s1, Inches(5.2), y, Inches(0.5), Inches(0.22), str(tot), font_size=12, color=BLUE, bold=True) + +add_text_box(s1, Inches(0.6), y_row2 + Inches(1.78), Inches(2), Inches(0.18), '■ 一般类', font_size=9, color=BLUE) +add_text_box(s1, Inches(1.5), y_row2 + Inches(1.78), Inches(2), Inches(0.18), '■ 超规类', font_size=9, color=GOLD) + +# Module 4: 审批进度 +m4 = add_rect(s1, x_m2, y_row2, mod_w, Inches(2.0), fill_color=WHITE, line_color=BORDER) +add_rect(s1, x_m2, y_row2, mod_w, Inches(0.36), fill_color=BLUE) +add_text_box(s1, x_m2 + Inches(0.2), y_row2 + Inches(0.04), Inches(4), Inches(0.28), '4. 审批进度 & 三色预警信号', font_size=11, color=WHITE, bold=True) + +# Row 1 cards +cy = y_row2 + Inches(0.5) +cw, ch = Inches(0.85), Inches(0.7) +gap = Inches(0.12) +for idx, (num, label, color) in enumerate([ + ('5', '预警总计', RED), ('🔴0', '红色', RED), ('🟠1', '橙色', RED), ('🟡4', '黄色', GOLD), ('23', '未完成审批', GRAY)]): + cx = x_m2 + Inches(0.15) + Inches(idx * 1.05) + add_kpi_card(s1, cx, cy, cw, ch, num, label, num_color=color, num_size=28 if idx > 0 else 36) + +# Legend +add_text_box(s1, x_m2 + Inches(4.8), cy, Inches(1.2), Inches(0.55), + '🔴 在实施未审批\n🟠 ≤30天\n🟡 ≤45天', font_size=8, color=GRAY, align=PP_ALIGN.RIGHT) + +# Row 2: 审批率 + progress bar +py = y_row2 + Inches(1.35) +add_kpi_card(s1, x_m2 + Inches(0.15), py, Inches(1.0), Inches(0.7), '56%', '审批完成率', num_color=GREEN, num_size=36) +# Progress bar +bar_bg = add_rect(s1, x_m2 + Inches(1.35), py + Inches(0.12), Inches(3.5), Inches(0.22), fill_color=BG) +bar_fg = add_rect(s1, x_m2 + Inches(1.35), py + Inches(0.12), Inches(3.5 * 0.56), Inches(0.22), fill_color=BLUE) +add_text_box(s1, x_m2 + Inches(1.35), py + Inches(0.38), Inches(3.5), Inches(0.18), + '已审批 29 未审批 23 / 总计 52', font_size=8, color=GRAY) + +# Footer +add_rect(s1, Inches(0), Inches(7.18), Inches(13.333), Inches(0.32), fill_color=RGBColor(0xF5, 0xF6, 0xF8)) +add_text_box(s1, Inches(0.5), Inches(7.2), Inches(4), Inches(0.25), '中国港湾中东区域公司 技术部', font_size=8, color=GRAY) +add_text_box(s1, Inches(11.5), Inches(7.2), Inches(1.5), Inches(0.25), '1 / 2', font_size=8, color=GRAY, align=PP_ALIGN.RIGHT) + +# ═══════════════════════════════════════ +# Slide 2: 预警明细 +# ═══════════════════════════════════════ +s2 = prs.slides.add_slide(prs.slide_layouts[6]) +add_rect(s2, Inches(0), Inches(0), Inches(13.333), Inches(0.44), fill_color=BLUE) +add_text_box(s2, Inches(0.5), Inches(0.08), Inches(2), Inches(0.3), '关键指标', font_size=11, color=GRAY) +add_text_box(s2, Inches(2.5), Inches(0.08), Inches(2), Inches(0.3), '★ 预警明细', font_size=11, color=GOLD, bold=True) + +add_rect(s2, Inches(0.5), Inches(0.65), Inches(0.06), Inches(0.3), fill_color=GOLD) +add_text_box(s2, Inches(0.7), Inches(0.6), Inches(8), Inches(0.4), '预警信号明细清单', font_size=22, color=BLUE, bold=True) +add_text_box(s2, Inches(8.5), Inches(0.68), Inches(4.5), Inches(0.3), '共 5 项预警', font_size=10, color=GRAY, align=PP_ALIGN.RIGHT) + +# Table +warnings = [ + ('🟠', '阿联酋迪拜马克图姆国际机场地下结构工程', 'BHS/GSE隧道现浇板(4包)', '已添加未实施', '2天'), + ('🟡', '阿联酋阿布扎比汽车基地房建项目', '模板支立工程', '未审批未实施', '32天'), + ('🟡', '阿联酋阿布扎比汽车基地房建项目', '深基坑开挖方案', '未审批未实施', '37天'), + ('🟡', '阿联酋迪拜马克图姆国际机场地下结构工程', '现浇倒T梁(4包)', '审批中未实施', '42天'), + ('🟡', '阿联酋迪拜马克图姆国际机场地下结构工程', 'T梁预制运输安装(4包)', '已添加未实施', '42天'), +] + +ty = Inches(1.15) +# Table header +th_h = Inches(0.32) +cols = [0.5, 3.0, 3.0, 3.0, 2.5] +headers = ['信号', '项目名称', '方案名称', '当前状态', '距开工'] +for i, (h, w) in enumerate(zip(headers, cols)): + add_rect(s2, Inches(0.5 + sum(cols[:i])), ty, Inches(w), th_h, fill_color=BLUE) + add_text_box(s2, Inches(0.55 + sum(cols[:i])), ty + Inches(0.02), Inches(w - 0.1), Inches(0.28), h, font_size=10, color=WHITE, bold=True) + +for j, (icon, proj, scheme, status, days) in enumerate(warnings): + y = ty + Inches(0.32 + j * 0.38) + bg_c = BG if j % 2 == 0 else WHITE + add_rect(s2, Inches(0.5), y, Inches(12.0), Inches(0.38), fill_color=bg_c) + add_text_box(s2, Inches(0.55), y + Inches(0.04), Inches(0.4), Inches(0.3), icon, font_size=14, align=PP_ALIGN.CENTER) + add_text_box(s2, Inches(1.0), y + Inches(0.04), Inches(3.8), Inches(0.3), proj, font_size=10) + add_text_box(s2, Inches(4.8), y + Inches(0.04), Inches(3.0), Inches(0.3), scheme, font_size=10) + add_text_box(s2, Inches(7.8), y + Inches(0.04), Inches(2.0), Inches(0.3), status, font_size=10) + add_text_box(s2, Inches(9.8), y + Inches(0.04), Inches(1.5), Inches(0.3), days, font_size=11, color=RED, bold=True, align=PP_ALIGN.RIGHT) + +# Rule box +add_rect(s2, Inches(0.5), Inches(4.0), Inches(12.0), Inches(0.5), fill_color=BG) +add_rect(s2, Inches(0.5), Inches(4.0), Inches(0.06), Inches(0.5), fill_color=GOLD) +add_text_box(s2, Inches(0.75), Inches(4.05), Inches(11.5), Inches(0.4), + '📐 预警规则:🟠 橙色 ≤30天未审批 · 🟡 黄色 ≤45天未审批 · 🔴 红色 在实施未审批(本月0项)', font_size=10, color=BLACK) + +# Footer +add_rect(s2, Inches(0), Inches(7.18), Inches(13.333), Inches(0.32), fill_color=RGBColor(0xF5, 0xF6, 0xF8)) +add_text_box(s2, Inches(0.5), Inches(7.2), Inches(4), Inches(0.25), '中国港湾中东区域公司 技术部', font_size=8, color=GRAY) +add_text_box(s2, Inches(11.5), Inches(7.2), Inches(1.5), Inches(0.25), '2 / 2', font_size=8, color=GRAY, align=PP_ALIGN.RIGHT) + +# ── Save ── +out = "/mnt/y/Openclaw_Hub/03.资源/实施项目 wiki/dashboard/data/2026-06-08/cleaned/危大方案编审进度看板.pptx" +prs.save(out) +print(f"✅ PPTX已生成: {out}") +print(f" Slide 1: 关键指标(4模块)") +print(f" Slide 2: 预警明细(5项)") +print(f" 所有文字可编辑 · 16:9 · CHEC规范配色")