Просмотр исходного кода

Merge branch 'beta' of https://git.cocorobo.cn/jack/PPT into beta

lsc 1 неделя назад
Родитель
Сommit
9c619681dc

+ 8 - 26
package-lock.json

@@ -43,7 +43,7 @@
         "qs": "^6.14.0",
         "rtf.js": "^3.0.9",
         "svg-arc-to-cubic-bezier": "^3.2.0",
-        "svg-pathdata": "^7.1.0",
+        "svg-pathdata": "^7.2.0",
         "tinycolor2": "^1.6.0",
         "tippy.js": "^6.3.7",
         "utif": "^3.1.0",
@@ -5744,12 +5744,10 @@
       "integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g=="
     },
     "node_modules/svg-pathdata": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmmirror.com/svg-pathdata/-/svg-pathdata-7.1.0.tgz",
-      "integrity": "sha512-wrvKHXZSYZyODOj5E1l1bMTIo8sR7YCH0E4SA8IgLgMsZq4RypslpYvNSsrdg4ThD6du2KWPyVeKinkqUelGhg==",
-      "dependencies": {
-        "yerror": "^8.0.0"
-      },
+      "version": "7.2.0",
+      "resolved": "https://registry.npmmirror.com/svg-pathdata/-/svg-pathdata-7.2.0.tgz",
+      "integrity": "sha512-qd+AxqMpfRrRQaWb2SrNFvn69cvl6piqY8TxhYl2Li1g4/LO5F9NJb5wI4vNwRryqgSgD43gYKLm/w3ag1bKvQ==",
+      "license": "MIT",
       "engines": {
         "node": ">=20.11.1"
       }
@@ -6344,14 +6342,6 @@
         "node": ">=12"
       }
     },
-    "node_modules/yerror": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmmirror.com/yerror/-/yerror-8.0.0.tgz",
-      "integrity": "sha512-FemWD5/UqNm8ffj8oZIbjWXIF2KE0mZssggYpdaQkWDDgXBQ/35PNIxEuz6/YLn9o0kOxDBNJe8x8k9ljD7k/g==",
-      "engines": {
-        "node": ">=18.16.0"
-      }
-    },
     "node_modules/yjs": {
       "version": "13.6.27",
       "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.27.tgz",
@@ -10595,12 +10585,9 @@
       "integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g=="
     },
     "svg-pathdata": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmmirror.com/svg-pathdata/-/svg-pathdata-7.1.0.tgz",
-      "integrity": "sha512-wrvKHXZSYZyODOj5E1l1bMTIo8sR7YCH0E4SA8IgLgMsZq4RypslpYvNSsrdg4ThD6du2KWPyVeKinkqUelGhg==",
-      "requires": {
-        "yerror": "^8.0.0"
-      }
+      "version": "7.2.0",
+      "resolved": "https://registry.npmmirror.com/svg-pathdata/-/svg-pathdata-7.2.0.tgz",
+      "integrity": "sha512-qd+AxqMpfRrRQaWb2SrNFvn69cvl6piqY8TxhYl2Li1g4/LO5F9NJb5wI4vNwRryqgSgD43gYKLm/w3ag1bKvQ=="
     },
     "text-extensions": {
       "version": "2.4.0",
@@ -11010,11 +10997,6 @@
       "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
       "dev": true
     },
-    "yerror": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmmirror.com/yerror/-/yerror-8.0.0.tgz",
-      "integrity": "sha512-FemWD5/UqNm8ffj8oZIbjWXIF2KE0mZssggYpdaQkWDDgXBQ/35PNIxEuz6/YLn9o0kOxDBNJe8x8k9ljD7k/g=="
-    },
     "yjs": {
       "version": "13.6.27",
       "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.27.tgz",

+ 1 - 1
package.json

@@ -50,7 +50,7 @@
     "qs": "^6.14.0",
     "rtf.js": "^3.0.9",
     "svg-arc-to-cubic-bezier": "^3.2.0",
-    "svg-pathdata": "^7.1.0",
+    "svg-pathdata": "^7.2.0",
     "tinycolor2": "^1.6.0",
     "tippy.js": "^6.3.7",
     "utif": "^3.1.0",

+ 3 - 4
src/hooks/useImport.ts

@@ -1947,10 +1947,9 @@ export default () => {
                 }
 
                 if (shape) {
-                  element.path = shape.path
-                  // const { maxX, maxY } = getSvgPathRange(el.path);
-                  element.viewBox = shape.viewBox
-                  // element.viewBox = [originWidth || maxX, originHeight || maxY];
+                  const { maxX, maxY } = getSvgPathRange(el.path)
+                  element.path = el.path
+                  element.viewBox = poriginWidth ? [maxX, maxY] : [originWidth, originHeight]
                   if (shape.pathFormula) {
                     element.pathFormula = shape.pathFormula
                     element.viewBox = [el.width, el.height]

+ 219 - 1
src/utils/svgPathParser.ts

@@ -114,7 +114,7 @@ export const toPoints = (d: string) => {
   }
   return points
 }
-
+/*
 export const getSvgPathRange = (path: string) => {
   try {
     const pathData = new SVGPathData(path)
@@ -143,4 +143,222 @@ export const getSvgPathRange = (path: string) => {
   }
 }
 
+*/
+
+// 辅助:采样三次贝塞尔曲线
+function sampleCubicBezier(
+  x0: number, y0: number, x1: number, y1: number,
+  x2: number, y2: number, x3: number, y3: number,
+  samples = 20
+): { x: number; y: number }[] {
+  const points = [];
+  for (let i = 0; i <= samples; i++) {
+    const t = i / samples;
+    const mt = 1 - t;
+    const x = mt ** 3 * x0 + 3 * mt ** 2 * t * x1 + 3 * mt * t ** 2 * x2 + t ** 3 * x3;
+    const y = mt ** 3 * y0 + 3 * mt ** 2 * t * y1 + 3 * mt * t ** 2 * y2 + t ** 3 * y3;
+    points.push({ x, y });
+  }
+  return points;
+}
+
+// 辅助:采样二次贝塞尔曲线
+function sampleQuadraticBezier(
+  x0: number, y0: number, x1: number, y1: number,
+  x2: number, y2: number, samples = 20
+): { x: number; y: number }[] {
+  const points = [];
+  for (let i = 0; i <= samples; i++) {
+    const t = i / samples;
+    const mt = 1 - t;
+    const x = mt ** 2 * x0 + 2 * mt * t * x1 + t ** 2 * x2;
+    const y = mt ** 2 * y0 + 2 * mt * t * y1 + t ** 2 * y2;
+    points.push({ x, y });
+  }
+  return points;
+}
+
+// 辅助:采样椭圆弧(基于 SVG 规范参数方程)
+function sampleArc(
+  x0: number, y0: number, rx: number, ry: number,
+  xAxisRotation: number, largeArcFlag: boolean, sweepFlag: boolean,
+  x: number, y: number, samples = 30
+): { x: number; y: number }[] {
+  const phi = (xAxisRotation * Math.PI) / 180;
+  const cosPhi = Math.cos(phi);
+  const sinPhi = Math.sin(phi);
+
+  const dx = (x0 - x) / 2;
+  const dy = (y0 - y) / 2;
+  const x1p = cosPhi * dx + sinPhi * dy;
+  const y1p = -sinPhi * dx + cosPhi * dy;
+
+  let rxx = Math.abs(rx);
+  let ryy = Math.abs(ry);
+  const lambda = (x1p * x1p) / (rxx * rxx) + (y1p * y1p) / (ryy * ryy);
+  if (lambda > 1) {
+    rxx *= Math.sqrt(lambda);
+    ryy *= Math.sqrt(lambda);
+  }
+
+  const sq = Math.sqrt(
+    (rxx * rxx * (ryy * ryy) - rxx * rxx * (y1p * y1p) - ryy * ryy * (x1p * x1p)) /
+    (rxx * rxx * (y1p * y1p) + ryy * ryy * (x1p * x1p))
+  );
+  const sign = largeArcFlag === sweepFlag ? -1 : 1;
+  const cxp = sign * sq * ((rxx * y1p) / ryy);
+  const cyp = sign * sq * ((-ryy * x1p) / rxx);
+
+  const cx = cosPhi * cxp - sinPhi * cyp + (x0 + x) / 2;
+  const cy = sinPhi * cxp + cosPhi * cyp + (y0 + y) / 2;
+
+  const vectorAngle = (ux: number, uy: number, vx: number, vy: number) => {
+    const dot = ux * vx + uy * vy;
+    const len = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
+    let ang = Math.acos(Math.max(-1, Math.min(1, dot / len)));
+    const cross = ux * vy - uy * vx;
+    return cross < 0 ? -ang : ang;
+  };
+
+  const ux = (x1p - cxp) / rxx;
+  const uy = (y1p - cyp) / ryy;
+  const vx = (-x1p - cxp) / rxx;
+  const vy = (-y1p - cyp) / ryy;
+
+  let startAngle = vectorAngle(1, 0, ux, uy);
+  let deltaAngle = vectorAngle(ux, uy, vx, vy);
+
+  if (!sweepFlag && deltaAngle > 0) {
+    deltaAngle -= 2 * Math.PI;
+  } else if (sweepFlag && deltaAngle < 0) {
+    deltaAngle += 2 * Math.PI;
+  }
+
+  const points: { x: number; y: number }[] = [];
+  for (let i = 0; i <= samples; i++) {
+    const t = i / samples;
+    const angle = startAngle + t * deltaAngle;
+    const xp = rxx * Math.cos(angle);
+    const yp = ryy * Math.sin(angle);
+    const px = cosPhi * xp - sinPhi * yp + cx;
+    const py = sinPhi * xp + cosPhi * yp + cy;
+    points.push({ x: px, y: py });
+  }
+  return points;
+}
+
+// 主函数
+export const getSvgPathRange = (path: string) => {
+  try {
+    const pathData = new SVGPathData(path);
+    let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
+    let curX = 0, curY = 0;           // 当前点(上一命令终点)
+    let startX = 0, startY = 0;        // 子路径起点(用于 Z)
+
+    const updateBounds = (x: number, y: number) => {
+      minX = Math.min(minX, x);
+      minY = Math.min(minY, y);
+      maxX = Math.max(maxX, x);
+      maxY = Math.max(maxY, y);
+    };
+
+    const processPoints = (points: { x: number; y: number }[]) => {
+      points.forEach(p => updateBounds(p.x, p.y));
+    };
+
+    for (const cmd of pathData.commands) {
+      switch (cmd.type) {
+        case SVGPathData.MOVE_TO:
+          curX = cmd.x;
+          curY = cmd.y;
+          startX = curX;
+          startY = curY;
+          updateBounds(curX, curY);
+          break;
+
+        case SVGPathData.LINE_TO:
+          curX = cmd.x;
+          curY = cmd.y;
+          updateBounds(curX, curY);
+          break;
+
+        case SVGPathData.HORIZ_LINE_TO:
+          curX = cmd.x;
+          updateBounds(curX, curY);
+          break;
+
+        case SVGPathData.VERT_LINE_TO:
+          curY = cmd.y;
+          updateBounds(curX, curY);
+          break;
+
+        case SVGPathData.CURVE_TO:
+          {
+            const points = sampleCubicBezier(
+              curX, curY,
+              cmd.x1, cmd.y1,
+              cmd.x2, cmd.y2,
+              cmd.x, cmd.y
+            );
+            processPoints(points);
+            curX = cmd.x;
+            curY = cmd.y;
+          }
+          break;
+
+        case SVGPathData.SMOOTH_CURVE_TO:
+          // 为简化,此处省略反射控制点的精确计算,可根据需要补充
+          // 可直接使用采样近似或添加反射逻辑
+          break;
+
+        case SVGPathData.QUAD_TO:
+          {
+            const points = sampleQuadraticBezier(
+              curX, curY,
+              cmd.x1, cmd.y1,
+              cmd.x, cmd.y
+            );
+            processPoints(points);
+            curX = cmd.x;
+            curY = cmd.y;
+          }
+          break;
+
+        case SVGPathData.SMOOTH_QUAD_TO:
+          // 省略
+          break;
+
+        case SVGPathData.ARC:
+          {
+            const points = sampleArc(
+              curX, curY,
+              cmd.rX, cmd.rY,
+              cmd.xRot,
+              cmd.lArcFlag, cmd.sweepFlag,
+              cmd.x, cmd.y
+            );
+            processPoints(points);
+            curX = cmd.x;
+            curY = cmd.y;
+          }
+          break;
+
+        case SVGPathData.CLOSE_PATH:
+          curX = startX;
+          curY = startY;
+          updateBounds(curX, curY);
+          break;
+      }
+    }
+
+    if (minX === Infinity) {
+      return { minX: 0, minY: 0, maxX: 0, maxY: 0 };
+    }
+
+    return { minX, minY, maxX, maxY };
+  } catch {
+    return { minX: 0, minY: 0, maxX: 0, maxY: 0 };
+  }
+};
+
 export type SvgPoints = ReturnType<typeof toPoints>

+ 293 - 124
src/views/Student/components/choiceQuestionDetailDialog.vue

@@ -16,19 +16,22 @@
               .teststitle
           }}</div>
           <div class="c_t45_msg">
-            <div>{{ lang.ssAnswerCount }} {{ props.showData.workArray.length}}<span v-if="props.showData.unsubmittedStudents.length > 0">/{{ props.showData.unsubmittedStudents.length }}</span></div>
-            <span v-if="props.showData.unsubmittedStudents.length > 0" @click="viewUnsubmittedStudents()">{{ lang.ssViewUnsubmittedStudents }}</span>
+            <div>{{ lang.ssAnswerCount }} {{ props.showData.workArray.length }}<span
+                v-if="props.showData.unsubmittedStudents.length > 0">/{{ props.showData.unsubmittedStudents.length
+                }}</span></div>
+            <span v-if="props.showData.unsubmittedStudents.length > 0" @click="viewUnsubmittedStudents()">{{
+              lang.ssViewUnsubmittedStudents }}</span>
           </div>
           <!--<span class="c_t45_t_btn" :class="{'c_t45_t_btn_noActive': props.showData.workIndex <= 0}" @click="changeWorkIndex(0)">{{ lang.ssPrevQ }}</span>-->
           <!--<span class="c_t45_t_btn" :class="{'c_t45_t_btn_noActive': props.showData.workIndex >= props.showData.choiceQuestionListData.length - 1}" @click="changeWorkIndex(1)">{{ lang.ssNextQ }}</span>-->
         </div>
         <img class="c_t45_img" :src="props.showData.choiceQuestionListData[props.showData.workIndex]
-            .timuList[0].src
+          .timuList[0].src
           " v-if="props.showData.choiceQuestionListData[props.showData.workIndex] &&
             props.showData.choiceQuestionListData[props.showData.workIndex]
               .timuList.length > 0
           " @click="previewImageToolRef.previewImage(props.showData.choiceQuestionListData[props.showData.workIndex]
-            .timuList[0].src)"/>
+            .timuList[0].src)" />
         <!-- <span class="c_t45_type" v-if="
           props.showData.choiceQuestionListData[props.showData.workIndex]
             .type === '1'
@@ -43,21 +46,34 @@
         }">
           <div id="echartsArea1" ref="echartsArea1"></div>
         </div>
-        <div class="aiAnalysis" v-if="props.workArray.length>0">
+        <div class="aiAnalysis" v-if="props.workArray.length > 0">
           <div class="ai_header">
             <div class="ai_title">
-              <svg viewBox="0 0 1024 1024" width="200" height="200"><path d="M512 170.666667C323.477333 170.666667 170.666667 323.477333 170.666667 512s152.810667 341.333333 341.333333 341.333333 341.333333-152.810667 341.333333-341.333333S700.522667 170.666667 512 170.666667zM85.333333 512C85.333333 276.352 276.352 85.333333 512 85.333333s426.666667 191.018667 426.666667 426.666667-191.018667 426.666667-426.666667 426.666667S85.333333 747.648 85.333333 512z"></path><path d="M693.013333 330.986667a42.666667 42.666667 0 0 1 10.304 43.648l-75.413333 226.282666a42.666667 42.666667 0 0 1-26.986667 26.986667l-226.282666 75.413333a42.666667 42.666667 0 0 1-53.973334-53.973333l75.434667-226.261333a42.666667 42.666667 0 0 1 26.986667-26.986667l226.282666-75.413333a42.666667 42.666667 0 0 1 43.648 10.304z m-222.72 139.306666l-41.685333 125.098667 125.077333-41.706667 41.706667-125.077333-125.077333 41.706667z"></path></svg>{{ lang.ssAnalysis }}
+              <svg viewBox="0 0 1024 1024" width="200" height="200">
+                <path
+                  d="M512 170.666667C323.477333 170.666667 170.666667 323.477333 170.666667 512s152.810667 341.333333 341.333333 341.333333 341.333333-152.810667 341.333333-341.333333S700.522667 170.666667 512 170.666667zM85.333333 512C85.333333 276.352 276.352 85.333333 512 85.333333s426.666667 191.018667 426.666667 426.666667-191.018667 426.666667-426.666667 426.666667S85.333333 747.648 85.333333 512z">
+                </path>
+                <path
+                  d="M693.013333 330.986667a42.666667 42.666667 0 0 1 10.304 43.648l-75.413333 226.282666a42.666667 42.666667 0 0 1-26.986667 26.986667l-226.282666 75.413333a42.666667 42.666667 0 0 1-53.973334-53.973333l75.434667-226.261333a42.666667 42.666667 0 0 1 26.986667-26.986667l226.282666-75.413333a42.666667 42.666667 0 0 1 43.648 10.304z m-222.72 139.306666l-41.685333 125.098667 125.077333-41.706667 41.706667-125.077333-125.077333 41.706667z">
+                </path>
+              </svg>{{ lang.ssAnalysis }}
             </div>
-            <div class="ai_refresh" :class="{'disabled': currentAnalysis && currentAnalysis.loading}" @click="aiAnalysisRefresh45()">
+            <div class="ai_refresh" :class="{ 'disabled': currentAnalysis && currentAnalysis.loading }"
+              @click="aiAnalysisRefresh45()">
               {{ lang.ssAIGenerate }}
-             <svg viewBox="0 0 1024 1024" width="200" height="200"><path d="M875 483c-33.4 0-60.5 27.1-60.5 60.5v0.1C814.4 710.3 678.8 846 512 846S209.5 710.3 209.5 543.5 345.2 241 512 241c36.8 0 71.7 7.6 104.4 19.7-32 3-57.4 29.1-57.4 61.9 0 34.8 28.2 63 63 63h201.9c34.8 0 63-28.2 63-63V120c0-34.8-28.2-63-63-63s-63 28.2-63 63v81.4C691 150.5 605.2 120 512 120 278.1 120 88.5 309.6 88.5 543.5S278.1 967 512 967s423.5-189.6 423.5-423.5c0-33.4-27.1-60.5-60.5-60.5z"></path></svg>
+              <svg viewBox="0 0 1024 1024" width="200" height="200">
+                <path
+                  d="M875 483c-33.4 0-60.5 27.1-60.5 60.5v0.1C814.4 710.3 678.8 846 512 846S209.5 710.3 209.5 543.5 345.2 241 512 241c36.8 0 71.7 7.6 104.4 19.7-32 3-57.4 29.1-57.4 61.9 0 34.8 28.2 63 63 63h201.9c34.8 0 63-28.2 63-63V120c0-34.8-28.2-63-63-63s-63 28.2-63 63v81.4C691 150.5 605.2 120 512 120 278.1 120 88.5 309.6 88.5 543.5S278.1 967 512 967s423.5-189.6 423.5-423.5c0-33.4-27.1-60.5-60.5-60.5z">
+                </path>
+              </svg>
 
             </div>
           </div>
           <div class="ai_content" v-if="currentAnalysis">
             {{ currentAnalysis.json }}
           </div>
-          <div class="ai_updateTime" v-if="currentAnalysis">{{ lang.ssUpdateTime }}:{{ currentAnalysis.update_at }}</div>
+          <div class="ai_updateTime" v-if="currentAnalysis">{{ lang.ssUpdateTime }}:{{ currentAnalysis.update_at }}
+          </div>
         </div>
         <div class="cq_changeBtn" v-if="props.showData.choiceQuestionListData.length > 1">
           <div :class="{ cq_cb_disabled: props.showData.workIndex <= 0 }" @click="changeWorkIndex(0)">
@@ -67,7 +83,7 @@
                 fill=""></path>
             </svg>
           </div>
-          <span>{{ props.showData.workIndex+1 }}/{{ props.showData.choiceQuestionListData.length }}</span>
+          <span>{{ props.showData.workIndex + 1 }}/{{ props.showData.choiceQuestionListData.length }}</span>
           <div :class="{ cq_cb_disabled: props.showData.workIndex >= props.showData.choiceQuestionListData.length - 1 }"
             @click="changeWorkIndex(1)">
             <svg style="transform: rotate(90deg);" viewBox="0 0 1024 1024" version="1.1" width="200" height="200">
@@ -95,21 +111,34 @@
           </div>
         </div>
 
-       <div class="aiAnalysis" style="margin-top:1rem ;" v-if="processedWorkArray.length>0">
+        <div class="aiAnalysis" style="margin-top:1rem ;" v-if="processedWorkArray.length > 0">
           <div class="ai_header">
             <div class="ai_title">
-              <svg viewBox="0 0 1024 1024" width="200" height="200"><path d="M512 170.666667C323.477333 170.666667 170.666667 323.477333 170.666667 512s152.810667 341.333333 341.333333 341.333333 341.333333-152.810667 341.333333-341.333333S700.522667 170.666667 512 170.666667zM85.333333 512C85.333333 276.352 276.352 85.333333 512 85.333333s426.666667 191.018667 426.666667 426.666667-191.018667 426.666667-426.666667 426.666667S85.333333 747.648 85.333333 512z"></path><path d="M693.013333 330.986667a42.666667 42.666667 0 0 1 10.304 43.648l-75.413333 226.282666a42.666667 42.666667 0 0 1-26.986667 26.986667l-226.282666 75.413333a42.666667 42.666667 0 0 1-53.973334-53.973333l75.434667-226.261333a42.666667 42.666667 0 0 1 26.986667-26.986667l226.282666-75.413333a42.666667 42.666667 0 0 1 43.648 10.304z m-222.72 139.306666l-41.685333 125.098667 125.077333-41.706667 41.706667-125.077333-125.077333 41.706667z"></path></svg>{{ lang.ssAnalysis }}
+              <svg viewBox="0 0 1024 1024" width="200" height="200">
+                <path
+                  d="M512 170.666667C323.477333 170.666667 170.666667 323.477333 170.666667 512s152.810667 341.333333 341.333333 341.333333 341.333333-152.810667 341.333333-341.333333S700.522667 170.666667 512 170.666667zM85.333333 512C85.333333 276.352 276.352 85.333333 512 85.333333s426.666667 191.018667 426.666667 426.666667-191.018667 426.666667-426.666667 426.666667S85.333333 747.648 85.333333 512z">
+                </path>
+                <path
+                  d="M693.013333 330.986667a42.666667 42.666667 0 0 1 10.304 43.648l-75.413333 226.282666a42.666667 42.666667 0 0 1-26.986667 26.986667l-226.282666 75.413333a42.666667 42.666667 0 0 1-53.973334-53.973333l75.434667-226.261333a42.666667 42.666667 0 0 1 26.986667-26.986667l226.282666-75.413333a42.666667 42.666667 0 0 1 43.648 10.304z m-222.72 139.306666l-41.685333 125.098667 125.077333-41.706667 41.706667-125.077333-125.077333 41.706667z">
+                </path>
+              </svg>{{ lang.ssAnalysis }}
             </div>
-            <div class="ai_refresh" :class="{'disabled': currentAnalysis && currentAnalysis.loading}" @click="aiAnalysisRefresh15()">
+            <div class="ai_refresh" :class="{ 'disabled': currentAnalysis && currentAnalysis.loading }"
+              @click="aiAnalysisRefresh15()">
               {{ lang.ssAIGenerate }}
-             <svg viewBox="0 0 1024 1024" width="200" height="200"><path d="M875 483c-33.4 0-60.5 27.1-60.5 60.5v0.1C814.4 710.3 678.8 846 512 846S209.5 710.3 209.5 543.5 345.2 241 512 241c36.8 0 71.7 7.6 104.4 19.7-32 3-57.4 29.1-57.4 61.9 0 34.8 28.2 63 63 63h201.9c34.8 0 63-28.2 63-63V120c0-34.8-28.2-63-63-63s-63 28.2-63 63v81.4C691 150.5 605.2 120 512 120 278.1 120 88.5 309.6 88.5 543.5S278.1 967 512 967s423.5-189.6 423.5-423.5c0-33.4-27.1-60.5-60.5-60.5z"></path></svg>
+              <svg viewBox="0 0 1024 1024" width="200" height="200">
+                <path
+                  d="M875 483c-33.4 0-60.5 27.1-60.5 60.5v0.1C814.4 710.3 678.8 846 512 846S209.5 710.3 209.5 543.5 345.2 241 512 241c36.8 0 71.7 7.6 104.4 19.7-32 3-57.4 29.1-57.4 61.9 0 34.8 28.2 63 63 63h201.9c34.8 0 63-28.2 63-63V120c0-34.8-28.2-63-63-63s-63 28.2-63 63v81.4C691 150.5 605.2 120 512 120 278.1 120 88.5 309.6 88.5 543.5S278.1 967 512 967s423.5-189.6 423.5-423.5c0-33.4-27.1-60.5-60.5-60.5z">
+                </path>
+              </svg>
 
             </div>
           </div>
           <div class="ai_content" v-if="currentAnalysis">
             {{ currentAnalysis.json }}
           </div>
-          <div class="ai_updateTime" v-if="currentAnalysis">{{ lang.ssUpdateTime }}:{{ currentAnalysis.update_at }}</div>
+          <div class="ai_updateTime" v-if="currentAnalysis">{{ lang.ssUpdateTime }}:{{ currentAnalysis.update_at }}
+          </div>
         </div>
 
 
@@ -142,6 +171,36 @@
           </div>
         </div>
 
+        <div class="aiAnalysis" style="margin-top:1rem ;" v-show="!lookWorkData" v-if="processedWorkArray.length > 0">
+          <div class="ai_header">
+            <div class="ai_title">
+              <svg viewBox="0 0 1024 1024" width="200" height="200">
+                <path
+                  d="M512 170.666667C323.477333 170.666667 170.666667 323.477333 170.666667 512s152.810667 341.333333 341.333333 341.333333 341.333333-152.810667 341.333333-341.333333S700.522667 170.666667 512 170.666667zM85.333333 512C85.333333 276.352 276.352 85.333333 512 85.333333s426.666667 191.018667 426.666667 426.666667-191.018667 426.666667-426.666667 426.666667S85.333333 747.648 85.333333 512z">
+                </path>
+                <path
+                  d="M693.013333 330.986667a42.666667 42.666667 0 0 1 10.304 43.648l-75.413333 226.282666a42.666667 42.666667 0 0 1-26.986667 26.986667l-226.282666 75.413333a42.666667 42.666667 0 0 1-53.973334-53.973333l75.434667-226.261333a42.666667 42.666667 0 0 1 26.986667-26.986667l226.282666-75.413333a42.666667 42.666667 0 0 1 43.648 10.304z m-222.72 139.306666l-41.685333 125.098667 125.077333-41.706667 41.706667-125.077333-125.077333 41.706667z">
+                </path>
+              </svg>{{ lang.ssAnalysis }}
+            </div>
+            <div class="ai_refresh" :class="{ 'disabled': currentAnalysis && currentAnalysis.loading }"
+              @click="aiAnalysisRefresh72()">
+              {{ lang.ssAIGenerate }}
+              <svg viewBox="0 0 1024 1024" width="200" height="200">
+                <path
+                  d="M875 483c-33.4 0-60.5 27.1-60.5 60.5v0.1C814.4 710.3 678.8 846 512 846S209.5 710.3 209.5 543.5 345.2 241 512 241c36.8 0 71.7 7.6 104.4 19.7-32 3-57.4 29.1-57.4 61.9 0 34.8 28.2 63 63 63h201.9c34.8 0 63-28.2 63-63V120c0-34.8-28.2-63-63-63s-63 28.2-63 63v81.4C691 150.5 605.2 120 512 120 278.1 120 88.5 309.6 88.5 543.5S278.1 967 512 967s423.5-189.6 423.5-423.5c0-33.4-27.1-60.5-60.5-60.5z">
+                </path>
+              </svg>
+
+            </div>
+          </div>
+          <div class="ai_content" v-if="currentAnalysis">
+            {{ currentAnalysis.json }}
+          </div>
+          <div class="ai_updateTime" v-if="currentAnalysis">{{ lang.ssUpdateTime }}:{{ currentAnalysis.update_at }}
+          </div>
+        </div>
+
         <div class="c_t72_workDetail" v-if="lookWorkData">
           <div class="c_t72_wd_top">
             <img src="../../../assets/img/arrow_left.png" @click="lookWork('')" />
@@ -242,6 +301,7 @@ const props = defineProps<{
   workArray: any[];
   courseDetail: any;
   userId: string;
+  workId: string;
 }>()
 
 const emit = defineEmits<{
@@ -501,7 +561,7 @@ const setEchartsArea1 = () => {
           fontSize: 17,
           lineHeight: 20,
           interval: 0,
-          formatter: function(value: any, idx: number) {
+          formatter: function (value: any, idx: number) {
             // 如果是字符串且格式为JSON(图片),则解析处理
             if (typeof value === 'string') {
               try {
@@ -645,11 +705,12 @@ const setEchartsArea1 = () => {
 
 // 获取分析
 const getAnalysis = () => {
-  if (!props.showData || !props.showData.workDetail || !props.showData.workDetail.id) {
+  if (!props.workId) {
     return
   }
+  console.log('props.workId', props.workId)
   const params = {
-    pid: props.showData.workDetail.id,
+    pid: props.workId,
   }
   axios.get('https://pbl.cocorobo.cn/api/pbl/select_pptAnalysisByPid?pid=' + params.pid).then(res => {
     const data = res[0]
@@ -700,6 +761,8 @@ watch(
   { immediate: true, deep: true }
 )
 
+
+
 // 监听作业变化
 watch(
   () => props.showData?.choiceQuestionListData,
@@ -776,7 +839,7 @@ watch(
 
 // 查看未提交学生
 const viewUnsubmittedStudents = () => {
-  selectUserDialogRef.value.open(lang.ssUnsubmittedStudents, {user: props.showData.unsubmittedStudents.map((item: any) => item.name)})
+  selectUserDialogRef.value.open(lang.ssUnsubmittedStudents, { user: props.showData.unsubmittedStudents.map((item: any) => item.name) })
   // if (props.unsubmittedStudents.length > 0) {
   // unsubmittedStudentsDialogRef.value.open(props.unsubmittedStudents)
   // }
@@ -817,7 +880,7 @@ const aiAnalysisRefresh45 = () => {
 
   if (!currentAnalysis.value) {
     aiAnalysisData.value.push({
-      pid: props.showData.workDetail.id,
+      pid: props.workId,
       index: props.showData.workIndex,
       loading: true,
       json: '',
@@ -827,33 +890,33 @@ const aiAnalysisRefresh45 = () => {
     })
   }
   else {
-    aiAnalysisData.value.find((item:any) => {
-      return item.pid === props.showData.workDetail.id && item.index === props.showData.workIndex
+    aiAnalysisData.value.find((item: any) => {
+      return item.pid === props.workId && item.index === props.showData.workIndex
     }).loading = true
-    aiAnalysisData.value.find((item:any) => {
-      return item.pid === props.showData.workDetail.id && item.index === props.showData.workIndex
+    aiAnalysisData.value.find((item: any) => {
+      return item.pid === props.workId && item.index === props.showData.workIndex
     }).json = ''
   }
 
   chat_stream(msg, 'a7741704-ba56-40b7-a6b8-62a423ef9376', props.userId, lang.lang, (event) => {
-    if (event.type === 'message') { 
-      aiAnalysisData.value.find((item:any) => {
-        return item.pid === props.showData.workDetail.id && item.index === props.showData.workIndex
+    if (event.type === 'message') {
+      aiAnalysisData.value.find((item: any) => {
+        return item.pid === props.workId && item.index === props.showData.workIndex
       }).json = event.data
 
-      aiAnalysisData.value.find((item:any) => {
-        return item.pid === props.showData.workDetail.id && item.index === props.showData.workIndex
+      aiAnalysisData.value.find((item: any) => {
+        return item.pid === props.workId && item.index === props.showData.workIndex
       }).loading = false
     }
     else if (event.type === 'messageEnd') {
-      aiAnalysisData.value.find((item:any) => {
-        return item.pid === props.showData.workDetail.id && item.index === props.showData.workIndex
+      aiAnalysisData.value.find((item: any) => {
+        return item.pid === props.workId && item.index === props.showData.workIndex
       }).json = event.data
-      aiAnalysisData.value.find((item:any) => {
-        return item.pid === props.showData.workDetail.id && item.index === props.showData.workIndex
+      aiAnalysisData.value.find((item: any) => {
+        return item.pid === props.workId && item.index === props.showData.workIndex
       }).noEnd = false
-      aiAnalysisData.value.find((item:any) => {
-        return item.pid === props.showData.workDetail.id && item.index === props.showData.workIndex
+      aiAnalysisData.value.find((item: any) => {
+        return item.pid === props.workId && item.index === props.showData.workIndex
       }).update_at = new Date().toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }).replace(/\//g, '-')
       saveAnalysis()
     }
@@ -876,7 +939,7 @@ const aiAnalysisRefresh15 = () => {
 - 课程学科:${props.courseDetail.name}
 当前页面答题数据(问答题):【分析重点】
 - 问答题题目:${props.showData.workDetail.json.answerQ}
-- 回答数据:${JSON.stringify(processedWorkArray.value.map((i) => ({user: i.name, answer: i.content.answer})))}
+- 回答数据:${JSON.stringify(processedWorkArray.value.map((i) => ({ user: i.name, answer: i.content.answer })))}
 - 未提交学生:${JSON.stringify(props.showData.unsubmittedStudents.map((item: any) => item.name))}
 
 # ANALYSIS RULES #
@@ -891,10 +954,104 @@ const aiAnalysisRefresh15 = () => {
 
 # EXAMPLES #
 样例:
-选择题正确率62%,核心概念“机器学习三步骤”(输入数据→训练模型→预测结果)掌握尚可,但“训练与预测区分”混淆率达38%;建议强化训练vs预测对比教学。`
+30人中15人提交问答题(参与率50%),核心概念“同理心地图”掌握薄弱。发现学生13在智能体对话中6次答“不知道”,需单独辅导设计思维基础概念。建议课程增加POV框架实例演练,强化痛点识别能力训练。`
+  if (!currentAnalysis.value) {
+    aiAnalysisData.value.push({
+      pid: props.workId,
+      index: props.showData.workIndex,
+      loading: true,
+      json: '',
+      noEnd: true,
+      update_at: '',
+      create_at: '',
+    })
+  }
+  else {
+    aiAnalysisData.value.find((item: any) => {
+      return item.pid === props.workId && item.index === props.showData.workIndex
+    }).loading = true
+    aiAnalysisData.value.find((item: any) => {
+      return item.pid === props.workId && item.index === props.showData.workIndex
+    }).json = ''
+  }
+
+  chat_stream(msg, 'a7741704-ba56-40b7-a6b8-62a423ef9376', props.userId, lang.lang, (event) => {
+    if (event.type === 'message') {
+      aiAnalysisData.value.find((item: any) => {
+        return item.pid === props.workId && item.index === props.showData.workIndex
+      }).json = event.data
+
+      aiAnalysisData.value.find((item: any) => {
+        return item.pid === props.workId && item.index === props.showData.workIndex
+      }).loading = false
+    }
+    else if (event.type === 'messageEnd') {
+      aiAnalysisData.value.find((item: any) => {
+        return item.pid === props.workId && item.index === props.showData.workIndex
+      }).json = event.data
+      aiAnalysisData.value.find((item: any) => {
+        return item.pid === props.workId && item.index === props.showData.workIndex
+      }).noEnd = false
+      aiAnalysisData.value.find((item: any) => {
+        return item.pid === props.workId && item.index === props.showData.workIndex
+      }).update_at = new Date().toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }).replace(/\//g, '-')
+      saveAnalysis()
+    }
+  }).catch(err => {
+    console.log('err', err)
+  })
+}
+
+// ai应用
+const aiAnalysisRefresh72 = async () => {
+
+  let chatMsg = ``
+
+  processedWorkArray.value.forEach((i) => {
+    i.content.forEach(j => {
+      j.messages.forEach((a) => {
+        chatMsg += `\n${a.sender}:
+${a.content}\n`
+      })
+    })
+
+  })
+
+
+
+// - 未提交学生:${JSON.stringify(props.showData.unsubmittedStudents.map((item: any) => item.name))}
+  const msg = `# CONTEXT #
+你是K-12阶段的AI教育课堂分析助手,基于上传的课件、逐字稿,以及当页的学生答题数据(选择题/问答题/智能体对话)进行智能分析。
+
+# OBJECTIVE #
+输出当前学生答题的分析报告:整体表现、核心发现(共性问题/误区)、教学优化(改进方向),为教师提供教学策略的建议和支持。
+
+#INPUT#
+课程数据:
+- 课程名称:${props.courseDetail.title}
+- 课程学科:${props.courseDetail.name}
+当前页面答题数据(问答题):【分析重点】
+- AI应用
+- 对话数据:${chatMsg}
+
+
+# ANALYSIS RULES #
+1. **问题定位**:明确指出哪个知识点/概念/步骤掌握不佳
+2. **建议具体**:给出可执行的教学动作(如“增加XX实例演练”“补充XX对比图”)
+
+# RESPONSE #
+采用段落叙述,数据量化呈现,突出可操作建议,严格控制输出为80词内。采用三段式论述:
+1. 整体表现-1句话
+2. 核心发现-1-2句,指出共性问题+典型案例
+3. 改进建议-1句话,提出具体教学动作
+
+# EXAMPLES #
+样例:
+智能体对话显示学生对“模型训练”概念模糊,多次询问“为什么不能直接告诉机器答案”。针对概念混淆学生,补充“人类学习类比”相关解释,巩固“从数据中学习规律”核心认知。`
+console.log("cs",msg)
   if (!currentAnalysis.value) {
     aiAnalysisData.value.push({
-      pid: props.showData.workDetail.id,
+      pid: props.workId,
       index: props.showData.workIndex,
       loading: true,
       json: '',
@@ -904,33 +1061,33 @@ const aiAnalysisRefresh15 = () => {
     })
   }
   else {
-    aiAnalysisData.value.find((item:any) => {
-      return item.pid === props.showData.workDetail.id && item.index === props.showData.workIndex
+    aiAnalysisData.value.find((item: any) => {
+      return item.pid === props.workId && item.index === props.showData.workIndex
     }).loading = true
-    aiAnalysisData.value.find((item:any) => {
-      return item.pid === props.showData.workDetail.id && item.index === props.showData.workIndex
+    aiAnalysisData.value.find((item: any) => {
+      return item.pid === props.workId && item.index === props.showData.workIndex
     }).json = ''
   }
 
   chat_stream(msg, 'a7741704-ba56-40b7-a6b8-62a423ef9376', props.userId, lang.lang, (event) => {
-    if (event.type === 'message') { 
-      aiAnalysisData.value.find((item:any) => {
-        return item.pid === props.showData.workDetail.id && item.index === props.showData.workIndex
+    if (event.type === 'message') {
+      aiAnalysisData.value.find((item: any) => {
+        return item.pid === props.workId && item.index === props.showData.workIndex
       }).json = event.data
 
-      aiAnalysisData.value.find((item:any) => {
-        return item.pid === props.showData.workDetail.id && item.index === props.showData.workIndex
+      aiAnalysisData.value.find((item: any) => {
+        return item.pid === props.workId && item.index === props.showData.workIndex
       }).loading = false
     }
     else if (event.type === 'messageEnd') {
-      aiAnalysisData.value.find((item:any) => {
-        return item.pid === props.showData.workDetail.id && item.index === props.showData.workIndex
+      aiAnalysisData.value.find((item: any) => {
+        return item.pid === props.workId && item.index === props.showData.workIndex
       }).json = event.data
-      aiAnalysisData.value.find((item:any) => {
-        return item.pid === props.showData.workDetail.id && item.index === props.showData.workIndex
+      aiAnalysisData.value.find((item: any) => {
+        return item.pid === props.workId && item.index === props.showData.workIndex
       }).noEnd = false
-      aiAnalysisData.value.find((item:any) => {
-        return item.pid === props.showData.workDetail.id && item.index === props.showData.workIndex
+      aiAnalysisData.value.find((item: any) => {
+        return item.pid === props.workId && item.index === props.showData.workIndex
       }).update_at = new Date().toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }).replace(/\//g, '-')
       saveAnalysis()
     }
@@ -942,8 +1099,8 @@ const aiAnalysisRefresh15 = () => {
 
 // 当前分析
 const currentAnalysis = computed(() => {
-  return aiAnalysisData.value.find((item:any) => {
-    return item.pid === props.showData.workDetail.id && item.index === props.showData.workIndex
+  return aiAnalysisData.value.find((item: any) => {
+    return item.pid === props.workId && item.index === props.showData.workIndex
   })
 })
 
@@ -953,7 +1110,7 @@ const saveAnalysis = () => {
     return
   }
   const params = [{
-    pid: props.showData.workDetail.id,
+    pid: props.workId,
     idx: props.showData.workIndex,
     json: currentAnalysis.value.json,
   }]
@@ -970,7 +1127,7 @@ const saveAnalysis = () => {
 
 // 监听 props.showData.workDetail.id 变化
 watch(
-  () => props.showData?.workDetail?.id,
+  () => props.showData?.workDetail,
   (newId, oldId) => {
     if (newId && newId !== oldId) {
       getAnalysis()
@@ -1077,13 +1234,14 @@ onUnmounted(() => {
           color: #CCCCCC;
         }
 
-        .c_t45_msg{
+        .c_t45_msg {
           display: flex;
           align-items: center;
           font-size: .9rem;
           font-weight: 400;
           gap: 1rem;
-          &>span{
+
+          &>span {
             text-decoration: underline;
             cursor: pointer;
           }
@@ -1127,6 +1285,7 @@ onUnmounted(() => {
         align-items: center;
         gap: 1.5rem;
         margin: 1rem auto;
+
         &>div {
           padding: .6rem;
           border-radius: .5rem;
@@ -1686,70 +1845,80 @@ onUnmounted(() => {
   }
 }
 
-      .aiAnalysis{
-        width: 100%;
-        height: auto;
-        display: flex;
-        flex-direction: column;
-        padding: 1rem;
-        border: solid 1px #F6C82B;
-        border-left-width: 4px;
-        border-radius: 1rem;
-        gap: 1rem;
-        &>.ai_header{
-          display: flex;
-          align-items: center;
-          justify-content: space-between;
-          gap: 1rem;
-          &>div{
-            display: flex;
-            align-items: center;
-            gap: .5rem;
-            &>svg{
-              width: 1rem;
-              height: 1rem;
-              
-            }
-          }
-          &>.ai_title{
-            color: #F7CD49;
-            font-weight: 500;
-            &>svg{
-              fill: #F7CD49;
-              width: 1.2rem;
-              height: 1.2rem;
-            }
-          }
-          &>.ai_refresh{
-            padding: .5rem 1rem;
-            border-radius: .5rem;
-            background: #F6C82B;
-            display: flex;
-            justify-content: center;
-            align-items: center;
-            cursor: pointer;
-            color: #000;
-            font-weight: bold;
-            font-size: .8rem;
-            gap: .5rem;
-            &>svg{
-              fill: #000;
-              width: .8rem;
-              height: .8rem;
-            }
-            &.disabled{
-              cursor: not-allowed !important;
-              background: #FEF8E9 !important;
-            }
-          }
-        }
-        &>.ai_content{
-          font-size: 1rem;
-          font-weight: 500;
-        }
-        &>.ai_updateTime{
-          font-size: .8rem;
-           font-weight: 500;
-        }
+.aiAnalysis {
+  width: 100%;
+  height: auto;
+  display: flex;
+  flex-direction: column;
+  padding: 1rem;
+  border: solid 1px #F6C82B;
+  border-left-width: 4px;
+  border-radius: 1rem;
+  gap: 1rem;
+
+  &>.ai_header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    gap: 1rem;
+
+    &>div {
+      display: flex;
+      align-items: center;
+      gap: .5rem;
+
+      &>svg {
+        width: 1rem;
+        height: 1rem;
+
       }
+    }
+
+    &>.ai_title {
+      color: #F7CD49;
+      font-weight: 500;
+
+      &>svg {
+        fill: #F7CD49;
+        width: 1.2rem;
+        height: 1.2rem;
+      }
+    }
+
+    &>.ai_refresh {
+      padding: .5rem 1rem;
+      border-radius: .5rem;
+      background: #F6C82B;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      cursor: pointer;
+      color: #000;
+      font-weight: bold;
+      font-size: .8rem;
+      gap: .5rem;
+
+      &>svg {
+        fill: #000;
+        width: .8rem;
+        height: .8rem;
+      }
+
+      &.disabled {
+        cursor: not-allowed !important;
+        background: #FEF8E9 !important;
+      }
+    }
+  }
+
+  &>.ai_content {
+    font-size: 1rem;
+    font-weight: 500;
+  }
+
+  &>.ai_updateTime {
+    font-size: .8rem;
+    font-weight: 500;
+  }
+}
 </style>

+ 1 - 1
src/views/Student/index.vue

@@ -106,7 +106,7 @@
           <ScreenSlideList :style="{ width: isFullscreen ? '100%' : slideWidth2 * canvasScale + 'px', height: isFullscreen ? '100%' : slideHeight2 * canvasScale + 'px', margin: '0 auto' }" :slideWidth="isFullscreen ? slideWidth * canvasScale : slideWidth2 * canvasScale" :slideHeight="isFullscreen ? slideHeight * canvasScale : slideHeight2 * canvasScale"
             :animationIndex="0" :turnSlideToId="() => { }" :manualExitFullscreen="() => { }"  :slideIndex="slideIndex" v-show="!choiceQuestionDetailDialogOpenList.includes(slideIndex)"/>
 
-          <choiceQuestionDetailDialog v-if="choiceQuestionDetailDialogOpenList.includes(slideIndex)"  :userId="props.userid" :courseDetail="courseDetail" :workArray="workArray" @changeWorkIndex="changeWorkIndex" v-model:visible="choiceQuestionDetailDialogOpenList" :showData="answerTheResultRef" :slideIndex="slideIndex" :workIndex="0" :style="{ width: isFullscreen ? '100%' : slideWidth2 * canvasScale + 'px', height: isFullscreen ? '100%' : slideHeight2 * canvasScale + 'px', margin: '0 auto' }" :slideWidth="isFullscreen ? slideWidth * canvasScale : slideWidth2 * canvasScale" :slideHeight="isFullscreen ? slideHeight * canvasScale : slideHeight2 * canvasScale"/>
+          <choiceQuestionDetailDialog v-if="choiceQuestionDetailDialogOpenList.includes(slideIndex)" :workId="workId"  :userId="props.userid" :courseDetail="courseDetail" :workArray="workArray" @changeWorkIndex="changeWorkIndex" v-model:visible="choiceQuestionDetailDialogOpenList" :showData="answerTheResultRef" :slideIndex="slideIndex" :workIndex="0" :style="{ width: isFullscreen ? '100%' : slideWidth2 * canvasScale + 'px', height: isFullscreen ? '100%' : slideHeight2 * canvasScale + 'px', margin: '0 auto' }" :slideWidth="isFullscreen ? slideWidth * canvasScale : slideWidth2 * canvasScale" :slideHeight="isFullscreen ? slideHeight * canvasScale : slideHeight2 * canvasScale"/>
 
 
           <div class="slide-bottom" v-if="!isFullscreen">