diff --git a/data/2026-06-08/cleaned/危大方案编审进度看板.pptx b/data/2026-06-08/cleaned/危大方案编审进度看板.pptx index c5a6ecb..6f16277 100644 Binary files a/data/2026-06-08/cleaned/危大方案编审进度看板.pptx and b/data/2026-06-08/cleaned/危大方案编审进度看板.pptx differ diff --git a/src/gen_pptx.py b/src/gen_pptx.py index 79ff277..0d8d356 100644 --- a/src/gen_pptx.py +++ b/src/gen_pptx.py @@ -1,216 +1,220 @@ #!/usr/bin/env python3 -"""HTML看板 → 可编辑PPTX (python-pptx) · CHEC规范 16:9""" +"""HTML看板 → 可编辑PPTX v2 · 加饼图·修正三色卡·调间距""" from pptx import Presentation -from pptx.util import Inches, Pt, Emu, Cm +from pptx.util import Inches, Pt, Cm from pptx.dml.color import RGBColor -from pptx.enum.text import PP_ALIGN, MSO_ANCHOR +from pptx.enum.text import PP_ALIGN from pptx.enum.shapes import MSO_SHAPE +from pptx.chart.data import CategoryChartData +from pptx.enum.chart import XL_CHART_TYPE -# ── 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) +# ── 品牌色 ── +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) prs = Presentation() -prs.slide_width = Inches(13.333) # 16:9 +prs.slide_width = Inches(13.333) 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 R(l,t,w,h,fill=None,line=None): + s=prs.slides[-1].shapes.add_shape(MSO_SHAPE.RECTANGLE,l,t,w,h) + s.line.fill.background() + if fill: s.fill.solid(); s.fill.fore_color.rgb=fill + if line: s.line.color.rgb=line; s.line.width=Pt(0.5) + return s -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 T(l,t,w,h,txt,sz=12,clr=BLACK,b=False,fn='Microsoft YaHei',al=PP_ALIGN.LEFT): + tb=prs.slides[-1].shapes.add_textbox(l,t,w,h) + tb.text_frame.word_wrap=True + p=tb.text_frame.paragraphs[0]; p.text=txt + p.font.size=Pt(sz); p.font.color.rgb=clr; p.font.bold=b + p.font.name=fn; p.alignment=al + return tb -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) +def kpi(l,t,w,h,num,lbl,nc=BLUE,ns=44,ls=9): + """KPI卡片方块""" + R(l,t,w,h,fill=BG) + T(l,t+Inches(0.06),w,Inches(0.5),str(num),sz=ns,clr=nc,b=True,al=PP_ALIGN.CENTER) + T(l,t+Inches(0.55),w,Inches(0.18),lbl,sz=ls,clr=GRAY,al=PP_ALIGN.CENTER) + +def donut(l,t,w,h,vals,labels,colors): + """原生饼图""" + cd=CategoryChartData() + cd.categories=labels + cd.add_series('',vals) + ch=prs.slides[-1].shapes.add_chart(XL_CHART_TYPE.DOUGHNUT,l,t,w,h,cd) + plot=ch.chart.plots[0] + for i,clr in enumerate(colors): + plot.series[0].points[i].format.fill.solid() + plot.series[0].points[i].format.fill.fore_color.rgb=clr + ch.chart.has_legend=False + ch.chart.has_title=False + return ch # ═══════════════════════════════════════ -# Slide 1: 关键指标 +# SLIDE 1 # ═══════════════════════════════════════ -s1 = prs.slides.add_slide(prs.slide_layouts[6]) # blank +s1=prs.slides.add_slide(prs.slide_layouts[6]) -# 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) +# Nav +R(Inches(0),Inches(0),Inches(13.333),Inches(0.42),fill=BLUE) +T(Inches(0.5),Inches(0.07),Inches(2),Inches(0.28),'★ 关键指标',sz=11,clr=GOLD,b=True) +T(Inches(2.5),Inches(0.07),Inches(2),Inches(0.28),'国别分布',sz=11,clr=GRAY) +T(Inches(4.5),Inches(0.07),Inches(2),Inches(0.28),'预警明细',sz=11,clr=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) +R(Inches(0.5),Inches(0.58),Inches(0.06),Inches(0.28),fill=GOLD) +T(Inches(0.7),Inches(0.55),Inches(8),Inches(0.36),'危大方案编审进度看板',sz=22,clr=BLUE,b=True) +T(Inches(8.5),Inches(0.62),Inches(4.5),Inches(0.28),'中国港湾中东区域公司 技术部 · 2026年6月',sz=9,clr=GRAY,al=PP_ALIGN.RIGHT) -# === ROW 1: Module 1 & 2 === -y_row1 = Inches(1.2) -mod_w = Inches(5.9) +# === ROW 1: M1 + M2 === +RY1=Inches(1.1); MW=Inches(5.9); MH=Inches(1.85) -# 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) +# Module 1 +R(Inches(0.5),RY1,MW,MH,fill=WHITE,line=RGBColor(0xDB,0xE2,0xEA)) +R(Inches(0.5),RY1,MW,Inches(0.38),fill=BLUE) +T(Inches(0.7),RY1+Inches(0.04),Inches(3),Inches(0.3),'1. 年度认定',sz=13,clr=WHITE,b=True) +T(Inches(2.2),RY1+Inches(0.05),Inches(2.5),Inches(0.28),'中港科技便〔2026〕6号',sz=9,clr=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) +kpi(Inches(0.7),RY1+Inches(0.58),Inches(1.1),Inches(1.0),'43','安全专项总数',ns=44) +donut(Inches(2.1),RY1+Inches(0.55),Inches(1.2),Inches(1.2),[27,16],['一般类','超规类'],[BLUE,GOLD]) +T(Inches(3.5),RY1+Inches(0.7),Inches(1.5),Inches(0.25),'■ 一般类 27',sz=11,clr=BLUE) +T(Inches(3.5),RY1+Inches(1.0),Inches(1.5),Inches(0.25),'■ 超规类 16',sz=11,clr=GOLD) +T(Inches(3.5),RY1+Inches(1.3),Inches(1.5),Inches(0.25),'覆盖 7 个项目',sz=9,clr=GRAY) -# 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 +MX2=Inches(6.93) +R(MX2,RY1,MW,MH,fill=WHITE,line=RGBColor(0xDB,0xE2,0xEA)) +R(MX2,RY1,MW,Inches(0.38),fill=BLUE) +T(MX2+Inches(0.2),RY1+Inches(0.04),Inches(3),Inches(0.3),'2. OA有效登记',sz=13,clr=WHITE,b=True) +T(MX2+Inches(1.8),RY1+Inches(0.05),Inches(2),Inches(0.28),'排除已作废',sz=9,clr=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) +kpi(MX2+Inches(0.2),RY1+Inches(0.58),Inches(1.1),Inches(1.0),'52','有效登记总数',ns=44) +donut(MX2+Inches(1.6),RY1+Inches(0.55),Inches(1.2),Inches(1.2),[30,22],['一般类','超规类'],[BLUE,GOLD]) +T(MX2+Inches(3.0),RY1+Inches(0.7),Inches(1.5),Inches(0.25),'■ 一般类 30',sz=11,clr=BLUE) +T(MX2+Inches(3.0),RY1+Inches(1.0),Inches(1.5),Inches(0.25),'■ 超规类 22',sz=11,clr=GOLD) +T(MX2+Inches(3.0),RY1+Inches(1.3),Inches(1.5),Inches(0.25),'登记率 121%(52/43)',sz=9,clr=GRAY) -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) +# === ROW 2: M3 + M4 === +RY2=Inches(3.15) # 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) +R(Inches(0.5),RY2,MW,Inches(1.85),fill=WHITE,line=RGBColor(0xDB,0xE2,0xEA)) +R(Inches(0.5),RY2,MW,Inches(0.36),fill=BLUE) +T(Inches(0.7),RY2+Inches(0.03),Inches(4),Inches(0.28),'3. 按国别分布 · 分层条形图',sz=11,clr=WHITE,b=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) +countries=[('阿拉伯联合酋长国',27,18,45),('沙特阿拉伯',3,3,6),('卡塔尔',0,1,1)] +y0=RY2+Inches(0.5) +for i,(name,gn,ch,tot) in enumerate(countries): + y=y0+Inches(i*0.42) + T(Inches(0.55),y,Inches(1.5),Inches(0.22),name,sz=10,clr=BLACK,al=PP_ALIGN.RIGHT) + bw=max(gn*0.065,0.03) if gn>0 else 0 + if bw>0: R(Inches(2.15),y+Inches(0.02),Inches(bw),Inches(0.2),fill=BLUE) + cw=max(ch*0.065,0.03) if ch>0 else 0 + if cw>0: R(Inches(2.15)+Inches(bw),y+Inches(0.02),Inches(cw),Inches(0.2),fill=GOLD) + T(Inches(5.3),y,Inches(0.5),Inches(0.22),str(tot),sz=12,clr=BLUE,b=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) +T(Inches(0.55),RY2+Inches(1.58),Inches(2),Inches(0.18),'■ 一般类',sz=9,clr=BLUE) +T(Inches(1.3),RY2+Inches(1.58),Inches(2),Inches(0.18),'■ 超规类',sz=9,clr=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) +# Module 4: 审批+预警 +R(MX2,RY2,MW,Inches(1.85),fill=WHITE,line=RGBColor(0xDB,0xE2,0xEA)) +R(MX2,RY2,MW,Inches(0.36),fill=BLUE) +T(MX2+Inches(0.2),RY2+Inches(0.03),Inches(4),Inches(0.28),'4. 审批进度 & 三色预警信号',sz=11,clr=WHITE,b=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) +# Row 1: 预警指标卡片 — 统一样式 +CARD_W=Inches(0.85); CARD_H=Inches(0.7); CARD_GAP=Inches(0.08) +CY=RY2+Inches(0.48) -# 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) +warn_data=[ + ('5', '预警总计', RED, 36), + ('0', '红色🔴', RED, 28), + ('1', '橙色🟠', RED, 28), + ('4', '黄色🟡', GOLD, 28), + ('23', '未完成审批', GRAY, 28), +] +for idx,(num,lbl,clr,ns) in enumerate(warn_data): + cx=MX2+Inches(0.12)+Inches(idx*(CARD_W+CARD_GAP)) + kpi(cx,CY,CARD_W,CARD_H,num,lbl,nc=clr,ns=ns,ls=8) -# 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) +# Legend right-aligned +T(MX2+Inches(4.9),CY+Inches(0.05),Inches(1.0),Inches(0.65), + '🔴 在实施未审批\n🟠 ≤30天\n🟡 ≤45天',sz=7,clr=GRAY,al=PP_ALIGN.RIGHT) + +# Row 2: 审批率 + 进度条 +PY=RY2+Inches(1.3) +kpi(MX2+Inches(0.12),PY,Inches(0.95),Inches(0.5),'56%','审批完成率',nc=GREEN,ns=32,ls=8) # 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) +R(MX2+Inches(1.2),PY+Inches(0.08),Inches(3.6),Inches(0.18),fill=BG) +R(MX2+Inches(1.2),PY+Inches(0.08),Inches(3.6*0.56),Inches(0.18),fill=BLUE) +T(MX2+Inches(1.2),PY+Inches(0.28),Inches(3.6),Inches(0.18), + '已审批 29 未审批 23 / 总计 52',sz=7,clr=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) +R(Inches(0),Inches(7.18),Inches(13.333),Inches(0.32),fill=RGBColor(0xF5,0xF6,0xF8)) +T(Inches(0.5),Inches(7.2),Inches(4),Inches(0.2),'中国港湾中东区域公司 技术部',sz=8,clr=GRAY) +T(Inches(11.5),Inches(7.2),Inches(1.5),Inches(0.2),'1 / 2',sz=8,clr=GRAY,al=PP_ALIGN.RIGHT) # ═══════════════════════════════════════ -# Slide 2: 预警明细 +# 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) +s2=prs.slides.add_slide(prs.slide_layouts[6]) -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) +R(Inches(0),Inches(0),Inches(13.333),Inches(0.42),fill=BLUE) +T(Inches(0.5),Inches(0.07),Inches(2),Inches(0.28),'关键指标',sz=11,clr=GRAY) +T(Inches(2.5),Inches(0.07),Inches(2),Inches(0.28),'国别分布',sz=11,clr=GRAY) +T(Inches(4.5),Inches(0.07),Inches(2),Inches(0.28),'★ 预警明细',sz=11,clr=GOLD,b=True) -# Table -warnings = [ - ('🟠', '阿联酋迪拜马克图姆国际机场地下结构工程', 'BHS/GSE隧道现浇板(4包)', '已添加未实施', '2天'), - ('🟡', '阿联酋阿布扎比汽车基地房建项目', '模板支立工程', '未审批未实施', '32天'), - ('🟡', '阿联酋阿布扎比汽车基地房建项目', '深基坑开挖方案', '未审批未实施', '37天'), - ('🟡', '阿联酋迪拜马克图姆国际机场地下结构工程', '现浇倒T梁(4包)', '审批中未实施', '42天'), - ('🟡', '阿联酋迪拜马克图姆国际机场地下结构工程', 'T梁预制运输安装(4包)', '已添加未实施', '42天'), +R(Inches(0.5),Inches(0.58),Inches(0.06),Inches(0.28),fill=GOLD) +T(Inches(0.7),Inches(0.55),Inches(8),Inches(0.36),'预警信号明细清单',sz=22,clr=BLUE,b=True) +T(Inches(8.5),Inches(0.62),Inches(4.5),Inches(0.28),'共 5 项预警',sz=10,clr=GRAY,al=PP_ALIGN.RIGHT) + +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) +TY=Inches(1.1); TH=Inches(0.32) +CW=[Inches(0.6),Inches(3.8),Inches(3.0),Inches(2.2),Inches(1.0)] +HDS=['信号','项目名称','方案名称','当前状态','距开工'] +x_off=Inches(0.5) +for i,(h,w) in enumerate(zip(HDS,CW)): + R(x_off+sum(CW[:i]),TY,w,TH,fill=BLUE) + T(x_off+Inches(0.05)+sum(CW[:i]),TY+Inches(0.02),w-Inches(0.1),Inches(0.28),h,sz=10,clr=WHITE,b=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) +for j,(icon,proj,scheme,status,days) in enumerate(warnings): + y=TY+TH+Inches(j*0.38) + bg_c=BG if j%2==0 else WHITE + R(x_off,y,sum(CW),Inches(0.38),fill=bg_c) + T(x_off+Inches(0.05),y+Inches(0.04),Inches(0.5),Inches(0.3),icon,sz=16,al=PP_ALIGN.CENTER) + T(x_off+CW[0],y+Inches(0.04),CW[1]-Inches(0.1),Inches(0.3),proj,sz=10) + T(x_off+CW[0]+CW[1],y+Inches(0.04),CW[2]-Inches(0.1),Inches(0.3),scheme,sz=10) + T(x_off+CW[0]+CW[1]+CW[2],y+Inches(0.04),CW[3]-Inches(0.1),Inches(0.3),status,sz=10) + T(x_off+sum(CW[:4]),y+Inches(0.04),CW[4]-Inches(0.1),Inches(0.3),days,sz=11,clr=RED,b=True,al=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) +# Rule +R(x_off,TY+TH+Inches(5*0.38+0.2),sum(CW),Inches(0.45),fill=BG) +R(x_off,TY+TH+Inches(5*0.38+0.2),Inches(0.06),Inches(0.45),fill=GOLD) +T(x_off+Inches(0.2),TY+TH+Inches(5*0.38+0.25),Inches(11),Inches(0.35), + '📐 预警规则:🟠 橙色 ≤30天未审批 · 🟡 黄色 ≤45天未审批 · 🔴 红色 在实施未审批(本月0项)',sz=10,clr=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) +R(Inches(0),Inches(7.18),Inches(13.333),Inches(0.32),fill=RGBColor(0xF5,0xF6,0xF8)) +T(Inches(0.5),Inches(7.2),Inches(4),Inches(0.2),'中国港湾中东区域公司 技术部',sz=8,clr=GRAY) +T(Inches(11.5),Inches(7.2),Inches(1.5),Inches(0.2),'2 / 2',sz=8,clr=GRAY,al=PP_ALIGN.RIGHT) -# ── Save ── -out = "/mnt/y/Openclaw_Hub/03.资源/实施项目 wiki/dashboard/data/2026-06-08/cleaned/危大方案编审进度看板.pptx" +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规范配色") +print(f"✅ v2 已生成: {out}") +print(f" + 原生饼图(一般/超规)") +print(f" + 三色预警统一卡片样式") +print(f" + 模块间距重新计算")