kindergarten_java/docs/generate-pdf.py
2026-02-28 16:41:39 +08:00

477 lines
19 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.

#!/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', 'admin123'],
['学校', '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()