Files
k3GPT/main/make_ppt.py
2025-11-19 19:42:45 +08:00

585 lines
27 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from pptx import Presentation
from pptx.util import Inches, Pt, Cm
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
from pptx.enum.text import MSO_ANCHOR # 导入正确的垂直对齐枚举
from pptx.enum.shapes import MSO_SHAPE
from pptx.chart.data import ChartData
from pptx.enum.chart import XL_CHART_TYPE,XL_LABEL_POSITION,XL_LEGEND_POSITION
from pptx.table import Table
from parse_html import parse_html_to_ppt
from init import gcfg
def apply_gradient_background(slide):
"""应用统一的渐变背景"""
background = slide.background
fill = background.fill
fill.gradient()
fill.gradient_stops[0].color.rgb = RGBColor(0, 32, 96) # 深蓝
fill.gradient_stops[1].color.rgb = RGBColor(0, 112, 192) # 蓝色
def create_content_slide(prs, title_text, subtitle_text=None, content_items=None):
"""创建带二级标题的内容页"""
slide_layout = prs.slide_layouts[6] # 空白布局
slide = prs.slides.add_slide(slide_layout)
apply_gradient_background(slide)
# 添加主标题
left = Cm(1.5)
top = Cm(1.5)
width = Cm(30)
height = Cm(2)
title_box = slide.shapes.add_textbox(left, top, width, height)
tf = title_box.text_frame
tf.word_wrap = True
tf.clear()
p = tf.add_paragraph()
p.text = title_text
p.font.size = Pt(36)
p.font.color.rgb = RGBColor(255, 255, 255)
p.font.bold = True
# 添加二级标题(如果有)
if subtitle_text:
left = Cm(1.5)
top = Cm(3)
width = Cm(30)
height = Cm(1.5)
subtitle_box = slide.shapes.add_textbox(left, top, width, height)
tf = subtitle_box.text_frame
tf.word_wrap = True
tf.clear()
p = tf.add_paragraph()
p.text = subtitle_text
p.font.size = Pt(24)
p.font.color.rgb = RGBColor(200, 230, 255) # 浅蓝色
p.font.italic = True
# 添加内容区域,多级列表
if content_items:
# 添加内容文字
left_text = Cm(2)
top_text = Cm(5.5 if subtitle_text else 4.5)
width_text = Cm(36)
height_text = Cm(8)
text_box = slide.shapes.add_textbox(left_text, top_text, width_text, height_text)
tf = text_box.text_frame
# 启用自动换行
tf.word_wrap = True
tf.clear()
for item in content_items:
p = tf.add_paragraph()
p.text = item["text"]
p.font.size = Pt(item.get("size", 18))
p.font.color.rgb = RGBColor(200, 230, 255)
p.space_after = Pt(item.get("space_after", 8))
p.level = item.get("level", 0)
if item.get("bold", False):
p.font.bold = True
if item.get("bullet", False):
p.text = "" + p.text
return slide
def create_table_slide(prs, title_text, subtitle_text, headers, data,content):
"""创建带表格的数据页"""
slide = create_content_slide(prs, title_text, subtitle_text,content)
# 计算表格位置和大小
left = Cm(2)
top = Cm(8 if content else 7) #有内容则朝下一些
width = Cm(26.5)
height = Cm(12 if len(data) >10 else 8) #表格数据比较多
# 创建表格 (行数=数据行数+1列数=标题数)
rows = len(data) + 1
cols = len(headers)
table = slide.shapes.add_table(rows, cols, left, top, width, height).table
# 设置表格样式
table.first_row = True # 强调第一行
table.horz_banding = True # 横向条纹
# 设置表头
for col_idx, header in enumerate(headers):
cell = table.cell(0, col_idx)
cell.text = header
cell.fill.solid()
cell.fill.fore_color.rgb = RGBColor(0, 64, 128) # 深蓝色表头
cell.text_frame.paragraphs[0].font.color.rgb = RGBColor(255, 255, 255)
cell.text_frame.paragraphs[0].font.bold = True
cell.text_frame.paragraphs[0].alignment = PP_ALIGN.CENTER
# 填充表格数据
for row_idx, row_data in enumerate(data, start=1):
for col_idx, cell_data in enumerate(row_data):
cell = table.cell(row_idx, col_idx)
cell.text = str(cell_data)
cell.fill.solid()
cell.fill.fore_color.rgb = RGBColor(255, 255, 255)
cell.fill.fore_color.alpha = 0.2 # 半透明白色
cell.text_frame.paragraphs[0].font.color.rgb = RGBColor(0, 0, 0) # 黑色文字
cell.text_frame.paragraphs[0].alignment = PP_ALIGN.CENTER
# #边框线(赞不支持)
# for border_side in ["top", "bottom", "left", "right"]:
# border = getattr(cell, f"{border_side}_border")
# border.fill.background() # 清除任何填充色,确保只应用线条样式
# border.color.rgb = RGBColor(0, 64, 128)
# border.size = Cm(0.1)
# 设置表格边框
for cell in table.iter_cells():
cell.margin_left = Cm(0.1)
cell.margin_right = Cm(0.1)
cell.margin_top = Cm(0.05)
cell.margin_bottom = Cm(0.05)
cell.vertical_anchor = MSO_ANCHOR.MIDDLE
return slide
def create_chart_slide(prs, title_text, subtitle_text, chart_type, data):
"""创建图表的数据页"""
slide = create_content_slide(prs, title_text, subtitle_text)
#半透明白色背景
left_content = Cm(2)
top_content = Cm(7 if subtitle_text else 6) # 根据是否有二级标题调整位置
width_content = Cm(28.5)
height_content = Cm(15 if len(data) >10 else 12 )
content_box = slide.shapes.add_shape(
MSO_SHAPE.RECTANGLE, left_content, top_content, width_content, height_content
)
content_box.fill.solid()
content_box.fill.fore_color.rgb = RGBColor(255, 255, 255)
content_box.fill.fore_color.alpha = 0.2 # 20%透明度
content_box.line.color.rgb = RGBColor(200, 230, 255)
# 和前端颜色一致
colors = [
"#FF6384", # 对应 rgba(255,99,132,1)
"#36A2EB", # 对应 rgba(54, 162, 235, 1)
"#FFCE56", # 对应 rgba(255, 206, 86, 1)
"#4BC0C0", # 对应 rgba(75, 192, 192, 1)
"#9966FF", # 对应 rgba(153, 102, 255, 1)
"#FF9F40", # 对应 rgba(255, 159, 64, 1)
"#FF6347", # 新增:对应 rgba(255, 99, 71, 1),番茄红
"#90EE90", # 新增:对应 rgba(144, 238, 144, 1),淡绿
"#ADD8E6", # 新增:对应 rgba(173, 216, 230, 1),浅天蓝
"#FFC0CB" # 新增:对应 rgba(255, 192, 203, 1),浅粉
]
# 添加图表数据
if data:
chart_data = ChartData()
chart_data.categories = data["categories"]
del data["categories"]
for lable, series in data.items():
chart_data.add_series(lable, series)
# 添加图表
left_chart = Cm(3)
top_chart = Cm(8)
width_chart = Cm(25)
height_chart = Cm(12 if len(data) >10 else 10 )
if chart_type=="[chart][bar]":
xl_chart_type=XL_CHART_TYPE.COLUMN_CLUSTERED
elif chart_type=="[chart][line]":
xl_chart_type=XL_CHART_TYPE.LINE
elif chart_type=="[chart][bar_line]":
xl_chart_type=XL_CHART_TYPE.BAR_CLUSTERED
elif chart_type=="[chart][area]":
xl_chart_type=XL_CHART_TYPE.AREA
else:
xl_chart_type=XL_CHART_TYPE.BAR_STACKED
chart = slide.shapes.add_chart(
xl_chart_type,left_chart, top_chart, width_chart, height_chart, chart_data
)
# 设置数据标签
plot = chart.chart.plots[0]
plot.has_data_labels = True
data_labels = plot.data_labels
data_labels.show_value = True # 显示数值
data_labels.position = XL_LABEL_POSITION.OUTSIDE_END # 标签位置
# 设置数据标签的字体大小
data_labels.font.size = Pt(6) # 假设你想要设置字体大小为12磅
# 设置图例
chart.chart.has_legend = True # 确保图例可见
chart.chart.legend.position = XL_LEGEND_POSITION.BOTTOM # 设置图例位置为底部
chart.chart.legend.include_in_layout = False # 不让图例覆盖图表
# 设置图表颜色
if len(chart.chart.series) ==1: #只有一个series
for idx, point in enumerate(chart.chart.series[0].points):
if idx < len(colors): # 确保颜色数量足够
fill = point.format.fill
fill.solid()
fill.fore_color.alpha = 0.2 # 20%透明度
fill.fore_color.rgb = RGBColor.from_string(colors[idx][1:])
else:
#设置图表颜色 - 为每个系列分配一种颜色
for idx, serie in enumerate(chart.chart.series):
if idx < len(colors): # 确保颜色数量足够
fill = serie.format.fill
fill.solid()
fill.fore_color.rgb = RGBColor.from_string(colors[idx][1:]) # 移除前缀'#'
# # 设置图表颜色以匹配主题
# chart.chart.plots[0].series[0].format.fill.solid()
# chart.chart.plots[0].series[0].format.fill.fore_color.rgb = RGBColor(100, 180, 255)
# chart.chart.plots[0].series[1].format.fill.solid()
# chart.chart.plots[0].series[1].format.fill.fore_color.rgb = RGBColor(200, 230, 255)
return slide
def create_image_layout_slide(prs, title_text, subtitle_text, images,content_items=None):
"""创建图片布局页面"""
slide = create_content_slide(prs, title_text, subtitle_text)
# 清除默认内容区域
for shape in slide.shapes:
if shape.shape_type == MSO_SHAPE.ROUNDED_RECTANGLE:
sp = shape._element
sp.getparent().remove(sp)
# 根据图片数量决定布局
if len(images) == 1:
# 单张大图布局
left = Cm(3)
top = Cm(5 if subtitle_text else 4)+Cm(0.5)
width = Cm(26)
height = Cm(15)
pic = slide.shapes.add_picture(images[0]["path"], left, top, width, height)
# 添加图片说明
if "caption" in images[0]:
left_cap = Cm(3)
top_cap = Cm(19 if subtitle_text else 20)
width_cap = Cm(25)
height_cap = Cm(1)
cap = slide.shapes.add_textbox(left_cap, top_cap, width_cap, height_cap)
tf = cap.text_frame
p = tf.add_paragraph()
if images[0]["caption"]:
p.text = images[0]["caption"]
else:
p.text = "主图"
p.font.size = Pt(14)
p.font.color.rgb = RGBColor(200, 230, 255)
p.alignment = PP_ALIGN.CENTER
# 在图片的右侧添加内容区域,可以多级列表
if content_items:
# 添加内容文字
left_text = Cm(30)
top_text = Cm(5.5 if subtitle_text else 4.5)
width_text = Cm(5)
height_text = Cm(18)
text_box = slide.shapes.add_textbox(left_text, top_text, width_text, height_text)
tf = text_box.text_frame
# 启用自动换行
tf.word_wrap = True
tf.clear()
for item in content_items:
p = tf.add_paragraph()
p.text = item["text"]
p.font.size = Pt(item.get("size", 18))
p.font.color.rgb = RGBColor(200, 230, 255)
p.space_after = Pt(item.get("space_after", 8))
p.level = item.get("level", 0)
if item.get("bold", False):
p.font.bold = True
if item.get("bullet", False):
p.text = "" + p.text
elif len(images) == 2:
# 两张图片并排布局,大小一致
# 第一张图片
left1 = Cm(2)
top1 = Cm(5 if subtitle_text else 4)+Cm(0.5)
width1 = Cm(13)
height1 = Cm(15)
pic1 = slide.shapes.add_picture(images[0]["path"], left1, top1, width1, height1)
# 第二张图片
left2 = Cm(16)
top2 = Cm(5 if subtitle_text else 4)+Cm(0.5)
width2 = Cm(13)
height2 = Cm(15)
pic2 = slide.shapes.add_picture(images[1]["path"], left2, top2, width2, height2)
# 添加图片说明
for i, img in enumerate(images):
if "caption" in img:
left_cap = Cm(2 if i == 0 else 19)
top_cap = Cm(17 if subtitle_text else 16)
width_cap = Cm(13)
height_cap = Cm(1)
cap = slide.shapes.add_textbox(left_cap, top_cap, width_cap, height_cap)
tf = cap.text_frame
p = tf.add_paragraph()
if img["caption"]:
p.text = img["caption"]
else:
p.text = f"图片{i+1}"
p.font.size = Pt(12)
p.font.color.rgb = RGBColor(200, 230, 255)
p.alignment = PP_ALIGN.CENTER
# 在图片的右侧添加内容区域,可以多级列表
if content_items:
# 添加内容文字
left_text = Cm(30)
top_text = Cm(5.5 if subtitle_text else 4.5)
width_text = Cm(5)
height_text = Cm(18)
text_box = slide.shapes.add_textbox(left_text, top_text, width_text, height_text)
tf = text_box.text_frame
# 启用自动换行
tf.word_wrap = True
tf.clear()
for item in content_items:
p = tf.add_paragraph()
p.text = item["text"]
p.font.size = Pt(item.get("size", 18))
p.font.color.rgb = RGBColor(200, 230, 255)
p.space_after = Pt(item.get("space_after", 8))
p.level = item.get("level", 0)
if item.get("bold", False):
p.font.bold = True
if item.get("bullet", False):
p.text = "" + p.text
elif len(images) >= 3:
# 三张以上图片网格布局
# 第一行,三张
if len(images)==3:
img_size = Cm(12)
else:
img_size = Cm(8)
gap = Cm(1)
for i in range(min(3, len(images))):
left = Cm(3) + (img_size + gap) * i
top = Cm(5 if subtitle_text else 4)+Cm(0.5)
pic = slide.shapes.add_picture(images[i]["path"], left, top, img_size, img_size)
if "caption" in images[i]:
left_cap = left
top_cap = top + img_size + Cm(0.5)
width_cap = img_size
height_cap = Cm(1)
cap = slide.shapes.add_textbox(left_cap, top_cap, width_cap, height_cap)
tf = cap.text_frame
p = tf.add_paragraph()
if images[i]["caption"]:
p.text = images[i]["caption"]
else:
p.text = f"图片{i+1}"
p.font.size = Pt(10)
p.font.color.rgb = RGBColor(200, 230, 255)
p.alignment = PP_ALIGN.CENTER
# 第二行如果有4张以上图片
img_size = Cm(4)
gap = Cm(1)
for i in range(3, len(images)):
left = Cm(3) + (img_size + gap) * (i - 3)
top = Cm(16 if subtitle_text else 15)
pic = slide.shapes.add_picture(images[i]["path"], left, top, img_size, img_size)
if "caption" in images[i] and images[i]["caption"]:
left_cap = left
top_cap = top + img_size + Cm(0.5)
width_cap = img_size
height_cap = Cm(1)
cap = slide.shapes.add_textbox(left_cap, top_cap, width_cap, height_cap)
tf = cap.text_frame
p = tf.add_paragraph()
p.text = images[i]["caption"]
p.font.size = Pt(10)
p.font.color.rgb = RGBColor(200, 230, 255)
p.alignment = PP_ALIGN.CENTER
return slide
# ===== 封面幻灯片 =====
def create_main_slide(prs, title_text, subject_text,content=None):
# ===== 封面幻灯片 =====
slide_layout = prs.slide_layouts[6] # 空白布局
slide = prs.slides.add_slide(slide_layout)
apply_gradient_background(slide)
#添加宽屏主题
left = Cm(2)
top = Cm(8)
width = Cm(36)
height = Cm(6)
subtitle_box = slide.shapes.add_textbox(left, top, width, height)
tf = subtitle_box.text_frame
tf.word_wrap = True
p = tf.add_paragraph()
p.text = title_text
p.font.size = Pt(66)
p.font.color.rgb = RGBColor(200, 230, 255)
p.alignment = PP_ALIGN.LEFT
# 添加副标题
if subject_text:
left = Cm(1.5)
top = Cm(1.5)
width = Cm(36)
height = Cm(4)
title_box = slide.shapes.add_textbox(left, top, width, height)
tf = title_box.text_frame
tf.word_wrap = True
p = tf.add_paragraph()
p.text = subject_text
p.font.size = Pt(36)
p.font.color.rgb = RGBColor(255, 255, 255)
p.font.bold = True
p.alignment = PP_ALIGN.LEFT
# 添加其它标题
if content:
i=0
for line in content:
left = Cm(12)
top = Cm(12+i)
width = Cm(16)
height = Cm(2)
subtitle_box = slide.shapes.add_textbox(left, top, width, height)
tf = subtitle_box.text_frame
p = tf.add_paragraph()
p.text = line["text"]
p.font.size = Pt(28)
p.font.color.rgb = RGBColor(200, 230, 255)
p.alignment = PP_ALIGN.CENTER
i+=3
#end
# ===== 结束幻灯片 =====
def create_end_slide(prs,title,subtitle):
slide = create_content_slide(
prs,
title,
subtitle
)
img_width = Cm(8)
img_height = Cm(4)
print(prs.slide_width,prs.slide_width/Cm(1),prs.slide_width/Cm(1)/2-3)
left = Cm(prs.slide_width/Cm(1)/2-3) #屏幕中央
top = Cm(8)
#添加logo
if gcfg["fs"]["logo"]!="":
pic = slide.shapes.add_picture(f'{gcfg["fs"]["path"]}/img/{gcfg["fs"]["logo"]}', left, top, img_width, img_height)
else:
pic = slide.shapes.add_picture(f'ui/images/logo2.jpg', left, top, img_width, img_height)
# 在内容区域添加slogan和网址
left = Cm(12)
top = Cm(12)
width = Cm(16)
height = Cm(3)
contact_box = slide.shapes.add_textbox(left, top, width, height)
tf = contact_box.text_frame
p = tf.add_paragraph()
p.text = gcfg["fs"]["slogan"]
p.font.size = Pt(28)
p.font.color.rgb = RGBColor(255, 255, 255)
p.font.bold = True
p.alignment = PP_ALIGN.CENTER
p.space_after = Pt(16)
p = tf.add_paragraph()
p.text = gcfg["fs"]["url"]
p.font.size = Pt(18)
p.font.color.rgb = RGBColor(200, 230, 255)
p.alignment = PP_ALIGN.CENTER
p.space_before = Pt(10)
#入口函数根据html创建ppt
def create_unified_ppt(html,output_filename):
# 创建一个16:9宽屏演示文稿对象
prs = Presentation()
#prs.slide_width = Inches(13.333) # 16:9的宽度
#prs.slide_height = Inches(7.5) # 16:9的高度
prs.slide_width = Inches(16) # 16:9的宽度
prs.slide_height = Inches(9) # 16:9的高度
ppt = parse_html_to_ppt(html)
for slide in ppt:
if slide["type"]=="main":
create_main_slide(prs,slide["title"],slide["subtitle"],slide["content"])
elif slide["type"]=="text":
create_content_slide(
prs,slide["title"],slide["subtitle"],slide["content"]
)
elif slide["type"]=="image":
create_image_layout_slide(
prs,slide["title"],slide["subtitle"],slide["images"],slide["content"]
)
elif slide["type"]=="chart":
create_chart_slide(
prs,slide["title"],slide["subtitle"],slide["chart_type"],slide["data"]
)
elif slide["type"]=="table":
create_table_slide(
prs,slide["title"],slide["subtitle"],slide["header"],slide["data"],slide["content"]
)
else:
pass
create_end_slide(prs,"感谢聆听","欢迎指正")
# 保存演示文稿
prs.save(output_filename)
if __name__ == "__main__":
html_string ="""
<h1>K3GPT数据分析智能体</h1>
<h3>三国工资表.xls</h3>
<h2>问题</h2><h3>按如下分析三国详情1不同集团的人数2不同集团不同级别的人数3不同集团的平均服务年龄4不同集团工种帝王人数</h3>
<h2>数据分析</h2><h3>集团分布</h3><p>[chart][bar]</p><pre><code >{\"categories\":[\"\",\"\",\"\",\"\"],\"count\":[3,3,3,2]}</code></pre>
<h3>集团-&gt;职级的分布</h3><p>[chart][bar]</p><pre><code >{\"L4\":[10,9,7,1],\"L3\":[8,0,4,1],\"L5\":[6,9,6,3],\"L6\":[4,5,3,2],\"L7\":[3,4,3,2],\"L8\":[2,2,2,0],\"L9\":[1,1,1,0],\"L2\":[0,0,1,0],\"categories\":[\"\",\"\",\"\",\"\"]}</code></pre>
<h3>不同集团对应的司龄(年)的常见六指标分析</h3><p>[chart][bar]</p><pre><code >{\"categories\":[\"\",\"\",\"\",\"\"],\"总和\":[397,586,179,560],\"平均值\":[14.703703703703704,19.533333333333335,19.88888888888889,16.470588235294116],\"中位数\":[12,19,20,15],\"最小值\":[5,10,8,5],\"最大值\":[40,35,30,40],\"个数\":[27,30,9,34]}</code></pre>
<h3>表格1</h3><table style=\"width: auto;\"><tbody><tr><th colSpan=\"1\" rowSpan=\"1\" width=\"auto\">序号</th><th colSpan=\"1\" rowSpan=\"1\" width=\"auto\">姓名</th><th colSpan=\"1\" rowSpan=\"1\" width=\"auto\">集团</th><th colSpan=\"1\" rowSpan=\"1\" width=\"auto\">工种</th><th colSpan=\"1\" rowSpan=\"1\" width=\"auto\">职级</th><th colSpan=\"1\" rowSpan=\"1\" width=\"auto\">工资(万)</th><th colSpan=\"1\" rowSpan=\"1\" width=\"auto\">司龄(年)</th></tr><tr><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">1</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">曹操</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">魏</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">帝王</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">L9</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">10</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">30</td></tr><tr><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">2</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">刘备</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">蜀</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">帝王</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">L9</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">9.8</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">25</td></tr><tr><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">3</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">孙权</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">吴</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">帝王</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">L9</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">9.5</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">40</td></tr><tr><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">53</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">孙策</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">吴</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">帝王</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">L8</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">8</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">10</td></tr><tr><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">54</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">孙坚</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">吴</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">帝王</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">L7</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">7</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">15</td></tr><tr><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">55</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">刘禅</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">蜀</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">帝王</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">L7</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">6</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">40</td></tr><tr><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">56</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">曹丕</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">魏</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">帝王</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">L8</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">8.5</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">15</td></tr><tr><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">57</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">曹叡</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">魏</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">帝王</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">L7</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">7</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">10</td></tr><tr><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">97</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">袁绍</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">无</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">帝王</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">L7</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">6</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">20</td></tr><tr><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">98</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">袁术</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">无</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">帝王</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">L6</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">5</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">15</td></tr><tr><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">100</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">刘表</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">无</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">帝王</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">L6</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">4.5</td><td colSpan=\"1\" rowSpan=\"1\" width=\"auto\">25</td></tr></tbody></table>
<h2>小结</h2><p>根据分析结果,三国工资表的详细情况如下:</p><ol><li>集团人数分布 吴国34人 魏国30人 蜀国27人 无集团9人</li><li>集团-职级人数分布 吴国主要职级为L410人、L38人、L56人 魏国最高职级达L91人L59人占比较大 蜀国职级分布较均衡L47人、L56人为主</li><li>平均服务年龄 魏国平均19.53年(最高) 吴国16.47年 蜀国14.70年(最低) 无集团19.89年(可能包含临时人员)</li><li>帝王工种分布 吴国、魏国、无集团各有3名帝王蜀国2名 帝王多集中在高级职级L6-L9</li></ol><p><strong>分析点评</strong> &nbsp;</p><ul><li>魏国和无集团的平均服务年龄显著高于其他集团,可能与人员稳定性或招聘策略有关。 &nbsp;</li><li>帝王工种在吴、魏、无集团分布较均但蜀国仅2人可能需关注其人才结构。 &nbsp;</li><li>职级分布显示魏国存在较高职级人员,而蜀国职级整体偏低,可能反映组织架构差异。</li></ul>",
"""
# 使用前请替换示例图片路径为实际图片路径
create_unified_ppt(html_string,"test.pptx")
print("带生成完成!")