yonge 1 hafta önce
ebeveyn
işleme
eed72d38b1

+ 1 - 1
dist/assets/index-B63Vs1kC.js → dist/assets/index-B3jBHDVD.js

@@ -1 +1 @@
-import{_ as a}from"./404-R0y5dkvZ.js";import{a5 as o,en as t,ae as r,af as c,bM as _,bN as d,ak as s,bH as p}from"./index-Roqv-ag3.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,bM as _,bN as d,ak as s,bH as p}from"./index-QxPCrhOo.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};

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/assets/index-BIhOjY0V.js


+ 1 - 1
dist/assets/index-Css4rIox.js → dist/assets/index-BMdGGGaU.js

@@ -1 +1 @@
-import{_ as t}from"./404-R0y5dkvZ.js";import{E as c}from"./index-Bq0KUkHP.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,bM as m,bN as f,bH as u}from"./index-Roqv-ag3.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})])]))}}),S=u(v,[["__scopeId","data-v-340bbdaa"]]);export{S as default};
+import{_ as t}from"./404-R0y5dkvZ.js";import{E as c}from"./index-BIhOjY0V.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,bM as m,bN as f,bH as u}from"./index-QxPCrhOo.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})])]))}}),S=u(v,[["__scopeId","data-v-340bbdaa"]]);export{S as default};

+ 1 - 1
dist/assets/index-D883sfse.js → dist/assets/index-BO3KYykb.js

@@ -1 +1 @@
-import{u as c,af as l}from"./index-IymdfAbv.js";import{w as p,bA as d,a5 as g,cB as m,R as t,ae as r,af as u,al as f,ag as w,bH as k}from"./index-Roqv-ag3.js";import"./shapes-DyqWWBUj.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}))]))}}),B=k(_,[["__scopeId","data-v-90b2692a"]]);export{B as default};
+import{u as c,af as l}from"./index-CdAP7bqw.js";import{w as p,bA as d,a5 as g,cB as m,R as t,ae as r,af as u,al as f,ag as w,bH as k}from"./index-QxPCrhOo.js";import"./shapes-BtiB0LYH.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}))]))}}),B=k(_,[["__scopeId","data-v-90b2692a"]]);export{B as default};

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/assets/index-BlldfNpE.css


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/assets/index-CFzCdJE0.css


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
dist/assets/index-CWLj3MJ0.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/assets/index-CdAP7bqw.js


+ 1 - 1
dist/assets/index-CmiJx1j9.js → dist/assets/index-DQ_58T42.js

@@ -1 +1 @@
-import{u as s,af as r}from"./index-IymdfAbv.js";import{a5 as c,cB as n,R as p,ae as t,af as _,al as u,ag as i,bH as f}from"./index-Roqv-ag3.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}))]))}}),P=f(m,[["__scopeId","data-v-678905e9"]]);export{P as default};
+import{u as s,af as r}from"./index-CdAP7bqw.js";import{a5 as c,cB as n,R as p,ae as t,af as _,al as u,ag as i,bH as f}from"./index-QxPCrhOo.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}))]))}}),P=f(m,[["__scopeId","data-v-678905e9"]]);export{P as default};

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/assets/index-QxPCrhOo.js


+ 1 - 1
dist/assets/shapes-DyqWWBUj.js → dist/assets/shapes-BtiB0LYH.js

@@ -1,4 +1,4 @@
-import{cE as k,bB as $1}from"./index-Roqv-ag3.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-IymdfAbv.js";var z1={exports:{}};/*!
+import{cE as k,bB as $1}from"./index-QxPCrhOo.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-CdAP7bqw.js";var z1={exports:{}};/*!
  * clipboard.js v2.0.11
  * clipboard.js v2.0.11
  * https://clipboardjs.com/
  * https://clipboardjs.com/
  *
  *

+ 1 - 1
dist/index.html

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

+ 1 - 1
src/api/aiCourseware.ts

@@ -10,7 +10,6 @@ export interface AiCoursewareGenerateQuery {
   id: string
   id: string
   fromType?: string
   fromType?: string
   outline: AiCoursewareOutline
   outline: AiCoursewareOutline
-  visualPrompt?: string
 }
 }
 
 
 export interface AiCoursewareOutline {
 export interface AiCoursewareOutline {
@@ -30,6 +29,7 @@ export interface AiCoursewarePage {
   title: string
   title: string
   subtitle?: string
   subtitle?: string
   bullets: string[]
   bullets: string[]
+  designRequirement?: string
   teacherTips?: string
   teacherTips?: string
   interaction?: {
   interaction?: {
     type: string
     type: string

+ 252 - 174
src/views/Editor/EditorHeader/AiCoursewareDialog.vue

@@ -1,7 +1,7 @@
 <template>
 <template>
   <Modal
   <Modal
     :visible="visible"
     :visible="visible"
-    :width="980"
+    :width="1120"
     :close-button="true"
     :close-button="true"
     :close-on-click-mask="!working"
     :close-on-click-mask="!working"
     @update:visible="value => emit('update:visible', value)"
     @update:visible="value => emit('update:visible', value)"
@@ -9,19 +9,20 @@
     <div class="ai-dialog">
     <div class="ai-dialog">
       <div class="header">
       <div class="header">
         <div>
         <div>
-          <div class="title">AI生成课件</div>
-          <div class="sub">根据当前教材、单元和章节生成15页音乐课件</div>
+          <div class="title">{{ pageTitle }}</div>
+          <div class="sub">{{ pageMeta }}</div>
         </div>
         </div>
+        <div v-if="outline" class="page-count">{{ outline.pages.length }}页</div>
       </div>
       </div>
 
 
-      <div v-if="taskInfo" class="progress-panel">
-        <div class="progress-head">
-          <div class="progress-title">{{ taskInfo.message || "正在处理" }}</div>
-          <div class="progress-value">{{ taskInfo.progress || 0 }}%</div>
+      <div v-if="taskInfo" class="progress-strip">
+        <div class="step-list">
+          <span v-for="step in progressSteps" :key="step.threshold" :class="{ active: progressValue >= step.threshold }">{{ step.label }}</span>
         </div>
         </div>
-        <div class="progress-track">
-          <div class="progress-bar" :style="{ width: `${taskInfo.progress || 0}%` }"></div>
+        <div class="progress-line">
+          <div class="progress-bar" :style="{ width: `${progressValue}%` }"></div>
         </div>
         </div>
+        <div class="progress-value">{{ progressValue }}%</div>
       </div>
       </div>
 
 
       <div class="extra">
       <div class="extra">
@@ -29,48 +30,66 @@
         <TextArea v-model:value="extraPrompt" :rows="2" placeholder="可选,例如:多一些节奏互动,语言更适合小学低年级" :disabled="working" />
         <TextArea v-model:value="extraPrompt" :rows="2" placeholder="可选,例如:多一些节奏互动,语言更适合小学低年级" :disabled="working" />
       </div>
       </div>
 
 
-      <div class="background-setting">
-        <div>
-          <div class="label">课件背景图(可选)</div>
-          <div class="hint">建议 16:9,推荐 1920×1080 或 1600×900。上传后会应用到每一页。</div>
-        </div>
-        <div class="background-actions">
-          <FileInput @change="handleBackgroundUpload">
-            <button class="upload-btn" :disabled="working || uploadingBackground" type="button">
-              {{ uploadingBackground ? "上传中..." : backgroundImageUrl ? "更换背景图" : "上传背景图" }}
-            </button>
-          </FileInput>
-          <button v-if="backgroundImageUrl" class="remove-btn" :disabled="working" type="button" @click="removeBackground">移除</button>
+      <div v-if="outline" class="outline">
+        <div class="page-nav">
+          <button
+            v-for="page in outline.pages"
+            :key="page.pageNo"
+            class="nav-item"
+            :class="{ active: selectedPageNo === page.pageNo }"
+            type="button"
+            @click="selectedPageNo = page.pageNo"
+          >
+            <span class="nav-no">{{ formatPageNo(page.pageNo) }}</span>
+            <span class="nav-title">{{ page.title || "未命名页面" }}</span>
+          </button>
         </div>
         </div>
-        <div v-if="backgroundImageUrl" class="background-preview" :style="{ backgroundImage: `url(${backgroundImageUrl})` }"></div>
-      </div>
 
 
-      <div v-if="outline" class="outline">
-        <div class="outline-head">
-          <div>
-            <div class="outline-title">{{ outline.title }}</div>
-            <div class="outline-meta">{{ outlineMeta }}</div>
+        <div v-if="selectedPage" class="page-editor">
+          <div class="editor-head">
+            <div>
+              <div class="editor-page">第{{ selectedPage.pageNo }}页</div>
+              <input v-model="selectedPage.title" class="page-title" :disabled="working" />
+            </div>
+            <div class="page-type">{{ pageTypeLabel(selectedPage.type) }}</div>
           </div>
           </div>
-          <div class="count">{{ outline.pages.length }}页</div>
-        </div>
 
 
-        <div class="page-list">
-          <div v-for="page in outline.pages" :key="page.pageNo" class="page-item">
-            <div class="page-no">第{{ page.pageNo }}页</div>
-            <div class="page-content">
-              <input v-model="page.title" class="page-title" :disabled="working" />
+          <div class="field-grid">
+            <div class="field-card">
+              <div class="field-title">页面内容</div>
               <textarea
               <textarea
-                :value="page.bullets.join('\n')"
+                :value="selectedPage.bullets.join('\n')"
                 :disabled="working"
                 :disabled="working"
                 class="page-bullets"
                 class="page-bullets"
-                @input="event => updateBullets(page, event)"
+                @input="event => updateBullets(selectedPage!, event)"
               ></textarea>
               ></textarea>
             </div>
             </div>
+            <div class="field-card visual">
+              <div class="field-title">设计要求</div>
+              <textarea
+                v-model="selectedPage.designRequirement"
+                :disabled="working"
+                class="page-design"
+                placeholder="例如:左文右图,右侧放卡通山谷和音符,整体暖色活泼,文字保持大字号。"
+              ></textarea>
+            </div>
+          </div>
+
+          <div class="keyword-row">
+            <span class="keyword-label">图片关键词</span>
+            <input
+              :value="selectedPage.imageKeywords.join('、')"
+              class="keyword-input"
+              :disabled="working"
+              placeholder="用于检索网络图片,例如:山谷、音乐、卡通、小鸟"
+              @input="event => updateImageKeywords(selectedPage!, event)"
+            />
           </div>
           </div>
+          <div v-if="selectedPage.teacherTips" class="teacher-tip">教师提示:{{ selectedPage.teacherTips }}</div>
         </div>
         </div>
       </div>
       </div>
 
 
-      <div v-else class="empty">点击生成大纲后,可在这里确认和微调15页结构。</div>
+      <div v-else class="empty">生成大纲后,可在这里确认15页结构、页面内容和设计要求。</div>
 
 
       <div class="footer">
       <div class="footer">
         <Button @click="emit('update:visible', false)" :disabled="working">取消</Button>
         <Button @click="emit('update:visible', false)" :disabled="working">取消</Button>
@@ -87,13 +106,11 @@ import { ElMessage, ElMessageBox } from "element-plus"
 import Modal from "@/components/Modal.vue"
 import Modal from "@/components/Modal.vue"
 import Button from "@/components/Button.vue"
 import Button from "@/components/Button.vue"
 import TextArea from "@/components/TextArea.vue"
 import TextArea from "@/components/TextArea.vue"
-import FileInput from "@/components/FileInput.vue"
 import queryParams from "@/queryParams"
 import queryParams from "@/queryParams"
 import usePptWork from "@/store/pptWork"
 import usePptWork from "@/store/pptWork"
 import { useSlidesStore } from "@/store"
 import { useSlidesStore } from "@/store"
 import useHistorySnapshot from "@/hooks/useHistorySnapshot"
 import useHistorySnapshot from "@/hooks/useHistorySnapshot"
 import { httpAjaxErrMsg } from "@/plugins/httpAjax"
 import { httpAjaxErrMsg } from "@/plugins/httpAjax"
-import fileUpload from "@/utils/oss-file-upload"
 import {
 import {
   aiCoursewareGenerateApi,
   aiCoursewareGenerateApi,
   aiCoursewareOutlineApi,
   aiCoursewareOutlineApi,
@@ -122,13 +139,24 @@ const extraPrompt = ref("")
 const outline = ref<AiCoursewareOutline>()
 const outline = ref<AiCoursewareOutline>()
 const taskInfo = ref<AiCoursewareTaskInfo>()
 const taskInfo = ref<AiCoursewareTaskInfo>()
 const pollTimer = ref<number>()
 const pollTimer = ref<number>()
-const backgroundImageUrl = ref("")
-const uploadingBackground = ref(false)
+const selectedPageNo = ref(1)
 const working = computed(() => taskInfo.value?.status === "PENDING" || taskInfo.value?.status === "RUNNING")
 const working = computed(() => taskInfo.value?.status === "PENDING" || taskInfo.value?.status === "RUNNING")
-const outlineMeta = computed(() => {
-  if (!outline.value) return ""
+const progressValue = computed(() => taskInfo.value?.progress || 0)
+const progressSteps = [
+  { threshold: 0, label: "解析需求" },
+  { threshold: 25, label: "拆分结构" },
+  { threshold: 55, label: "生成内容" },
+  { threshold: 80, label: "匹配图片" }
+]
+const pageTitle = computed(() => outline.value?.chapterName || outline.value?.title || "课件大纲")
+const pageMeta = computed(() => {
+  if (!outline.value) return "根据当前教材、单元和章节生成15页音乐课件"
   return [outline.value.gradeName, outline.value.unitName].filter(Boolean).join(" · ")
   return [outline.value.gradeName, outline.value.unitName].filter(Boolean).join(" · ")
 })
 })
+const selectedPage = computed(() => {
+  const pages = outline.value?.pages || []
+  return pages.find(page => page.pageNo === selectedPageNo.value) || pages[0]
+})
 
 
 watch(
 watch(
   () => props.visible,
   () => props.visible,
@@ -151,6 +179,7 @@ async function handleOutline() {
   taskInfo.value = undefined
   taskInfo.value = undefined
   if (res.code === 200) {
   if (res.code === 200) {
     outline.value = res.data as AiCoursewareOutline
     outline.value = res.data as AiCoursewareOutline
+    selectedPageNo.value = outline.value.pages[0]?.pageNo || 1
   }
   }
 }
 }
 
 
@@ -168,8 +197,7 @@ async function handleGenerate() {
   const res = await httpAjaxErrMsg(aiCoursewareGenerateApi, {
   const res = await httpAjaxErrMsg(aiCoursewareGenerateApi, {
     id: pptWork.id,
     id: pptWork.id,
     fromType: queryParams.pptResourcesType || queryParams.fromType,
     fromType: queryParams.pptResourcesType || queryParams.fromType,
-    outline: outline.value,
-    visualPrompt: buildVisualPrompt()
+    outline: outline.value
   })
   })
   if (res.code !== 200) return
   if (res.code !== 200) return
 
 
@@ -216,7 +244,7 @@ async function applyResult(taskId: string) {
   if (res.code !== 200) return
   if (res.code !== 200) return
 
 
   const result = res.data as AiCoursewareGenerateResult
   const result = res.data as AiCoursewareGenerateResult
-  const converted = aiCoursewareToSlides(result.outline, { backgroundImage: backgroundImageUrl.value })
+  const converted = aiCoursewareToSlides(result.outline)
   slidesStore.setTitle(converted.title)
   slidesStore.setTitle(converted.title)
   slidesStore.setTheme(converted.theme)
   slidesStore.setTheme(converted.theme)
   slidesStore.setSlides(converted.slides)
   slidesStore.setSlides(converted.slides)
@@ -233,43 +261,39 @@ function updateBullets(page: AiCoursewarePage, event: Event) {
     .filter(Boolean)
     .filter(Boolean)
 }
 }
 
 
-async function handleBackgroundUpload(files: FileList) {
-  if (working.value || uploadingBackground.value) return
-  const file = files[0]
-  if (!file) return
-  if (!file.type.startsWith("image/")) {
-    ElMessage.error("请上传图片文件")
-    return
-  }
-  if (!pptWork.id) {
-    ElMessage.error("当前课件ID为空")
-    return
-  }
-  uploadingBackground.value = true
-  try {
-    backgroundImageUrl.value = await fileUpload(file.name, file, `${pptWork.id}/ai-background/`, true, { isLoading: true, text: "背景图上传中" })
-  } catch {
-    ElMessage.error("背景图上传失败")
-  } finally {
-    uploadingBackground.value = false
-  }
+function updateImageKeywords(page: AiCoursewarePage, event: Event) {
+  page.imageKeywords = (event.target as HTMLInputElement).value
+    .split(/[,,、;;\s]+/)
+    .map(item => item.trim())
+    .filter(Boolean)
+    .slice(0, 6)
 }
 }
 
 
-function removeBackground() {
-  backgroundImageUrl.value = ""
+function buildExtraPrompt() {
+  return extraPrompt.value.trim()
 }
 }
 
 
-function buildExtraPrompt() {
-  const prompts = [extraPrompt.value]
-  if (backgroundImageUrl.value) {
-    prompts.push(buildVisualPrompt())
-  }
-  return prompts.filter(Boolean).join("\n")
+function formatPageNo(pageNo?: number) {
+  return String(pageNo || 0).padStart(2, "0")
 }
 }
 
 
-function buildVisualPrompt() {
-  if (!backgroundImageUrl.value) return ""
-  return "用户将使用自定义16:9背景图作为每页底图。请生成适合叠加在背景图上的课件内容:每页文字更精简,标题清楚,保留留白,强调高对比和清晰层次,避免生成需要大面积纯色背景才能阅读的密集内容。"
+function pageTypeLabel(type?: string) {
+  const labels: Record<string, string> = {
+    cover: "封面",
+    intro: "导入",
+    goal: "目标",
+    background: "背景",
+    listen: "聆听",
+    content: "内容",
+    knowledge: "知识",
+    skill: "技能",
+    interaction: "互动",
+    activity: "活动",
+    extension: "拓展",
+    summary: "总结",
+    homework: "练习"
+  }
+  return labels[type || ""] || "页面"
 }
 }
 </script>
 </script>
 
 
@@ -278,51 +302,58 @@ function buildVisualPrompt() {
   height: 82vh;
   height: 82vh;
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;
+  color: #1f2933;
 }
 }
 .header,
 .header,
-.outline-head,
 .footer {
 .footer {
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
   justify-content: space-between;
   justify-content: space-between;
 }
 }
 .title {
 .title {
-  font-size: 18px;
+  font-size: 24px;
   font-weight: 700;
   font-weight: 700;
   color: #1f2933;
   color: #1f2933;
 }
 }
-.sub,
-.outline-meta,
-.hint {
+.sub {
   margin-top: 6px;
   margin-top: 6px;
   color: #7b8794;
   color: #7b8794;
-  font-size: 13px;
+  font-size: 14px;
 }
 }
-.progress-panel {
-  margin-top: 18px;
-  padding: 14px 16px;
-  border: 1px solid rgba($color: $themeColor, $alpha: 0.2);
-  background: rgba($color: $themeColor, $alpha: 0.06);
-  border-radius: 8px;
+.page-count {
+  color: #52616b;
+  font-size: 14px;
+  font-weight: 600;
 }
 }
-.progress-head {
+.progress-strip {
+  margin-top: 12px;
+  height: 42px;
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
-  justify-content: space-between;
-  margin-bottom: 10px;
+  gap: 12px;
 }
 }
-.progress-title {
-  font-size: 14px;
-  font-weight: 600;
-  color: #1f2933;
+.step-list {
+  display: flex;
+  align-items: center;
+  gap: 14px;
+  flex-shrink: 0;
+  color: #9aa5b1;
+  font-size: 13px;
+  span.active {
+    color: $themeColor;
+    font-weight: 600;
+  }
 }
 }
 .progress-value {
 .progress-value {
-  font-size: 14px;
+  width: 42px;
+  text-align: right;
+  font-size: 13px;
   color: $themeColor;
   color: $themeColor;
   font-weight: 700;
   font-weight: 700;
 }
 }
-.progress-track {
-  height: 8px;
+.progress-line {
+  height: 6px;
+  flex: 1;
   overflow: hidden;
   overflow: hidden;
   border-radius: 999px;
   border-radius: 999px;
   background: #e7edf5;
   background: #e7edf5;
@@ -334,104 +365,124 @@ function buildVisualPrompt() {
   transition: width 0.25s ease;
   transition: width 0.25s ease;
 }
 }
 .extra {
 .extra {
-  margin-top: 18px;
+  margin-top: 12px;
 }
 }
-.background-setting {
-  position: relative;
+.label {
+  margin-bottom: 8px;
+  font-size: 14px;
+  color: #52616b;
+}
+.outline {
   margin-top: 14px;
   margin-top: 14px;
-  padding: 14px 150px 14px 14px;
-  min-height: 74px;
+  min-height: 0;
+  flex: 1;
+  display: grid;
+  grid-template-columns: 280px minmax(0, 1fr);
+  gap: 14px;
+}
+.page-nav {
+  overflow: auto;
   border: 1px solid #edf1f5;
   border: 1px solid #edf1f5;
   border-radius: 8px;
   border-radius: 8px;
   background: #fafcff;
   background: #fafcff;
+  padding: 8px;
 }
 }
-.background-actions {
+.nav-item {
+  width: 100%;
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
-  gap: 8px;
-  margin-top: 10px;
-}
-.upload-btn,
-.remove-btn {
-  height: 32px;
-  padding: 0 12px;
+  gap: 10px;
+  min-height: 42px;
+  padding: 8px 10px;
+  border: 0;
   border-radius: 6px;
   border-radius: 6px;
-  border: 1px solid #d9d9d9;
-  background: #fff;
+  background: transparent;
+  color: #52616b;
+  text-align: left;
   cursor: pointer;
   cursor: pointer;
+  &.active {
+    background: #eaf4ff;
+    color: #1f2933;
+    .nav-no {
+      color: #fff;
+      background: $themeColor;
+    }
+  }
 }
 }
-.upload-btn {
-  color: $themeColor;
-  border-color: rgba($color: $themeColor, $alpha: 0.55);
-}
-.remove-btn {
+.nav-no {
+  width: 34px;
+  height: 28px;
+  line-height: 28px;
+  text-align: center;
+  border-radius: 5px;
   color: #7b8794;
   color: #7b8794;
+  background: #edf1f5;
+  flex-shrink: 0;
+  font-size: 12px;
+  font-weight: 600;
 }
 }
-.background-preview {
-  position: absolute;
-  right: 14px;
-  top: 14px;
-  width: 120px;
-  height: 68px;
-  border-radius: 6px;
-  border: 1px solid #d9d9d9;
-  background-size: cover;
-  background-position: center;
-}
-.label {
-  margin-bottom: 8px;
+.nav-title {
+  min-width: 0;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
   font-size: 13px;
   font-size: 13px;
-  color: #52616b;
+  font-weight: 600;
 }
 }
-.outline {
-  margin-top: 18px;
-  min-height: 0;
-  flex: 1;
+.page-editor {
+  min-width: 0;
+  overflow: auto;
+  border: 1px solid #edf1f5;
+  border-radius: 8px;
+  background: #fff;
+  padding: 16px;
+}
+.editor-head {
   display: flex;
   display: flex;
-  flex-direction: column;
+  align-items: flex-start;
+  justify-content: space-between;
+  gap: 16px;
 }
 }
-.outline-title {
-  font-size: 15px;
-  font-weight: 700;
+.editor-page {
+  margin-bottom: 8px;
+  color: #7b8794;
+  font-size: 13px;
 }
 }
-.count {
-  color: #52616b;
+.page-type {
+  height: 28px;
+  padding: 0 12px;
+  line-height: 28px;
+  border-radius: 999px;
+  color: $themeColor;
+  background: rgba($color: $themeColor, $alpha: 0.08);
   font-size: 13px;
   font-size: 13px;
+  flex-shrink: 0;
 }
 }
-.page-list {
+.field-grid {
   margin-top: 12px;
   margin-top: 12px;
-  overflow: auto;
+  display: grid;
+  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
+  gap: 12px;
+}
+.field-card {
+  padding: 12px;
   border: 1px solid #edf1f5;
   border: 1px solid #edf1f5;
   border-radius: 8px;
   border-radius: 8px;
-}
-.page-item {
-  display: flex;
-  padding: 14px;
-  border-bottom: 1px solid #edf1f5;
-  background: #fff;
-  &:last-child {
-    border-bottom: 0;
+  background: #fbfcfe;
+  &.visual {
+    background: #fffaf3;
   }
   }
 }
 }
-.page-no {
-  width: 64px;
-  height: 30px;
-  line-height: 30px;
-  text-align: center;
-  border-radius: 6px;
-  color: #fff;
-  background: $themeColor;
-  flex-shrink: 0;
+.field-title {
+  margin-bottom: 8px;
+  color: #52616b;
   font-size: 13px;
   font-size: 13px;
   font-weight: 600;
   font-weight: 600;
 }
 }
-.page-content {
-  margin-left: 12px;
-  min-width: 0;
-  flex: 1;
-}
 .page-title,
 .page-title,
-.page-bullets {
+.page-bullets,
+.page-design,
+.keyword-input {
   width: 100%;
   width: 100%;
   border: 1px solid #d9d9d9;
   border: 1px solid #d9d9d9;
   border-radius: 6px;
   border-radius: 6px;
@@ -441,18 +492,45 @@ function buildVisualPrompt() {
 .page-title {
 .page-title {
   height: 36px;
   height: 36px;
   padding: 0 10px;
   padding: 0 10px;
+  font-size: 16px;
   font-weight: 600;
   font-weight: 600;
 }
 }
-.page-bullets {
-  margin-top: 8px;
-  height: 86px;
+.page-bullets,
+.page-design {
+  height: 180px;
   padding: 10px;
   padding: 10px;
   resize: vertical;
   resize: vertical;
   line-height: 1.5;
   line-height: 1.5;
 }
 }
+.keyword-row {
+  margin-top: 12px;
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+.keyword-label {
+  width: 72px;
+  flex-shrink: 0;
+  color: #52616b;
+  font-size: 13px;
+  font-weight: 600;
+}
+.keyword-input {
+  height: 34px;
+  padding: 0 10px;
+}
+.teacher-tip {
+  margin-top: 12px;
+  padding: 10px 12px;
+  border-radius: 6px;
+  color: #52616b;
+  background: #f7f9fc;
+  font-size: 13px;
+  line-height: 1.5;
+}
 .empty {
 .empty {
   flex: 1;
   flex: 1;
-  margin-top: 18px;
+  margin-top: 14px;
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
   justify-content: center;
   justify-content: center;
@@ -463,7 +541,7 @@ function buildVisualPrompt() {
   min-height: 20px;
   min-height: 20px;
 }
 }
 .footer {
 .footer {
-  margin-top: 18px;
+  margin-top: 14px;
   justify-content: flex-end;
   justify-content: flex-end;
   gap: 10px;
   gap: 10px;
 }
 }

+ 1 - 1
src/views/Editor/EditorHeader/index.vue

@@ -23,7 +23,7 @@
                 aiCoursewareVisible = true
                 aiCoursewareVisible = true
               }
               }
             "
             "
-            >AI生成课件</PopoverMenuItem
+            >AI生成PPT</PopoverMenuItem
           >
           >
           <PopoverMenuItem
           <PopoverMenuItem
             @click="
             @click="

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor