yonge 5 часов назад
Родитель
Сommit
ea5ad65af1

+ 1 - 1
dist/assets/index-B3TQB3wV.js → dist/assets/index-BAPd0_0T.js

@@ -1,4 +1,4 @@
-import{cH as dw,cI as hw,aG as vw,cJ as pw,cK as gw,cL as mw,cM as yw,ah as Yt,cN as Sw,cO as bw,cP as xw,cQ as Cw,aM as ww,aF as Tw,cR as Iw,aB as c1,bg as _w,cS as Aw,cT as Ew,cU as Pw,cV as Mw,cW as Rw,cX as Dw,cY as Lw,aE as kw,cZ as Ow,s as Ae,bl as Bw,al as je,ag as ze,af as ve,ak as Z,c_ as Nw,c$ as Fw,d0 as $w,d1 as zw,bk as Uw,cy as ul,aS as Cv,aq as Ye,d2 as Hw,d3 as Vw,a5 as $e,d4 as Gw,d5 as Ww,d6 as jw,d7 as Kw,d8 as Xw,d9 as Yw,da as Zw,db as Qw,dc as Jw,dd as qw,de as eT,v as tT,df as rT,dg as nT,dh as iT,aW as aT,di as oT,dj as sT,dk as lT,dl as uT,dm as cT,ay as sr,dn as fT,dp as dT,dq as hT,dr as vT,A as pT,ds as gT,dt as mT,bq as yT,du as ST,dv as bT,dw as xT,ap as CT,ac as f1,ai as dt,dx as wT,au as Ce,dy as TT,X as d1,y as IT,dz as _T,aP as AT,dA as ET,w as _r,dB as PT,dC as MT,H as RT,dD as DT,bA as hn,aD as LT,ae as ie,bP as ea,$ as wc,dE as kT,bO as ta,dF as OT,aC as BT,aO as NT,R as fe,dG as FT,br as rh,b3 as Lr,aj as Hs,b1 as lt,b2 as Ua,an as wv,dH as $T,dI as zT,dJ as UT,dK as HT,dL as VT,dM as GT,dN as WT,Q as jT,dO as KT,dP as XT,dQ as YT,at as Mt,dR as ZT,aR as QT,aY as JT,ad as cl,aZ as qT,dS as eI,dT as tI,dU as rI,P as le,a6 as nI,dV as iI,dW as aI,dX as oI,dY as sI,a7 as lI,dZ as uI,bL as cI,d_ as fI,d$ as dI,bK as hI,b4 as vI,aA as Tv,e0 as pI,e1 as gI,E as gr,x as mI,e2 as yI,e3 as SI,e4 as bI,am as _n,e5 as xI,az as hr,aU as nh,e6 as CI,as as jr,e7 as wI,e8 as Iv,cG as mr,by as ih,cv as ah,bI as TI,bH as Ve,bu as oh,bv as _v,bx as Wt,bB as Tc,bN as di,e9 as wu,bE as hi,ea as II,bJ as ra,bC as _I,bw as Bt,eb as AI,ec as ui,cw as EI,ed as PI,ee as MI,ef as RI,eg as DI,bM as Zp,eh as LI,bF as Qp,cD as Ic,cC as kI,ei as Hu,ej as Il}from"./index-BTrbYkzU.js";/**
+import{cH as dw,cI as hw,aG as vw,cJ as pw,cK as gw,cL as mw,cM as yw,ah as Yt,cN as Sw,cO as bw,cP as xw,cQ as Cw,aM as ww,aF as Tw,cR as Iw,aB as c1,bg as _w,cS as Aw,cT as Ew,cU as Pw,cV as Mw,cW as Rw,cX as Dw,cY as Lw,aE as kw,cZ as Ow,s as Ae,bl as Bw,al as je,ag as ze,af as ve,ak as Z,c_ as Nw,c$ as Fw,d0 as $w,d1 as zw,bk as Uw,cy as ul,aS as Cv,aq as Ye,d2 as Hw,d3 as Vw,a5 as $e,d4 as Gw,d5 as Ww,d6 as jw,d7 as Kw,d8 as Xw,d9 as Yw,da as Zw,db as Qw,dc as Jw,dd as qw,de as eT,v as tT,df as rT,dg as nT,dh as iT,aW as aT,di as oT,dj as sT,dk as lT,dl as uT,dm as cT,ay as sr,dn as fT,dp as dT,dq as hT,dr as vT,A as pT,ds as gT,dt as mT,bq as yT,du as ST,dv as bT,dw as xT,ap as CT,ac as f1,ai as dt,dx as wT,au as Ce,dy as TT,X as d1,y as IT,dz as _T,aP as AT,dA as ET,w as _r,dB as PT,dC as MT,H as RT,dD as DT,bA as hn,aD as LT,ae as ie,bP as ea,$ as wc,dE as kT,bO as ta,dF as OT,aC as BT,aO as NT,R as fe,dG as FT,br as rh,b3 as Lr,aj as Hs,b1 as lt,b2 as Ua,an as wv,dH as $T,dI as zT,dJ as UT,dK as HT,dL as VT,dM as GT,dN as WT,Q as jT,dO as KT,dP as XT,dQ as YT,at as Mt,dR as ZT,aR as QT,aY as JT,ad as cl,aZ as qT,dS as eI,dT as tI,dU as rI,P as le,a6 as nI,dV as iI,dW as aI,dX as oI,dY as sI,a7 as lI,dZ as uI,bL as cI,d_ as fI,d$ as dI,bK as hI,b4 as vI,aA as Tv,e0 as pI,e1 as gI,E as gr,x as mI,e2 as yI,e3 as SI,e4 as bI,am as _n,e5 as xI,az as hr,aU as nh,e6 as CI,as as jr,e7 as wI,e8 as Iv,cG as mr,by as ih,cv as ah,bI as TI,bH as Ve,bu as oh,bv as _v,bx as Wt,bB as Tc,bN as di,e9 as wu,bE as hi,ea as II,bJ as ra,bC as _I,bw as Bt,eb as AI,ec as ui,cw as EI,ed as PI,ee as MI,ef as RI,eg as DI,bM as Zp,eh as LI,bF as Qp,cD as Ic,cC as kI,ei as Hu,ej as Il}from"./index-CS7FTuzz.js";/**
 * vue v3.4.34
 * (c) 2018-present Yuxi (Evan) You and Vue contributors
 * @license MIT

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/assets/index-BDoNJwJO.css


+ 1 - 1
dist/assets/index-A-ckG7Yu.js → dist/assets/index-BEuUI6a5.js

@@ -1 +1 @@
-import{_ as a}from"./404-R0y5dkvZ.js";import{a5 as o,en as t,ae as r,af as c,bO as _,bP as d,ak as s,bH as p}from"./index-BTrbYkzU.js";const i=e=>(_("data-v-dda25179"),e=e(),d(),e),n={class:"errorPage"},m=i(()=>s("div",{class:"error"},[s("img",{src:a,class:"img",alt:""}),s("div",{class:"tit"},"页面找不到了~")],-1)),l=[m],f=o({__name:"errorPage",setup(e){return t(),(u,h)=>(r(),c("div",n,l))}}),I=p(f,[["__scopeId","data-v-dda25179"]]);export{I as default};
+import{_ as a}from"./404-R0y5dkvZ.js";import{a5 as o,en as t,ae as r,af as c,bO as _,bP as d,ak as s,bH as p}from"./index-CS7FTuzz.js";const i=e=>(_("data-v-dda25179"),e=e(),d(),e),n={class:"errorPage"},m=i(()=>s("div",{class:"error"},[s("img",{src:a,class:"img",alt:""}),s("div",{class:"tit"},"页面找不到了~")],-1)),l=[m],f=o({__name:"errorPage",setup(e){return t(),(u,h)=>(r(),c("div",n,l))}}),I=p(f,[["__scopeId","data-v-dda25179"]]);export{I as default};

+ 1 - 1
dist/assets/index-CIUTvazc.js → dist/assets/index-ByxzWVNc.js

@@ -1 +1 @@
-import{u as c,af as l}from"./index-B3TQB3wV.js";import{w as p,bA as d,a5 as g,cD as m,R as t,ae as r,af as u,al as f,ag as w,bH as k}from"./index-BTrbYkzU.js";import"./shapes-BTNKLC5F.js";const S=()=>{function o(a){if(window.parent===window)return;const{api:e,playState:n}=a.data||{};e==="headerTogge"?window.parent.postMessage({api:"headerTogge",playState:n},"*"):e==="changeTogge"?window.parent.postMessage({api:"changeTogge"},"*"):["clickTempo","clickViewFigner"].includes(e)&&window.parent.postMessage({api:e},"*")}p(()=>{window.addEventListener("message",o)}),d(()=>{window.removeEventListener("message",o)})},_=g({__name:"mobileScreen",setup(o){S();const a=m(),e=t(!0),n=t(!1),i=c();return a.setScreenMode("mobileScreen"),i.initPPTData().then(()=>{e.value=!1}),(v,s)=>(r(),u("div",{class:"mobileScreen",onClick:s[0]||(s[0]=b=>n.value=!1)},[e.value?w("",!0):(r(),f(l,{key:0}))]))}}),P=k(_,[["__scopeId","data-v-90b2692a"]]);export{P as default};
+import{u as c,af as l}from"./index-BAPd0_0T.js";import{w as p,bA as d,a5 as g,cD as m,R as t,ae as r,af as u,al as f,ag as w,bH as k}from"./index-CS7FTuzz.js";import"./shapes-C3yc_Yz1.js";const S=()=>{function o(a){if(window.parent===window)return;const{api:e,playState:n}=a.data||{};e==="headerTogge"?window.parent.postMessage({api:"headerTogge",playState:n},"*"):e==="changeTogge"?window.parent.postMessage({api:"changeTogge"},"*"):["clickTempo","clickViewFigner"].includes(e)&&window.parent.postMessage({api:e},"*")}p(()=>{window.addEventListener("message",o)}),d(()=>{window.removeEventListener("message",o)})},_=g({__name:"mobileScreen",setup(o){S();const a=m(),e=t(!0),n=t(!1),i=c();return a.setScreenMode("mobileScreen"),i.initPPTData().then(()=>{e.value=!1}),(v,s)=>(r(),u("div",{class:"mobileScreen",onClick:s[0]||(s[0]=b=>n.value=!1)},[e.value?w("",!0):(r(),f(l,{key:0}))]))}}),P=k(_,[["__scopeId","data-v-90b2692a"]]);export{P as default};

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/assets/index-CJfmVt57.css


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/assets/index-CS7FTuzz.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/assets/index-CWXaVt4S.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/assets/index-CjpSQ8o9.js


+ 1 - 1
dist/assets/index-uv4qHVjm.js → dist/assets/index-CmuGMYTG.js

@@ -1 +1 @@
-import{_ as t}from"./404-R0y5dkvZ.js";import{E as c}from"./index-CPYJdTE3.js";import{a5 as n,ae as i,af as r,ak as o,aq as _,am as d,P as l,aS as p,bO as m,bP as f,bH as u}from"./index-BTrbYkzU.js";const s=a=>(m("data-v-340bbdaa"),a=a(),f(),a),h={class:"login"},b={class:"error"},g=s(()=>o("img",{src:t,class:"img",alt:""},null,-1)),k=s(()=>o("div",{class:"tit"},"登录已过期或服务器错误!",-1)),v=n({__name:"login",setup(a){function e(){window.close()}return(x,B)=>(i(),r("div",h,[o("div",b,[g,k,_(l(c),{class:"backBtn",type:"primary",plain:"",onClick:e},{default:d(()=>[p("关闭页面")]),_:1})])]))}}),C=u(v,[["__scopeId","data-v-340bbdaa"]]);export{C as default};
+import{_ as t}from"./404-R0y5dkvZ.js";import{E as c}from"./index-CjpSQ8o9.js";import{a5 as n,ae as i,af as r,ak as o,aq as _,am as d,P as l,aS as p,bO as m,bP as f,bH as u}from"./index-CS7FTuzz.js";const s=a=>(m("data-v-340bbdaa"),a=a(),f(),a),h={class:"login"},b={class:"error"},g=s(()=>o("img",{src:t,class:"img",alt:""},null,-1)),k=s(()=>o("div",{class:"tit"},"登录已过期或服务器错误!",-1)),v=n({__name:"login",setup(a){function e(){window.close()}return(x,B)=>(i(),r("div",h,[o("div",b,[g,k,_(l(c),{class:"backBtn",type:"primary",plain:"",onClick:e},{default:d(()=>[p("关闭页面")]),_:1})])]))}}),C=u(v,[["__scopeId","data-v-340bbdaa"]]);export{C as default};

+ 1 - 1
dist/assets/index-Ymwfxhai.js → dist/assets/index-DX0i0z2z.js

@@ -1 +1 @@
-import{u as s,af as r}from"./index-B3TQB3wV.js";import{a5 as c,cD as n,R as p,ae as t,af as _,al as u,ag as i,bH as f}from"./index-BTrbYkzU.js";const l={class:"pptScreen"},m=c({__name:"pptScreen",setup(d){const a=n(),e=p(!0),o=s();return a.setScreenMode("pptScreen"),o.initPPTData().then(()=>{e.value=!1}),(S,k)=>(t(),_("div",l,[e.value?i("",!0):(t(),u(r,{key:0}))]))}}),h=f(m,[["__scopeId","data-v-678905e9"]]);export{h as default};
+import{u as s,af as r}from"./index-BAPd0_0T.js";import{a5 as c,cD as n,R as p,ae as t,af as _,al as u,ag as i,bH as f}from"./index-CS7FTuzz.js";const l={class:"pptScreen"},m=c({__name:"pptScreen",setup(d){const a=n(),e=p(!0),o=s();return a.setScreenMode("pptScreen"),o.initPPTData().then(()=>{e.value=!1}),(S,k)=>(t(),_("div",l,[e.value?i("",!0):(t(),u(r,{key:0}))]))}}),h=f(m,[["__scopeId","data-v-678905e9"]]);export{h as default};

+ 1 - 1
dist/assets/shapes-BTNKLC5F.js → dist/assets/shapes-C3yc_Yz1.js

@@ -1,4 +1,4 @@
-import{cG as k,bB as $1}from"./index-BTrbYkzU.js";import{ag as Z,ah as H1,ai as U,aj as n0,ak as c0,al as i0,am as j1,an as K1,ao as Y1,ap as R}from"./index-B3TQB3wV.js";var z1={exports:{}};/*!
+import{cG as k,bB as $1}from"./index-CS7FTuzz.js";import{ag as Z,ah as H1,ai as U,aj as n0,ak as c0,al as i0,am as j1,an as K1,ao as Y1,ap as R}from"./index-BAPd0_0T.js";var z1={exports:{}};/*!
  * clipboard.js v2.0.11
  * https://clipboardjs.com/
  *

+ 1 - 1
dist/index.html

@@ -10,7 +10,7 @@
     <meta name="description" content="ppt编辑器" />
     <meta name="keywords" content="ppt,powerpoint,office powerpoint,在线ppt,幻灯片,演示文稿,ppt在线制作,Vue3,TypeScript" />
     <title>PPT</title>
-    <script type="module" crossorigin src="./assets/index-BTrbYkzU.js"></script>
+    <script type="module" crossorigin src="./assets/index-CS7FTuzz.js"></script>
     <link rel="stylesheet" crossorigin href="./assets/index-AavUEaYy.css">
   </head>
   <body>

+ 90 - 6
src/config/aiCoursewareTemplates.ts

@@ -8,6 +8,20 @@ export type AiCoursewareTemplateId =
   | "digital-tech"
   | "active-classroom"
 
+export type AiCoursewareTemplatePageRole =
+  | "cover"
+  | "intro"
+  | "goal"
+  | "story"
+  | "listen"
+  | "knowledge"
+  | "interaction"
+  | "performance"
+  | "practice"
+  | "activity"
+  | "summary"
+  | "content"
+
 export interface AiCoursewareThemePalette {
   primary: string
   secondary: string
@@ -32,6 +46,7 @@ export interface AiCoursewareTemplate {
   typography: string
   backgroundStyle: string
   illustrationStyle: string
+  pageLayouts: Partial<Record<AiCoursewareTemplatePageRole, string>>
   visualPrompt: string
   palettes: [AiCoursewareThemePalette, AiCoursewareThemePalette, AiCoursewareThemePalette]
 }
@@ -76,7 +91,7 @@ export const AI_COURSEWARE_TEMPLATES: AiCoursewareTemplate[] = [
   {
     id: "playful-handdrawn",
     name: "趣味手绘课堂",
-    sourceName: "手绘风通用模板",
+    sourceName: "原创故事课堂布局包",
     scene: "小学低年级",
     description: "故事导入、情境欣赏和童趣互动",
     baseStyle: "cartoon",
@@ -84,13 +99,25 @@ export const AI_COURSEWARE_TEMPLATES: AiCoursewareTemplate[] = [
     typography: "圆润醒目的低龄课堂字体层级,标题短而大,正文不低于30px",
     backgroundStyle: "绘本式连续场景背景,保留大面积干净文字区",
     illustrationStyle: "统一儿童绘本插画,角色造型、服装和标志色跨页一致",
+    pageLayouts: {
+      cover: "cover_scene",
+      intro: "story_stage",
+      story: "story_stage",
+      listen: "full_image_question",
+      goal: "observe_question",
+      interaction: "action_demo",
+      activity: "action_demo",
+      performance: "performance_stage",
+      practice: "practice_check",
+      summary: "visual_summary"
+    },
     visualPrompt: "趣味手绘课堂风格,画面亲切活泼,使用统一插画语言和明确场景,避免商务卡片堆叠。",
     palettes: playfulPalettes
   },
   {
     id: "fresh-education",
     name: "清新教育课件",
-    sourceName: "教育课件PPT模板",
+    sourceName: "原创清新教育布局包",
     scene: "常规音乐课",
     description: "知识讲解、听赏练习和课堂总结",
     baseStyle: "clean",
@@ -98,13 +125,25 @@ export const AI_COURSEWARE_TEMPLATES: AiCoursewareTemplate[] = [
     typography: "清晰现代的教学字体层级,标题明确,正文适合远距离投屏",
     backgroundStyle: "浅色教育场景与克制几何底纹",
     illustrationStyle: "简洁教育插画或真实课堂素材,主体明确",
+    pageLayouts: {
+      cover: "cover_scene",
+      intro: "split_image",
+      story: "split_image",
+      listen: "listen_wave",
+      goal: "goal_cards",
+      knowledge: "knowledge_cards",
+      interaction: "observe_question",
+      activity: "activity_steps",
+      practice: "practice_check",
+      summary: "summary_checklist"
+    },
     visualPrompt: "清新教育课件风格,留白充足、结构清楚、色彩温和,兼顾教师讲解和学生远距离阅读。",
     palettes: freshPalettes
   },
   {
     id: "academic-blue",
     name: "蓝色学术课堂",
-    sourceName: "严肃蓝色学术模板",
+    sourceName: "原创蓝色学术布局包",
     scene: "初高中课堂",
     description: "乐理、作品结构和知识密集型内容",
     baseStyle: "clean",
@@ -112,13 +151,24 @@ export const AI_COURSEWARE_TEMPLATES: AiCoursewareTemplate[] = [
     typography: "严谨的标题、正文和注释三级层级",
     backgroundStyle: "克制蓝灰背景与清晰内容分区",
     illustrationStyle: "写实乐器、谱例和结构图优先",
+    pageLayouts: {
+      cover: "cover_scene",
+      intro: "full_image_question",
+      story: "split_image",
+      listen: "compare_listen",
+      goal: "goal_cards",
+      knowledge: "knowledge_cards",
+      interaction: "listen_compare_visual",
+      practice: "practice_check",
+      summary: "summary_checklist"
+    },
     visualPrompt: "蓝色学术课堂风格,严谨克制、层级明确,强化标题、数据和谱例焦点,减少无关装饰。",
     palettes: academicPalettes
   },
   {
     id: "oriental-culture",
     name: "东方文化课堂",
-    sourceName: "中国风传统文化通用PPT模板",
+    sourceName: "原创东方文化布局包",
     scene: "传统文化主题",
     description: "民歌、戏曲、民族音乐和传统节日",
     baseStyle: "clean",
@@ -126,13 +176,24 @@ export const AI_COURSEWARE_TEMPLATES: AiCoursewareTemplate[] = [
     typography: "现代中文标题与舒展正文层级",
     backgroundStyle: "米白纸感与克制东方纹样",
     illustrationStyle: "统一现代东方插画,避免素材拼贴",
+    pageLayouts: {
+      cover: "cover_scene",
+      intro: "story_stage",
+      story: "story_stage",
+      listen: "full_image_question",
+      goal: "observe_question",
+      knowledge: "knowledge_cards",
+      interaction: "action_demo",
+      practice: "practice_check",
+      summary: "visual_summary"
+    },
     visualPrompt: "现代东方文化课堂风格,使用米白、黛色和朱砂色,装饰克制,突出传统文化意境与课堂可读性。",
     palettes: orientalPalettes
   },
   {
     id: "digital-tech",
     name: "数字科技课堂",
-    sourceName: "互联网科技风模板",
+    sourceName: "原创数字科技布局包",
     scene: "音乐科技主题",
     description: "电子音乐、数字工具和声音科技",
     baseStyle: "clean",
@@ -140,13 +201,24 @@ export const AI_COURSEWARE_TEMPLATES: AiCoursewareTemplate[] = [
     typography: "高对比科技标题与简洁正文层级",
     backgroundStyle: "深色网格、光点和节点背景",
     illustrationStyle: "统一数字音乐科技视觉,避免无关赛博装饰",
+    pageLayouts: {
+      cover: "cover_scene",
+      intro: "full_image_question",
+      story: "full_image_question",
+      listen: "sound_timeline",
+      goal: "observe_question",
+      knowledge: "knowledge_cards",
+      interaction: "listen_compare_visual",
+      practice: "practice_check",
+      summary: "visual_summary"
+    },
     visualPrompt: "数字科技课堂风格,深色背景配高对比蓝青光感,使用克制的网格和节点元素,保证教学文字清晰。",
     palettes: techPalettes
   },
   {
     id: "active-classroom",
     name: "活力互动课堂",
-    sourceName: "活动型模板",
+    sourceName: "原创互动课堂布局包",
     scene: "课堂活动",
     description: "分组练习、节奏游戏、表演与展示",
     baseStyle: "cartoon",
@@ -154,6 +226,18 @@ export const AI_COURSEWARE_TEMPLATES: AiCoursewareTemplate[] = [
     typography: "大字号动作指令与清晰编号层级",
     backgroundStyle: "明快舞台或课堂活动场景",
     illustrationStyle: "统一动作示范插画,人物姿态清楚",
+    pageLayouts: {
+      cover: "cover_scene",
+      intro: "full_image_question",
+      story: "story_stage",
+      listen: "listen_compare_visual",
+      goal: "action_demo",
+      interaction: "action_demo",
+      activity: "group_mission",
+      performance: "performance_stage",
+      practice: "practice_check",
+      summary: "visual_summary"
+    },
     visualPrompt: "活力互动课堂风格,节奏感强、色彩明快,用清晰编号和动作路径组织任务,避免信息拥挤。",
     palettes: activePalettes
   }

+ 20 - 4
src/utils/aiPptTemplateEngine.ts

@@ -16,7 +16,13 @@ import type {
   SlideTheme
 } from "@/types/slides"
 import type { AiCoursewareOutline, AiCoursewarePage } from "@/api/aiCourseware"
-import { getAiCoursewarePalette, type AiCoursewareTemplateId, type AiCoursewareThemePalette } from "@/config/aiCoursewareTemplates"
+import {
+  getAiCoursewarePalette,
+  getAiCoursewareTemplate,
+  type AiCoursewareTemplateId,
+  type AiCoursewareTemplatePageRole,
+  type AiCoursewareThemePalette
+} from "@/config/aiCoursewareTemplates"
 
 interface AiPptOptions {
   backgroundImage?: string
@@ -91,7 +97,7 @@ function buildSlide(page: AiCoursewarePage, outline: AiCoursewareOutline, palett
     ? [dedicatedResource]
     : videoBinding(page)
       ? videoResourceTemplate(page, outline, palette)
-      : [...templateForPage(page, role)(page, outline, palette, index), ...inlineResourceElements(page)]
+      : [...templateForPage(page, role, options.templateId)(page, outline, palette, index), ...inlineResourceElements(page)]
   const elements = dedicatedResource ? contentElements : [...templateDecorationElements(options.templateId, palette, role), ...contentElements]
   return {
     id: nanoid(10),
@@ -162,8 +168,9 @@ function entranceAnimations(elements: PPTElement[]): PPTAnimation[] {
     }))
 }
 
-function templateForPage(page: AiCoursewarePage, role: PageRole) {
-  const layout = cleanText(page.visualPlan?.layout || page.visual?.layout).toLowerCase()
+function templateForPage(page: AiCoursewarePage, role: PageRole, templateId?: AiCoursewareTemplateId) {
+  const requestedLayout = cleanText(page.visualPlan?.layout || page.visual?.layout).toLowerCase()
+  const layout = resolveTemplateLayout(requestedLayout, role, templateId)
   switch (layout) {
     case "cover_scene":
       return coverTemplate
@@ -1291,6 +1298,15 @@ function addClassroomCue(elements: PPTElement[], page: AiCoursewarePage, palette
   void palette
 }
 
+function resolveTemplateLayout(requestedLayout: string, role: PageRole, templateId?: AiCoursewareTemplateId) {
+  // 乐谱、歌词、节奏和乐器页面具有内容专属性,不能被模板包的通用版式覆盖。
+  const contentSpecificLayouts = new Set(["lyric_focus", "score_focus", "rhythm_grid", "instrument_focus", "homework_list"])
+  if (contentSpecificLayouts.has(requestedLayout)) return requestedLayout
+  const templateRole = role as AiCoursewareTemplatePageRole
+  const packagedLayout = templateId ? getAiCoursewareTemplate(templateId).pageLayouts[templateRole] : undefined
+  return packagedLayout || requestedLayout
+}
+
 function dedicatedResourceElement(page: AiCoursewarePage): PPTElement | undefined {
   const binding = (page.resourceBindings || []).find(item => item.verified !== false && item.dedicatedPage)
   if (!binding || binding.resourceType !== "MUSIC") return undefined

+ 40 - 5
src/views/Editor/EditorHeader/AiCoursewareDialog.vue

@@ -82,7 +82,7 @@
               :disabled="working"
               @click="selectTemplate(template.id)"
             >
-              <span class="template-mini" :style="templateCardStyle(template)">
+              <span :class="['template-mini', template.layoutFamily]" :style="templateCardStyle(template)">
                 <i class="mini-band"></i>
                 <i class="mini-title"></i>
                 <i class="mini-content first"></i>
@@ -213,7 +213,7 @@
               <div>
                 <span>当前模板</span>
                 <strong>{{ selectedTemplate.name }}</strong>
-                <small>切换模板只重绘视觉,不改变大纲和教学内容</small>
+                <small>切换模板会重排页面结构,不改变教学内容</small>
               </div>
               <button type="button" :disabled="working" @click="templatePickerExpanded = !templatePickerExpanded">
                 {{ templatePickerExpanded ? "收起模板" : "更换模板" }}
@@ -920,7 +920,7 @@ function selectTemplate(templateId: AiCoursewareTemplateId, fromSample = false)
   selectedTemplateId.value = templateId
   if (fromSample) {
     templatePickerExpanded.value = false
-    ElMessage.success(`已切换为${getAiCoursewareTemplate(templateId).name},教学内容未改变`)
+    ElMessage.success(`已切换为${getAiCoursewareTemplate(templateId).name},已同步重排样张结构`)
   }
 }
 
@@ -937,7 +937,9 @@ function templateCardStyle(template: AiCoursewareTemplate): Record<string, strin
     "--template-secondary": palette.secondary,
     "--template-accent": palette.accent,
     "--template-background": palette.background,
-    "--template-surface": palette.surface
+    "--template-surface": palette.surface,
+    "--template-soft": palette.soft,
+    "--template-dark": palette.dark
   }
 }
 
@@ -990,6 +992,9 @@ function buildExtraPrompt() {
 function buildVisualPrompt() {
   const base = selectedTemplate.value.visualPrompt
   const system = `模板布局族:${selectedTemplate.value.layoutFamily}。字体体系:${selectedTemplate.value.typography}。背景体系:${selectedTemplate.value.backgroundStyle}。素材画风:${selectedTemplate.value.illustrationStyle}。`
+  const layouts = Object.entries(selectedTemplate.value.pageLayouts)
+    .map(([role, layout]) => `${role}使用${layout}`)
+    .join(";")
   const asset =
     preferences.assetStyle === "illustration"
       ? "主视觉使用统一画风插画。"
@@ -1008,7 +1013,7 @@ function buildVisualPrompt() {
       : paletteVariant.value === 2
         ? "配色使用柔和紫色与珊瑚色体系。"
         : "配色保持明快且有清晰对比。"
-  return `${base}${system}模板场景:${selectedTemplate.value.scene}。${asset}${source}${palette}整套课件必须共享角色外观、环境、光线和重复道具;一页只保留一个视觉焦点。`
+  return `${base}${system}模板页面族:${layouts}。模板场景:${selectedTemplate.value.scene}。${asset}${source}${palette}整套课件必须共享角色外观、环境、光线和重复道具;一页只保留一个视觉焦点。`
 }
 
 function formatPageNo(pageNo?: number) {
@@ -1481,6 +1486,36 @@ function resourceTypeName(type: string) {
     color: #8a96a3;
     font-size: 9px;
   }
+  &.story {
+    .mini-band { width: 100%; height: 100%; opacity: 0.22; }
+    .mini-title { left: 10%; top: 12px; width: 46%; }
+    .mini-content { left: 10%; background: rgba(255, 255, 255, 0.9); }
+    .mini-accent { right: 12%; bottom: 13px; width: 30px; height: 30px; opacity: 0.85; }
+  }
+  &.activity {
+    .mini-band { left: 7%; top: 12px; width: 18%; height: 42px; border-radius: 50%; }
+    .mini-title { left: 32%; width: 48%; }
+    .mini-content.first { left: 32%; top: 29px; width: 58%; }
+    .mini-content.second { left: 32%; top: 47px; width: 45%; }
+  }
+  &.technology {
+    background: var(--template-dark, #102744);
+    .mini-band { width: 100%; height: 3px; opacity: 0.8; }
+    .mini-title { left: 10%; background: var(--template-secondary); }
+    .mini-content { left: 10%; width: 76%; background: rgba(255, 255, 255, 0.15); }
+    .mini-accent { border-radius: 3px; }
+  }
+  &.culture {
+    .mini-band { left: 78%; width: 14%; height: 100%; opacity: 0.18; }
+    .mini-title { left: 10%; width: 44%; }
+    .mini-content { left: 10%; width: 58%; background: var(--template-soft); box-shadow: none; }
+  }
+  &.academic {
+    .mini-band { width: 4%; }
+    .mini-title { left: 12%; width: 54%; }
+    .mini-content { left: 12%; width: 76%; border-radius: 0; box-shadow: none; }
+    .mini-accent { border-radius: 1px; }
+  }
 }
 .current-template-card {
   display: flex;

Некоторые файлы не были показаны из-за большого количества измененных файлов