| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- import { nanoid } from "nanoid"
- import type { Slide, PPTElement, SlideTheme, PPTTextElement, PPTImageElement } from "@/types/slides"
- import type { AiCoursewareOutline, AiCoursewarePage } from "@/api/aiCourseware"
- export function aiCoursewareToSlides(outline: AiCoursewareOutline): { title: string; theme: Partial<SlideTheme>; slides: Slide[] } {
- const primary = outline.style === "clean" ? "#2D6CDF" : "#FF8A3D"
- const background = outline.style === "clean" ? "#F7F9FC" : "#FFF8EF"
- const fontColor = "#263238"
- const theme: Partial<SlideTheme> = {
- themeColor: primary,
- backgroundColor: background,
- fontColor,
- fontName: "Microsoft Yahei",
- fontSize: "36px"
- }
- return {
- title: outline.title || outline.chapterName || "AI音乐课件",
- theme,
- slides: outline.pages.map(page => buildSlide(page, outline, primary, background, fontColor))
- }
- }
- function buildSlide(page: AiCoursewarePage, outline: AiCoursewareOutline, primary: string, background: string, fontColor: string): Slide {
- const isCover = page.type === "cover" || page.pageNo === 1
- const elements: PPTElement[] = []
- elements.push(
- textElement({
- left: 110,
- top: isCover ? 130 : 70,
- width: isCover ? 980 : 1040,
- height: isCover ? 160 : 82,
- content: page.title || outline.title,
- fontSize: isCover ? 58 : 38,
- color: primary,
- bold: true
- })
- )
- const subtitle = isCover ? [outline.gradeName, outline.textbookName, outline.unitName].filter(Boolean).join(" · ") : page.subtitle
- if (subtitle) {
- elements.push(
- textElement({
- left: 116,
- top: isCover ? 312 : 150,
- width: 920,
- height: 48,
- content: subtitle,
- fontSize: isCover ? 28 : 20,
- color: fontColor
- })
- )
- }
- if (page.image?.url) {
- elements.push(
- imageElement({
- left: isCover ? 1080 : 1180,
- top: isCover ? 150 : 150,
- width: isCover ? 650 : 590,
- height: isCover ? 620 : 480,
- src: page.image.url
- })
- )
- }
- const bulletTop = isCover ? 430 : 250
- const bulletWidth = page.image?.url ? 900 : 1320
- const bullets = normalizeBullets(page)
- elements.push(
- textElement({
- left: 120,
- top: bulletTop,
- width: bulletWidth,
- height: Math.min(430, 72 + bullets.length * 58),
- content: bullets.map(item => `• ${escapeHtml(item)}`).join("<br>"),
- fontSize: isCover ? 30 : 28,
- color: fontColor,
- lineHeight: 1.45
- })
- )
- if (page.interaction?.question) {
- elements.push(
- textElement({
- left: 120,
- top: 760,
- width: 1200,
- height: 105,
- content: `互动:${escapeHtml(page.interaction.question)}`,
- fontSize: 28,
- color: "#FFFFFF",
- fill: primary,
- lineHeight: 1.35
- })
- )
- } else if (page.teacherTips) {
- elements.push(
- textElement({
- left: 120,
- top: 780,
- width: 1200,
- height: 86,
- content: `提示:${escapeHtml(page.teacherTips)}`,
- fontSize: 22,
- color: "#52616B",
- fill: "#FFFFFF",
- lineHeight: 1.35
- })
- )
- }
- elements.push(
- textElement({
- left: 1700,
- top: 995,
- width: 120,
- height: 36,
- content: `${page.pageNo || ""}/15`,
- fontSize: 18,
- color: "#90A4AE"
- })
- )
- return {
- id: nanoid(10),
- elements,
- background: {
- type: "solid",
- color: background
- },
- remark: page.teacherTips || ""
- }
- }
- function normalizeBullets(page: AiCoursewarePage): string[] {
- const bullets = (page.bullets || []).filter(Boolean)
- if (bullets.length) return bullets.slice(0, 5)
- return [page.subtitle || page.title || "课堂内容"]
- }
- function textElement(params: {
- left: number
- top: number
- width: number
- height: number
- content: string
- fontSize: number
- color: string
- bold?: boolean
- fill?: string
- lineHeight?: number
- }): PPTTextElement {
- return {
- id: nanoid(10),
- type: "text",
- left: params.left,
- top: params.top,
- width: params.width,
- height: params.height,
- rotate: 0,
- content: `<div style="font-size:${params.fontSize}px;${params.bold ? "font-weight:700;" : ""}">${params.content}</div>`,
- defaultFontName: "Microsoft Yahei",
- defaultColor: params.color,
- fill: params.fill,
- lineHeight: params.lineHeight || 1.25,
- wordSpace: 0,
- opacity: 1
- }
- }
- function imageElement(params: { left: number; top: number; width: number; height: number; src: string }): PPTImageElement {
- return {
- id: nanoid(10),
- type: "image",
- left: params.left,
- top: params.top,
- width: params.width,
- height: params.height,
- rotate: 0,
- src: params.src,
- fixedRatio: false,
- radius: 12,
- shadow: {
- h: 0,
- v: 8,
- blur: 18,
- color: "rgba(38, 50, 56, 0.18)"
- }
- }
- }
- function escapeHtml(value: string) {
- return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")
- }
|