#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 少儿智慧阅读 - 产品介绍与功能说明 PDF生成脚本 """ from reportlab.lib.pagesizes import A4 from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.units import cm, mm from reportlab.lib.colors import HexColor, white, black from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer, PageBreak, Table, TableStyle, Image, ListFlowable, ListItem) from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont from reportlab.lib import colors import os # 注册中文字体 FONT_PATHS = [ '/System/Library/Fonts/PingFang.ttc', '/System/Library/Fonts/STHeiti Light.ttc', '/System/Library/Fonts/Hiragino Sans GB.ttc', '/Library/Fonts/Arial Unicode.ttf', ] CHINESE_FONT = None for font_path in FONT_PATHS: if os.path.exists(font_path): try: pdfmetrics.registerFont(TTFont('ChineseFont', font_path, subfontIndex=0)) CHINESE_FONT = 'ChineseFont' print(f"使用字体: {font_path}") break except: continue if not CHINESE_FONT: CHINESE_FONT = 'Helvetica' print("警告: 未找到中文字体,使用默认字体") # 颜色定义 PRIMARY_COLOR = HexColor('#1E3A5F') SECONDARY_COLOR = HexColor('#3E7ABF') ACCENT_COLOR = HexColor('#2E5A8F') LIGHT_BG = HexColor('#F5F8FA') TABLE_HEADER_BG = HexColor('#1E3A5F') # 页面设置 PAGE_WIDTH, PAGE_HEIGHT = A4 MARGIN = 2 * cm # 创建样式 def create_styles(): styles = getSampleStyleSheet() # 封面标题 styles.add(ParagraphStyle( name='CoverTitle', fontName=CHINESE_FONT, fontSize=32, leading=40, alignment=TA_CENTER, textColor=PRIMARY_COLOR, spaceAfter=20 )) # 封面副标题 styles.add(ParagraphStyle( name='CoverSubtitle', fontName=CHINESE_FONT, fontSize=18, leading=24, alignment=TA_CENTER, textColor=SECONDARY_COLOR, spaceAfter=40 )) # 章节标题 styles.add(ParagraphStyle( name='ChapterTitle', fontName=CHINESE_FONT, fontSize=22, leading=30, alignment=TA_LEFT, textColor=PRIMARY_COLOR, spaceBefore=20, spaceAfter=15 )) # 节标题 styles.add(ParagraphStyle( name='SectionTitle', fontName=CHINESE_FONT, fontSize=14, leading=20, alignment=TA_LEFT, textColor=ACCENT_COLOR, spaceBefore=15, spaceAfter=10 )) # 正文 styles.add(ParagraphStyle( name='ChineseBody', fontName=CHINESE_FONT, fontSize=11, leading=18, alignment=TA_JUSTIFY, textColor=black, spaceBefore=6, spaceAfter=6 )) # 图片说明 styles.add(ParagraphStyle( name='ImageCaption', fontName=CHINESE_FONT, fontSize=10, leading=14, alignment=TA_CENTER, textColor=HexColor('#666666'), spaceBefore=5, spaceAfter=15 )) # 列表项 styles.add(ParagraphStyle( name='ListItem', fontName=CHINESE_FONT, fontSize=11, leading=16, alignment=TA_LEFT, textColor=black, leftIndent=20, spaceBefore=3, spaceAfter=3 )) return styles def create_table(headers, rows, col_widths=None): """创建美观的表格""" data = [headers] + rows if col_widths is None: col_widths = [PAGE_WIDTH - 2*MARGIN] / len(headers) * len(headers) table = Table(data, colWidths=col_widths) style = TableStyle([ # 表头样式 ('BACKGROUND', (0, 0), (-1, 0), TABLE_HEADER_BG), ('TEXTCOLOR', (0, 0), (-1, 0), white), ('FONTNAME', (0, 0), (-1, 0), CHINESE_FONT), ('FONTSIZE', (0, 0), (-1, 0), 11), ('ALIGN', (0, 0), (-1, 0), 'CENTER'), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), ('BOTTOMPADDING', (0, 0), (-1, 0), 10), ('TOPPADDING', (0, 0), (-1, 0), 10), # 数据行样式 ('FONTNAME', (0, 1), (-1, -1), CHINESE_FONT), ('FONTSIZE', (0, 1), (-1, -1), 10), ('ALIGN', (0, 1), (-1, -1), 'LEFT'), ('BOTTOMPADDING', (0, 1), (-1, -1), 8), ('TOPPADDING', (0, 1), (-1, -1), 8), # 边框 ('GRID', (0, 0), (-1, -1), 0.5, HexColor('#CCCCCC')), ('BOX', (0, 0), (-1, -1), 1, TABLE_HEADER_BG), # 交替行背景 ('ROWBACKGROUNDS', (0, 1), (-1, -1), [white, LIGHT_BG]), ]) table.setStyle(style) return table def add_image_with_caption(story, image_path, caption, styles, max_width=15*cm, max_height=10*cm): """添加图片和说明""" if os.path.exists(image_path): try: img = Image(image_path) # 计算缩放比例 w, h = img.drawWidth, img.drawHeight scale = min(max_width/w, max_height/h, 1) img._restrictSize(max_width, max_height) img.hAlign = 'CENTER' story.append(img) story.append(Paragraph(caption, styles['ImageCaption'])) except Exception as e: story.append(Paragraph(f"[图片加载失败: {image_path}]", styles['ChineseBody'])) else: story.append(Paragraph(f"[图片不存在: {image_path}]", styles['ChineseBody'])) def build_document(): """构建PDF文档""" output_path = '/Users/retirado/ccProgram/docs/少儿智慧阅读-产品介绍与功能说明.pdf' screenshots_dir = '/Users/retirado/ccProgram/docs/screenshots' doc = SimpleDocTemplate( output_path, pagesize=A4, leftMargin=MARGIN, rightMargin=MARGIN, topMargin=MARGIN, bottomMargin=MARGIN ) styles = create_styles() story = [] # ===== 封面 ===== story.append(Spacer(1, 4*cm)) story.append(Paragraph('少儿智慧阅读', styles['CoverTitle'])) story.append(Paragraph('产品介绍与功能说明', styles['CoverSubtitle'])) story.append(Spacer(1, 3*cm)) story.append(Paragraph('版本:v1.0', styles['CoverSubtitle'])) story.append(Paragraph('日期:2026年2月24日', styles['CoverSubtitle'])) story.append(PageBreak()) # ===== 第一章:产品概述 ===== story.append(Paragraph('第一章 产品概述', styles['ChapterTitle'])) story.append(Paragraph('1.1 产品定位', styles['SectionTitle'])) story.append(Paragraph( '少儿智慧阅读是一款面向幼儿园的B2B2C综合性阅读教学管理系统,致力于为幼儿园、教师和家长提供全方位的绘本阅读教学服务。平台采用多端协同架构,打通超管、学校、教师、家长四方角色,实现从课程创作、教学管理到家校互动的完整闭环。', styles['ChineseBody'] )) story.append(Paragraph('1.2 目标用户', styles['SectionTitle'])) story.append(create_table( ['用户角色', '使用场景', '核心需求'], [ ['平台超管', '运营管理', '课程内容管理、租户服务、平台运营'], ['学校管理员', '园所管理', '教师学生管理、课程授权、数据统计'], ['教师', '教学实施', '课程备课、课堂教学、任务布置、成长记录'], ['家长', '家校共育', '查看任务、提交反馈、了解孩子成长'] ], [4*cm, 3*cm, 8*cm] )) story.append(Spacer(1, 10)) story.append(Paragraph('1.3 核心价值', styles['SectionTitle'])) values = [ '• 标准化教学:专业绘本课程包,标准化教学流程', '• 效率提升:备课上课一体化,教学管理智能化', '• 家校联动:任务布置与反馈,家校共育更紧密', '• 成长可视:多维度数据记录,成长轨迹清晰可见', '• 灵活管理:多租户架构,满足不同规模园所需求' ] for v in values: story.append(Paragraph(v, styles['ListItem'])) story.append(PageBreak()) # ===== 第二章:技术架构 ===== story.append(Paragraph('第二章 技术架构', styles['ChapterTitle'])) story.append(Paragraph('2.1 系统架构', styles['SectionTitle'])) story.append(Paragraph( '系统采用前后端分离架构,前端使用Vue 3框架,后端使用NestJS框架,通过JWT进行身份认证。数据层使用Prisma ORM,支持SQLite(开发)和PostgreSQL(生产)数据库。文件存储支持本地存储和云存储两种方案。', styles['ChineseBody'] )) story.append(Paragraph('2.2 技术选型', styles['SectionTitle'])) story.append(create_table( ['层级', '技术栈', '说明'], [ ['前端框架', 'Vue 3 + Vite', '现代化前端开发框架'], ['UI组件', 'Ant Design Vue 4.x', '企业级UI组件库'], ['状态管理', 'Pinia', 'Vue官方状态管理'], ['后端框架', 'NestJS', '企业级Node.js框架'], ['ORM', 'Prisma', '现代化数据库工具'], ['数据库', 'SQLite / PostgreSQL', '开发/生产数据库'], ['认证', 'JWT + Passport', '安全认证方案'] ], [3*cm, 5*cm, 7*cm] )) story.append(PageBreak()) # ===== 第三章:超管端功能 ===== story.append(Paragraph('第三章 超管端功能', styles['ChapterTitle'])) story.append(Paragraph( '超管端是平台的运营管理中心,负责课程内容生产、租户服务和平台运营。', styles['ChineseBody'] )) story.append(Paragraph('3.1 数据看板', styles['SectionTitle'])) story.append(Paragraph( '提供平台整体运营数据的可视化展示,包括核心指标(租户数量、课程数量、授课次数、学生总数)、趋势图表和快捷入口。', styles['ChineseBody'] )) add_image_with_caption(story, f'{screenshots_dir}/02-admin-dashboard.png', '图3-1 超管端数据看板 - 平台运营数据一目了然', styles) story.append(Paragraph('3.2 课程包管理', styles['SectionTitle'])) story.append(Paragraph( '完整的课程内容生产与发布流程,支持基础信息、资源上传、教学环节、逐页脚本、延伸活动等完整内容。课程状态包括:草稿、待审核、已发布、已下架。', styles['ChineseBody'] )) add_image_with_caption(story, f'{screenshots_dir}/03-admin-courses.png', '图3-2 课程包列表 - 管理所有课程内容', styles) add_image_with_caption(story, f'{screenshots_dir}/04-course-detail.png', '图3-3 课程详情 - 完整的教学设计展示', styles, max_height=12*cm) story.append(Paragraph('3.3 租户管理', styles['SectionTitle'])) story.append(Paragraph( '多租户服务的核心管理功能,包括租户列表、套餐配置、课程授权、服务管理(暂停/恢复)、密码重置等。', styles['ChineseBody'] )) add_image_with_caption(story, f'{screenshots_dir}/05-admin-tenants.png', '图3-4 租户管理 - 管理所有签约园所', styles) story.append(PageBreak()) # ===== 第四章:学校端功能 ===== story.append(Paragraph('第四章 学校端功能', styles['ChapterTitle'])) story.append(Paragraph( '学校端是园所管理员的管理后台,负责本园的教师、学生、班级和教学管理。', styles['ChineseBody'] )) story.append(Paragraph('4.1 数据概览', styles['SectionTitle'])) story.append(Paragraph( '园所运营数据一目了然,包括人员统计(教师数、学生数、班级数)、教学统计(授课次数、任务完成率)和图表展示。', styles['ChineseBody'] )) add_image_with_caption(story, f'{screenshots_dir}/06-school-dashboard.png', '图4-1 学校端数据概览 - 园所运营数据统计', styles) story.append(Paragraph('4.2 人员管理', styles['SectionTitle'])) story.append(Paragraph( '支持教师和学生的增删改查、批量导入、调班等功能。教师管理包括添加、编辑、删除、重置密码;学生管理包括添加、编辑、删除、批量导入、调班。', styles['ChineseBody'] )) add_image_with_caption(story, f'{screenshots_dir}/07-school-teachers.png', '图4-2 教师管理 - 管理本园教师信息', styles) story.append(Paragraph('4.3 课程排期', styles['SectionTitle'])) story.append(Paragraph( '日历视图展示排课情况,支持创建、编辑、删除排课,以及批量排课功能。', styles['ChineseBody'] )) add_image_with_caption(story, f'{screenshots_dir}/08-school-schedule.png', '图4-3 课程排期 - 日历视图管理排课', styles) story.append(PageBreak()) # ===== 第五章:教师端功能 ===== story.append(Paragraph('第五章 教师端功能', styles['ChapterTitle'])) story.append(Paragraph( '教师端是教师日常教学的核心工具,覆盖备课、上课、课后全流程。', styles['ChineseBody'] )) story.append(Paragraph('5.1 课程中心', styles['SectionTitle'])) story.append(Paragraph( '浏览和使用已授权的课程,支持年级筛选(小班/中班/大班)、领域筛选(语言、社会、科学等)、关键词搜索。', styles['ChineseBody'] )) add_image_with_caption(story, f'{screenshots_dir}/09-teacher-courses.png', '图5-1 课程中心 - 浏览可用课程', styles) story.append(Paragraph('5.2 备课模式', styles['SectionTitle'])) story.append(Paragraph( '课前准备工作,包括教学流程、教师讲稿、逐页脚本、备课笔记、教学材料等。支持选择授课班级。', styles['ChineseBody'] )) add_image_with_caption(story, f'{screenshots_dir}/10-teacher-prepare.png', '图5-2 备课模式 - 完整的备课支持', styles, max_height=12*cm) story.append(Paragraph('5.3 上课记录', styles['SectionTitle'])) story.append(Paragraph( '历史授课记录管理,支持状态筛选(已计划/进行中/已完成)、日期筛选、查看详情、课后记录补充。', styles['ChineseBody'] )) add_image_with_caption(story, f'{screenshots_dir}/11-teacher-lessons.png', '图5-3 上课记录 - 授课历史管理', styles) story.append(PageBreak()) # ===== 第六章:家长端功能 ===== story.append(Paragraph('第六章 家长端功能', styles['ChapterTitle'])) story.append(Paragraph( '家长端是家校互动的桥梁,让家长了解并参与孩子的阅读成长。', styles['ChineseBody'] )) story.append(Paragraph('6.1 首页', styles['SectionTitle'])) story.append(Paragraph( '孩子信息概览,包括孩子卡片(姓名、班级、阅读次数)、最近任务、成长档案等。', styles['ChineseBody'] )) add_image_with_caption(story, f'{screenshots_dir}/12-parent-dashboard.png', '图6-1 家长端首页 - 孩子信息概览', styles) story.append(Paragraph('6.2 阅读任务', styles['SectionTitle'])) story.append(Paragraph( '完成教师布置的任务,支持查看任务详情、截止日期、提交反馈、查看反馈记录。', styles['ChineseBody'] )) add_image_with_caption(story, f'{screenshots_dir}/13-parent-tasks.png', '图6-2 阅读任务 - 查看并完成阅读任务', styles) story.append(PageBreak()) # ===== 第七章:核心业务流程 ===== story.append(Paragraph('第七章 核心业务流程', styles['ChapterTitle'])) story.append(Paragraph('7.1 课程生产流程', styles['SectionTitle'])) story.append(Paragraph( '超管创建课程 → 填写基础信息 → 上传资源 → 设计教学环节 → 添加延伸活动 → 提交审核 → 审核通过 → 发布课程 → 授权给租户 → 学校/教师使用', styles['ChineseBody'] )) story.append(Paragraph('7.2 教学实施流程', styles['SectionTitle'])) story.append(Paragraph( '教师浏览课程 → 进入备课模式 → 记录备课笔记 → 选择班级 → 开始上课 → 按流程教学 → 课堂评价 → 结束课程 → 填写课堂记录 → 查看上课记录', styles['ChineseBody'] )) story.append(Paragraph('7.3 家校互动流程', styles['SectionTitle'])) story.append(Paragraph( '教师布置任务 → 分配给学生/班级 → 家长查看任务 → 亲子完成阅读 → 家长提交反馈 → 教师查看反馈', styles['ChineseBody'] )) story.append(Paragraph('7.4 成长记录流程', styles['SectionTitle'])) story.append(Paragraph( '教师观察学生 → 创建成长档案 → 选择学生 → 填写内容 → 上传图片 → 保存档案 → 家长查看', styles['ChineseBody'] )) story.append(PageBreak()) # ===== 第八章:部署与运维 ===== story.append(Paragraph('第八章 部署与运维', styles['ChapterTitle'])) story.append(Paragraph('8.1 环境配置', styles['SectionTitle'])) story.append(create_table( ['环境', '前端地址', '后端地址', '数据库'], [ ['开发', 'localhost:5173', 'localhost:3000', 'SQLite'], ['生产', '域名/CDN', '域名/API', 'PostgreSQL'] ], [2.5*cm, 4*cm, 4*cm, 4.5*cm] )) story.append(Spacer(1, 15)) story.append(Paragraph('8.2 启动命令', styles['SectionTitle'])) story.append(Paragraph('# 开发环境启动', styles['ChineseBody'])) story.append(Paragraph('./start-all.sh', styles['ListItem'])) story.append(Paragraph('# 停止服务', styles['ChineseBody'])) story.append(Paragraph('./stop-all.sh', styles['ListItem'])) story.append(Spacer(1, 15)) story.append(Paragraph('8.3 测试账号', styles['SectionTitle'])) story.append(create_table( ['角色', '账号', '密码'], [ ['超管', 'admin', '123456'], ['学校', 'school', '123456'], ['教师', 'teacher1', '123456'], ['家长', 'parent1', '123456'] ], [4*cm, 5.5*cm, 5.5*cm] )) # 结束 story.append(Spacer(1, 2*cm)) story.append(Paragraph('— 文档结束 —', styles['ImageCaption'])) # 生成PDF doc.build(story) print(f'PDF文档已生成: {output_path}') return output_path if __name__ == '__main__': build_document()