产品方案 PPT 是交付物,之前只存在于本机,换开发机后会丢,所以入库。 - 3 份 pptx:智能测肤方案(正版 1.3MB / 深色版 1.3MB)+ 购物小程序方案模版(0.1MB); 模版是 build_taobao_proposal_from_template.py 的输入,必须一起入库 - 3 个生成脚本:把硬编码的 D:\juzi\... 改为相对仓库根(Path(__file__).parents[1]), 渲染用的截图素材 temp/ppt_assets 属临时产物,加进 .gitignore - build_taobao_proposal_pptx.py 是更早的一版(产物 AI智能测肤...pptx 当前不在仓库), 保留用于追溯版式 - 新增 .gitignore:/sdk-java-35479221/(淘宝开放平台自动生成的 Java SDK, 对应已废弃的 AppKey 35479221,当前架构走 HTTP 透传,不引 SDK)
368 lines
18 KiB
Python
368 lines
18 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""Generate Taobao mini-program product proposal PPTX (no company name).
|
||
|
||
注意:这一份是更早的版本(输出 AI智能测肤-淘宝小程序产品方案.pptx,当前仓库里没有该成品),
|
||
保留用于追溯版式。现行版本见 build_taobao_proposal_from_template.py / build_taobao_proposal_dark.py。
|
||
"""
|
||
from pathlib import Path
|
||
|
||
from pptx import Presentation
|
||
from pptx.util import Emu, Pt
|
||
from pptx.dml.color import RGBColor
|
||
from pptx.enum.shapes import MSO_SHAPE
|
||
from pptx.enum.text import PP_ALIGN
|
||
from pptx.oxml.ns import qn
|
||
from lxml import etree
|
||
|
||
OUT = str(Path(__file__).resolve().parents[1] / "AI智能测肤-淘宝小程序产品方案.pptx")
|
||
|
||
W, H = 24384000, 13716000 # 16:9
|
||
|
||
BG = RGBColor(0x0F, 0x1A, 0x26)
|
||
BG_CARD = RGBColor(0x18, 0x27, 0x38)
|
||
ACCENT = RGBColor(0x2E, 0xC4, 0xB6)
|
||
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
|
||
MUTED = RGBColor(0xA8, 0xB4, 0xC0)
|
||
LINE = RGBColor(0x2A, 0x3B, 0x4F)
|
||
TITLE = RGBColor(0xF2, 0xF6, 0xFA)
|
||
PANEL = RGBColor(0x14, 0x2A, 0x3C)
|
||
|
||
|
||
def set_run(run, size=18, bold=False, color=WHITE, font="微软雅黑"):
|
||
run.font.size = Pt(size)
|
||
run.font.bold = bold
|
||
run.font.color.rgb = color
|
||
run.font.name = font
|
||
rPr = run._r.get_or_add_rPr()
|
||
ea = rPr.find(qn("a:ea"))
|
||
if ea is None:
|
||
ea = etree.SubElement(rPr, qn("a:ea"))
|
||
ea.set("typeface", font)
|
||
|
||
|
||
def add_bg(slide, color=BG):
|
||
shape = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, W, H)
|
||
shape.fill.solid()
|
||
shape.fill.fore_color.rgb = color
|
||
shape.line.fill.background()
|
||
spTree = slide.shapes._spTree
|
||
sp = shape._element
|
||
spTree.remove(sp)
|
||
spTree.insert(2, sp)
|
||
|
||
|
||
def add_bar(slide, left, top, width=Emu(457200), height=Emu(70000)):
|
||
bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, left, top, width, height)
|
||
bar.fill.solid()
|
||
bar.fill.fore_color.rgb = ACCENT
|
||
bar.line.fill.background()
|
||
|
||
|
||
def add_text(slide, left, top, width, height, text, size=18, bold=False, color=WHITE, align=PP_ALIGN.LEFT):
|
||
box = slide.shapes.add_textbox(left, top, width, height)
|
||
tf = box.text_frame
|
||
tf.word_wrap = True
|
||
p = tf.paragraphs[0]
|
||
p.alignment = align
|
||
run = p.add_run()
|
||
run.text = text
|
||
set_run(run, size=size, bold=bold, color=color)
|
||
return box
|
||
|
||
|
||
def add_paras(slide, left, top, width, height, lines, size=15, color=MUTED, bold=False, gap=10):
|
||
box = slide.shapes.add_textbox(left, top, width, height)
|
||
tf = box.text_frame
|
||
tf.word_wrap = True
|
||
for i, line in enumerate(lines):
|
||
p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
|
||
p.alignment = PP_ALIGN.LEFT
|
||
p.space_after = Pt(gap)
|
||
run = p.add_run()
|
||
run.text = line
|
||
set_run(run, size=size, bold=bold, color=color)
|
||
return box
|
||
|
||
|
||
def add_card(slide, left, top, width, height):
|
||
card = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, left, top, width, height)
|
||
card.fill.solid()
|
||
card.fill.fore_color.rgb = BG_CARD
|
||
card.line.color.rgb = LINE
|
||
card.line.width = Pt(1)
|
||
return card
|
||
|
||
|
||
def blank(prs):
|
||
return prs.slides.add_slide(prs.slide_layouts[6])
|
||
|
||
|
||
def section_title(slide, num, title):
|
||
add_bg(slide)
|
||
add_text(slide, Emu(914400), Emu(420000), Emu(2000000), Emu(450000), num, size=28, bold=True, color=ACCENT)
|
||
add_text(slide, Emu(1600200), Emu(450000), Emu(14000000), Emu(450000), title, size=28, bold=True, color=TITLE)
|
||
add_bar(slide, Emu(914400), Emu(950000))
|
||
|
||
|
||
def build():
|
||
prs = Presentation()
|
||
prs.slide_width = W
|
||
prs.slide_height = H
|
||
|
||
# ---- 1 Cover ----
|
||
s = blank(prs)
|
||
add_bg(s)
|
||
panel = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, Emu(3200400), H)
|
||
panel.fill.solid()
|
||
panel.fill.fore_color.rgb = PANEL
|
||
panel.line.fill.background()
|
||
add_bar(s, Emu(4000500), Emu(4200000), Emu(914400), Emu(90000))
|
||
add_text(s, Emu(4000500), Emu(4500000), Emu(17000000), Emu(900000),
|
||
"AI 智能测肤", size=44, bold=True, color=TITLE)
|
||
add_text(s, Emu(4000500), Emu(5450000), Emu(17000000), Emu(700000),
|
||
"淘宝小程序产品方案", size=32, bold=True, color=ACCENT)
|
||
add_text(s, Emu(4000500), Emu(6350000), Emu(17000000), Emu(500000),
|
||
"在线肌肤检测 · 个性化护理建议 · 精准商品推荐", size=16, color=MUTED)
|
||
add_text(s, Emu(4000500), Emu(11000000), Emu(8000000), Emu(400000),
|
||
"自研类型申请材料", size=12, color=MUTED)
|
||
|
||
# ---- 2 Product positioning (replaces company background) ----
|
||
s = blank(prs)
|
||
add_bg(s)
|
||
add_bar(s, Emu(914400), Emu(685800))
|
||
add_text(s, Emu(914400), Emu(420000), Emu(16000000), Emu(500000),
|
||
"产品定位", size=28, bold=True, color=TITLE)
|
||
add_text(s, Emu(914400), Emu(1200000), Emu(20000000), Emu(600000),
|
||
"面向淘宝美妆消费者的「检测—报告—推荐—购买」一站式肌肤服务小程序", size=18, color=MUTED)
|
||
|
||
cards = [
|
||
("核心能力", "基于面部图像的 AI 肌肤分析,输出综合评分、肌龄、七项分项结论与护理建议。"),
|
||
("转化路径", "报告结果直接关联店铺商品,按肌龄与肤质状态推荐适配精华,缩短决策路径。"),
|
||
("适用场景", "品牌旗舰店导购、直播/详情页引流、会员复购关怀、线下到线上测肤体验延伸。"),
|
||
("差异价值", "不是通用展示页,而是可闭环的「测肤服务 + 商品转化」能力,补足平台现有内容与货架之间的决策缺口。"),
|
||
]
|
||
for i, (t, d) in enumerate(cards):
|
||
col = i % 2
|
||
row = i // 2
|
||
left = Emu(914400 + col * 11000000)
|
||
top = Emu(2100000 + row * 4200000)
|
||
add_card(s, left, top, Emu(10000000), Emu(3600000))
|
||
add_text(s, left + Emu(400000), top + Emu(350000), Emu(9000000), Emu(500000),
|
||
t, size=18, bold=True, color=ACCENT)
|
||
add_text(s, left + Emu(400000), top + Emu(1000000), Emu(9000000), Emu(2200000),
|
||
d, size=15, color=MUTED)
|
||
|
||
# ---- 3 TOC ----
|
||
s = blank(prs)
|
||
add_bg(s)
|
||
add_text(s, Emu(914400), Emu(420000), Emu(8000000), Emu(500000),
|
||
"目录", size=32, bold=True, color=TITLE)
|
||
add_bar(s, Emu(914400), Emu(1000000))
|
||
toc = [
|
||
("01", "市场调研分析"),
|
||
("02", "小程序方案及价值"),
|
||
("03", "商业化运营规划及目标"),
|
||
]
|
||
for i, (num, name) in enumerate(toc):
|
||
top = Emu(2200000 + i * 2200000)
|
||
circle = s.shapes.add_shape(MSO_SHAPE.OVAL, Emu(914400), top, Emu(1100000), Emu(1100000))
|
||
circle.fill.solid()
|
||
circle.fill.fore_color.rgb = ACCENT
|
||
circle.line.fill.background()
|
||
add_text(s, Emu(914400), top + Emu(280000), Emu(1100000), Emu(600000),
|
||
num, size=22, bold=True, color=BG, align=PP_ALIGN.CENTER)
|
||
add_text(s, Emu(2400000), top + Emu(300000), Emu(12000000), Emu(600000),
|
||
name, size=26, bold=True, color=TITLE)
|
||
|
||
# ---- 4 Market research ----
|
||
s = blank(prs)
|
||
section_title(s, "01", "市场调研分析")
|
||
add_paras(s, Emu(914400), Emu(1300000), Emu(22000000), Emu(11000000), [
|
||
"1)商家价值与痛点 / 消费者诉求",
|
||
"· 商家痛点:美妆选品高度依赖经验,线上货架信息多、决策成本高,难以把「适合我」讲清楚;直播与详情页流量难沉淀为可复访服务。",
|
||
"· 消费者诉求:想知道自己肤质与肌龄、哪里需要改善、该买哪一支精华;希望少踩雷、有依据、可即时下单。",
|
||
"· 本方案以测肤报告建立信任,再以肌龄分档推荐商品,同时满足「专业解释」与「购买决策」两端诉求。",
|
||
"",
|
||
"2)市场规模与商业化空间",
|
||
"· 淘宝美妆护肤为高复购、高客单品类;「工具型服务」可提升停留、互动与转化,适配旗舰店与会员运营。",
|
||
"· 测肤完成后可沉淀历史报告,支撑复购提醒、换季护理与活动召回,形成服务型资产而非一次性流量。",
|
||
"",
|
||
"3)需求原声(典型)",
|
||
"· 「不知道自己适不适合这款抗老精华」 · 「想先测一下再买,别乱试」 · 「报告要能看懂,最好直接告诉我买什么」",
|
||
], size=14, color=MUTED, gap=6)
|
||
|
||
# ---- 5 Solution overview ----
|
||
s = blank(prs)
|
||
section_title(s, "02", "小程序方案及价值")
|
||
add_paras(s, Emu(914400), Emu(1300000), Emu(22000000), Emu(4000000), [
|
||
"1)产品价值与商业价值",
|
||
"· 产品价值:3 步拍摄即可完成 AI 检测,获得综合评分、展示肌龄、七项分析、护理建议与精华推荐。",
|
||
"· 商业价值:把「不确定」变成「可解释的购买理由」,提升详情页/店铺转化与客单匹配度;报告可复访,利于会员沉淀。",
|
||
"",
|
||
"2)商家运用场景与适用行业",
|
||
"· 适用行业:护肤 / 抗老精华 / 功效型美妆、医美周边护理(非诊疗)、品牌会员中心。",
|
||
"· 运用场景:店铺首页入口、商品详情页挂载、直播间互动测肤、会员日/换季活动、新客教育与老客复购。",
|
||
], size=14, color=MUTED, gap=6)
|
||
|
||
# three value cards at bottom
|
||
vals = [
|
||
("降低决策成本", "用检测数据替代主观猜测,减少试错与退货。"),
|
||
("提升转化效率", "报告页直达适配商品,缩短浏览—下单路径。"),
|
||
("沉淀服务资产", "历史报告可回看,支撑持续运营与复购。"),
|
||
]
|
||
for i, (t, d) in enumerate(vals):
|
||
left = Emu(914400 + i * 7500000)
|
||
top = Emu(8200000)
|
||
add_card(s, left, top, Emu(7000000), Emu(3800000))
|
||
add_text(s, left + Emu(350000), top + Emu(450000), Emu(6200000), Emu(600000),
|
||
t, size=16, bold=True, color=ACCENT)
|
||
add_text(s, left + Emu(350000), top + Emu(1300000), Emu(6200000), Emu(2000000),
|
||
d, size=14, color=MUTED)
|
||
|
||
# ---- 6 Core features ----
|
||
s = blank(prs)
|
||
section_title(s, "02", "产品核心功能")
|
||
feats = [
|
||
("授权登录", "淘宝账号授权登录,协议确认后进入服务;账号与报告隔离存储。"),
|
||
("基础档案", "出生年月、地区、肤质等信息一次填写、本地缓存,用于个性化展示。"),
|
||
("三步拍摄", "正脸 / 右脸 / 左脸手动拍摄或相册选图,压缩后上传检测。"),
|
||
("AI 检测报告", "综合评分、肌龄、三维度(紧塑力 / 透亮感 / 稳韧力)与七项分项结论。"),
|
||
("护理与推荐", "护理关注点选三、精华产品按肌龄分档推荐,并可跳转店铺商品。"),
|
||
("历史报告", "保留本人最近多份完整报告,支持复访对比与复购触达。"),
|
||
]
|
||
for i, (t, d) in enumerate(feats):
|
||
col = i % 3
|
||
row = i // 3
|
||
left = Emu(914400 + col * 7500000)
|
||
top = Emu(1400000 + row * 5200000)
|
||
add_card(s, left, top, Emu(7000000), Emu(4600000))
|
||
add_text(s, left + Emu(350000), top + Emu(450000), Emu(6200000), Emu(700000),
|
||
t, size=17, bold=True, color=ACCENT)
|
||
add_text(s, left + Emu(350000), top + Emu(1400000), Emu(6200000), Emu(2800000),
|
||
d, size=14, color=MUTED)
|
||
|
||
# ---- 7 Product flow ----
|
||
s = blank(prs)
|
||
section_title(s, "02", "产品核心链路")
|
||
add_text(s, Emu(914400), Emu(1200000), Emu(22000000), Emu(400000),
|
||
"用户从进入到购买的主路径(交互链路)", size=15, color=MUTED)
|
||
|
||
steps = [
|
||
("1", "启动页", "协议同意\n淘宝授权"),
|
||
("2", "基础档案", "年龄地区\n肤质选择"),
|
||
("3", "拍摄检测", "正/右/左脸\n上传分析"),
|
||
("4", "肌肤报告", "评分肌龄\n七项结论"),
|
||
("5", "护理推荐", "关注选三\n精华匹配"),
|
||
("6", "商品转化", "跳转详情\n完成下单"),
|
||
]
|
||
for i, (n, t, d) in enumerate(steps):
|
||
left = Emu(600000 + i * 3900000)
|
||
top = Emu(2200000)
|
||
circle = s.shapes.add_shape(MSO_SHAPE.OVAL, left + Emu(1100000), top, Emu(900000), Emu(900000))
|
||
circle.fill.solid()
|
||
circle.fill.fore_color.rgb = ACCENT
|
||
circle.line.fill.background()
|
||
add_text(s, left + Emu(1100000), top + Emu(220000), Emu(900000), Emu(500000),
|
||
n, size=22, bold=True, color=BG, align=PP_ALIGN.CENTER)
|
||
add_text(s, left, top + Emu(1200000), Emu(3200000), Emu(500000),
|
||
t, size=16, bold=True, color=TITLE, align=PP_ALIGN.CENTER)
|
||
add_text(s, left, top + Emu(1800000), Emu(3200000), Emu(1200000),
|
||
d, size=13, color=MUTED, align=PP_ALIGN.CENTER)
|
||
if i < len(steps) - 1:
|
||
add_text(s, left + Emu(3000000), top + Emu(250000), Emu(800000), Emu(500000),
|
||
"→", size=22, bold=True, color=ACCENT, align=PP_ALIGN.CENTER)
|
||
|
||
add_card(s, Emu(914400), Emu(6200000), Emu(22500000), Emu(5800000))
|
||
add_paras(s, Emu(1300000), Emu(6600000), Emu(21000000), Emu(5000000), [
|
||
"七项分析:轮廓紧致 · 皱纹细纹 · 肤色色素 · 平滑细腻 · 水分水润 · 敏感泛红 · 痘痘皮损",
|
||
"三维度评分:紧塑力 / 透亮感 / 稳韧力,支撑综合结论与商品分档推荐",
|
||
"推荐逻辑:以展示肌龄为主,结合维度评分在边界档位微调,匹配 25+ / 35+ / 50+ 精华组别",
|
||
"权限说明:拍摄需相机权限;拒绝后可走相册选图;相册权限独立申请,不循环弹窗",
|
||
], size=14, color=MUTED, gap=12)
|
||
|
||
# ---- 8 Differentiation ----
|
||
s = blank(prs)
|
||
section_title(s, "02", "与平台现有能力的差异 / 互补")
|
||
rows = [
|
||
("对比点", "平台现有货架 / 内容能力", "本小程序互补价值"),
|
||
("决策依据", "以图文、评价、直播讲解为主", "以本人面部检测数据生成可解释报告"),
|
||
("选品方式", "用户自行对比商品参数", "按肌龄与肤质状态主动匹配商品"),
|
||
("服务形态", "一次性浏览与成交", "可保存历史报告,支撑复访与复购"),
|
||
("商家侧价值", "流量与成交工具丰富", "增加「专业测肤服务」入口,提升信任与转化"),
|
||
]
|
||
for r, row in enumerate(rows):
|
||
top = Emu(1400000 + r * 1800000)
|
||
for c, cell in enumerate(row):
|
||
left = Emu(914400 + c * 7500000)
|
||
w = Emu(7000000)
|
||
add_card(s, left, top, w, Emu(1550000))
|
||
color = ACCENT if r == 0 or c == 0 else MUTED
|
||
bold = r == 0 or c == 0
|
||
add_text(s, left + Emu(250000), top + Emu(400000), Emu(6500000), Emu(900000),
|
||
cell, size=13, bold=bold, color=color)
|
||
|
||
# ---- 9 Commercial plan ----
|
||
s = blank(prs)
|
||
section_title(s, "03", "商业化运营规划及目标")
|
||
add_paras(s, Emu(914400), Emu(1300000), Emu(22000000), Emu(5000000), [
|
||
"1)目标覆盖",
|
||
"· 首期:服务本品牌淘宝旗舰店及核心护肤线(自研自用),完成测肤—推荐—购买闭环验证。",
|
||
"· 扩展:在验证转化与体验后,可按品牌矩阵店铺复制,或作为会员中心标准服务能力输出。",
|
||
"",
|
||
"2)上线后运营策略与节奏",
|
||
], size=14, color=MUTED, gap=6)
|
||
|
||
phases = [
|
||
("上线期(0–1 月)", "店铺首页 / 详情页入口曝光;引导完成首测;监控拍摄成功率、报告完成率、推荐点击率。"),
|
||
("成长期(1–3 月)", "结合大促与直播挂载测肤;会员专属复测提醒;优化推荐商品与话术转化。"),
|
||
("沉淀期(3 月+)", "历史报告召回;换季护理活动;A/B 测推荐规则;持续提升复测率与连带购买。"),
|
||
]
|
||
for i, (t, d) in enumerate(phases):
|
||
left = Emu(914400 + i * 7500000)
|
||
top = Emu(6200000)
|
||
add_card(s, left, top, Emu(7000000), Emu(5800000))
|
||
add_text(s, left + Emu(350000), top + Emu(400000), Emu(6200000), Emu(900000),
|
||
t, size=15, bold=True, color=ACCENT)
|
||
add_text(s, left + Emu(350000), top + Emu(1500000), Emu(6200000), Emu(3800000),
|
||
d, size=13, color=MUTED)
|
||
|
||
# ---- 10 KPI ----
|
||
s = blank(prs)
|
||
section_title(s, "03", "关键指标与合规说明")
|
||
kpis = [
|
||
("体验指标", "拍摄完成率、检测成功率、报告打开时长、历史回访率"),
|
||
("转化指标", "推荐位点击率、商品详情跳转率、测肤后下单转化、客单价"),
|
||
("运营指标", "日活测肤次数、复测间隔、会员渗透、活动期增量"),
|
||
("合规要点", "明示检测用途;相机/相册按需授权;人脸与报告按账号隔离;不做医疗诊断承诺"),
|
||
]
|
||
for i, (t, d) in enumerate(kpis):
|
||
col = i % 2
|
||
row = i // 2
|
||
left = Emu(914400 + col * 11000000)
|
||
top = Emu(1400000 + row * 5200000)
|
||
add_card(s, left, top, Emu(10000000), Emu(4600000))
|
||
add_text(s, left + Emu(400000), top + Emu(500000), Emu(9000000), Emu(700000),
|
||
t, size=18, bold=True, color=ACCENT)
|
||
add_text(s, left + Emu(400000), top + Emu(1500000), Emu(9000000), Emu(2600000),
|
||
d, size=15, color=MUTED)
|
||
|
||
# ---- 11 Thank you ----
|
||
s = blank(prs)
|
||
add_bg(s)
|
||
panel = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, Emu(3200400), H)
|
||
panel.fill.solid()
|
||
panel.fill.fore_color.rgb = PANEL
|
||
panel.line.fill.background()
|
||
add_bar(s, Emu(4000500), Emu(5200000), Emu(914400), Emu(90000))
|
||
add_text(s, Emu(4000500), Emu(5500000), Emu(16000000), Emu(900000),
|
||
"THANK YOU", size=44, bold=True, color=TITLE)
|
||
add_text(s, Emu(4000500), Emu(6500000), Emu(16000000), Emu(500000),
|
||
"AI 智能测肤 · 淘宝小程序产品方案", size=16, color=MUTED)
|
||
|
||
prs.save(OUT)
|
||
print("saved", OUT, "slides", len(prs.slides))
|
||
|
||
|
||
if __name__ == "__main__":
|
||
build()
|