黄琪勇 3 kuukautta sitten
vanhempi
commit
f5b0fa0153

+ 1 - 1
.env.development

@@ -1,5 +1,5 @@
 
-VITE_APP_URL = "/pptApi"
+VITE_APP_URL = "http://192.168.3.122:9527/pptApi"
 
 ## 云教练地址
 VITE_YJL_URL = "https://test.kt.colexiu.com/instrument"

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 724 - 52
package-lock.json


+ 2 - 2
package.json

@@ -34,7 +34,6 @@
     "number-precision": "^1.6.0",
     "pinia": "^2.1.7",
     "pptxgenjs": "^3.12.0",
-    "pptxtojson": "^1.0.3",
     "prosemirror-commands": "^1.6.0",
     "prosemirror-dropcursor": "^1.8.1",
     "prosemirror-gapcursor": "^1.3.2",
@@ -52,7 +51,8 @@
     "tippy.js": "^6.3.7",
     "vue": "^3.4.34",
     "vue-router": "^4.0.3",
-    "vuedraggable": "^4.1.0"
+    "vuedraggable": "^4.1.0",
+    "workerize-loader": "^2.0.2"
   },
   "devDependencies": {
     "@rushstack/eslint-patch": "^1.3.3",

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
public/pptworker/cos-js-sdk-v5.min.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 9083 - 0
public/pptworker/pptJson.js


+ 106 - 0
public/pptworker/upload.js

@@ -0,0 +1,106 @@
+const tencentBucket = "daya-online-1303457149"
+const ossType = "tencent"
+function fileUpload({ host, token }, { fileName, file }) {
+  // 上传名称加上时间戳
+  fileName = addTimestampBeforeExtension(fileName)
+  return new Promise((res, rej) => {
+    getUploadSign(fileName, host, token)
+      .then(resUploadSign => {
+        if (resUploadSign.code === 200) {
+          try {
+            onOnlyFileUpload(resUploadSign.data.signature, {
+              fileName,
+              file
+            })
+              .then(resUpload => {
+                if (resUpload.statusCode === 200) {
+                  res(resUpload.Location.includes("http") ? resUpload.Location : `https://${resUpload.Location}`)
+                } else {
+                  rej("")
+                }
+              })
+              .catch(() => {
+                rej("")
+              })
+          } catch {
+            rej("")
+          }
+        } else {
+          rej("")
+        }
+      })
+      .catch(() => {
+        rej("")
+      })
+  })
+}
+
+const getUploadSign = (fileName, host, token) => {
+  const fileUrl = "pptList/" + fileName
+  return ajaxRequest(
+    `${host}/edu-app/open/getUploadSign?pluginName=${ossType}`,
+    "POST",
+    {
+      "Content-Type": "application/json",
+      Authorization: token
+    },
+    {
+      postData: {
+        key: fileUrl
+      },
+      pluginName: ossType,
+      bucketName: tencentBucket,
+      filename: fileUrl
+    }
+  )
+}
+
+const onOnlyFileUpload = (signature, params) => {
+  const cos = new COS({
+    Domain: "https://oss.dayaedu.com",
+    Protocol: "https",
+    getAuthorization: async (options, callback) => {
+      callback({ Authorization: signature })
+    }
+  })
+  return cos.uploadFile({
+    Bucket: tencentBucket /* 填写自己的 bucket,必须字段 */,
+    Region: "ap-nanjing" /* 存储桶所在地域,必须字段 */,
+    Key: `pptList/${params.fileName}`,
+    /* 存储在桶里的对象键(例如:1.jpg,a/b/test.txt,图片.jpg)支持中文,必须字段 */
+    Body: params.file, // 上传文件对象
+    SliceSize: 1024 * 1024 * 500 /* 触发分块上传的阈值,超过5MB使用分块上传,小于5MB使用简单上传。可自行设置,非必须 */,
+    onProgress: function (progressData) {
+      // onProgress({ percent: Math.ceil((progressData.percent || 0) * 100) })
+    }
+  })
+}
+
+function ajaxRequest(url, method = "GET", headers = {}, body = null) {
+  return new Promise((resolve, reject) => {
+    const xhr = new XMLHttpRequest()
+    xhr.open(method, url, true)
+    // 设置请求头
+    for (const [key, value] of Object.entries(headers)) {
+      xhr.setRequestHeader(key, value)
+    }
+    xhr.onload = function () {
+      if (xhr.status >= 200 && xhr.status < 300) {
+        resolve(JSON.parse(xhr.responseText))
+      } else {
+        reject(new Error(`HTTP error! status: ${xhr.status}`))
+      }
+    }
+    xhr.onerror = function () {
+      reject(new Error("Network error occurred"))
+    }
+    xhr.send(method === "GET" ? null : JSON.stringify(body))
+  })
+}
+
+function addTimestampBeforeExtension(filename) {
+  const dotIndex = filename.lastIndexOf(".")
+  const name = filename.substring(0, dotIndex)
+  const extension = filename.substring(dotIndex)
+  return `${name}_${Date.now()}${extension}`
+}

+ 44 - 414
src/hooks/useImport.ts

@@ -1,11 +1,9 @@
-import { ref } from "vue"
-import { storeToRefs } from "pinia"
-import { parse, type Shape, type Element, type ChartItem } from "pptxtojson"
-import { nanoid } from "nanoid"
+import { ref, onMounted } from "vue"
 import { useSlidesStore } from "@/store"
-import { type ShapePoolItem, SHAPE_LIST, SHAPE_PATH_FORMULAS } from "@/configs/shapes"
 import message from "@/utils/message"
 import { getSvgPathRange } from "@/utils/svgPathParser"
+import axios from "axios"
+
 import type {
   Slide,
   TableCellStyle,
@@ -18,432 +16,64 @@ import type {
   PPTTextElement,
   ChartOptions
 } from "@/types/slides"
-
-const convertFontSizePtToPx = (html: string, ratio: number) => {
-  return html.replace(/font-size:\s*([\d.]+)pt/g, (match, p1) => {
-    return `font-size: ${(parseFloat(p1) * ratio).toFixed(1)}px`
-  })
-}
+import { getHttpJson } from "@/libs/jsonTool"
+import { initWorker, addCourseWareTask, getRunJobIds } from "@/worker/useCoursewaresWorker"
 
 export default () => {
   const slidesStore = useSlidesStore()
-  const { theme } = storeToRefs(useSlidesStore())
-
   const exporting = ref(false)
-
-  const parseLineElement = (el: Shape) => {
-    let start: [number, number] = [0, 0]
-    let end: [number, number] = [0, 0]
-
-    if (!el.isFlipV && !el.isFlipH) {
-      // 右下
-      start = [0, 0]
-      end = [el.width, el.height]
-    } else if (el.isFlipV && el.isFlipH) {
-      // 左上
-      start = [el.width, el.height]
-      end = [0, 0]
-    } else if (el.isFlipV && !el.isFlipH) {
-      // 右上
-      start = [0, el.height]
-      end = [el.width, 0]
-    } else {
-      // 左下
-      start = [el.width, 0]
-      end = [0, el.height]
-    }
-
-    const data: PPTLineElement = {
-      type: "line",
-      id: nanoid(10),
-      width: el.borderWidth || 1,
-      left: el.left,
-      top: el.top,
-      start,
-      end,
-      style: el.borderType,
-      color: el.borderColor,
-      points: ["", /straightConnector/.test(el.shapType) ? "arrow" : ""]
-    }
-    if (/bentConnector/.test(el.shapType)) {
-      data.broken2 = [Math.abs(start[0] - end[0]) / 2, Math.abs(start[1] - end[1]) / 2]
-    }
-
-    return data
-  }
-
+  onMounted(() => {
+    initWorker()
+  })
   // 导入PPTX文件
   const importPPTXFile = (files: FileList) => {
     const file = files[0]
     if (!file) return
-
     exporting.value = true
-
-    const shapeList: ShapePoolItem[] = []
-    for (const item of SHAPE_LIST) {
-      shapeList.push(...item.children)
-    }
-
-    const reader = new FileReader()
-    reader.onload = async e => {
-      const json = await parse(e.target!.result as ArrayBuffer)
-
-      const ratio = 96 / 72
-      const width = json.size.width
-
-      slidesStore.setViewportSize(width * ratio)
-
-      const slides: Slide[] = []
-      for (const item of json.slides) {
-        const { type, value } = item.fill
-        let background: SlideBackground
-        if (type === "image") {
-          background = {
-            type: "image",
-            image: {
-              src: value.picBase64,
-              size: "cover"
-            }
-          }
-        } else if (type === "gradient") {
-          background = {
-            type: "gradient",
-            gradient: {
-              type: "linear",
-              colors: value.colors.map(item => ({
-                ...item,
-                pos: parseInt(item.pos)
-              })),
-              rotate: value.rot
-            }
-          }
-        } else {
-          background = {
-            type: "solid",
-            color: value
-          }
+    const suffix = `.${file.name.split(".")?.reverse()[0]}`
+    addCourseWareTask(
+      {
+        type: "uploadCourseware",
+        extra: {
+          outputName: file.name.trim(),
+          name: file.name.replace(suffix, "").trim(),
+          type: file.type,
+          suffix,
+          path: file.webkitRelativePath,
+          paths: file.webkitRelativePath.split("/"),
+          files: file
         }
-
-        const slide: Slide = {
-          id: nanoid(10),
-          elements: [],
-          background
-        }
-
-        const parseElements = (elements: Element[]) => {
-          for (const el of elements) {
-            const originWidth = el.width || 1
-            const originHeight = el.height || 1
-            const originLeft = el.left
-            const originTop = el.top
-
-            el.width = el.width * ratio
-            el.height = el.height * ratio
-            el.left = el.left * ratio
-            el.top = el.top * ratio
-
-            if (el.type === "text") {
-              const textEl: PPTTextElement = {
-                type: "text",
-                id: nanoid(10),
-                width: el.width,
-                height: el.height,
-                left: el.left,
-                top: el.top,
-                rotate: el.rotate,
-                defaultFontName: theme.value.fontName,
-                defaultColor: theme.value.fontColor,
-                content: convertFontSizePtToPx(el.content, ratio),
-                lineHeight: 1,
-                outline: {
-                  color: el.borderColor,
-                  width: el.borderWidth,
-                  style: el.borderType
-                },
-                fill: el.fillColor,
-                vertical: el.isVertical
-              }
-              if (el.shadow) {
-                textEl.shadow = {
-                  h: el.shadow.h * ratio,
-                  v: el.shadow.v * ratio,
-                  blur: el.shadow.blur * ratio,
-                  color: el.shadow.color
-                }
-              }
-              slide.elements.push(textEl)
-            } else if (el.type === "image") {
-              slide.elements.push({
-                type: "image",
-                id: nanoid(10),
-                src: el.src,
-                width: el.width,
-                height: el.height,
-                left: el.left,
-                top: el.top,
-                fixedRatio: true,
-                rotate: el.rotate,
-                flipH: el.isFlipH,
-                flipV: el.isFlipV
-              })
-            } else if (el.type === "audio") {
-              slide.elements.push({
-                type: "audio",
-                id: nanoid(10),
-                src: el.blob,
-                width: el.width,
-                height: el.height,
-                left: el.left,
-                top: el.top,
-                rotate: 0,
-                fixedRatio: false,
-                color: theme.value.themeColor,
-                loop: false,
-                autoplay: false
-              })
-            } else if (el.type === "video") {
-              slide.elements.push({
-                type: "video",
-                id: nanoid(10),
-                src: (el.blob || el.src)!,
-                width: el.width,
-                height: el.height,
-                left: el.left,
-                top: el.top,
-                rotate: 0,
-                autoplay: false
-              })
-            } else if (el.type === "shape") {
-              if (el.shapType === "line" || /Connector/.test(el.shapType)) {
-                const lineElement = parseLineElement(el)
-                slide.elements.push(lineElement)
-              } else {
-                const shape = shapeList.find(item => item.pptxShapeType === el.shapType)
-
-                const vAlignMap: { [key: string]: ShapeTextAlign } = {
-                  mid: "middle",
-                  down: "bottom",
-                  up: "top"
-                }
-
-                const element: PPTShapeElement = {
-                  type: "shape",
-                  id: nanoid(10),
-                  width: el.width,
-                  height: el.height,
-                  left: el.left,
-                  top: el.top,
-                  viewBox: [200, 200],
-                  path: "M 0 0 L 200 0 L 200 200 L 0 200 Z",
-                  fill: el.fillColor || "none",
-                  fixedRatio: false,
-                  rotate: el.rotate,
-                  outline: {
-                    color: el.borderColor,
-                    width: el.borderWidth,
-                    style: el.borderType
-                  },
-                  text: {
-                    content: convertFontSizePtToPx(el.content, ratio),
-                    defaultFontName: theme.value.fontName,
-                    defaultColor: theme.value.fontColor,
-                    align: vAlignMap[el.vAlign] || "middle"
-                  },
-                  flipH: el.isFlipH,
-                  flipV: el.isFlipV
-                }
-                if (el.shadow) {
-                  element.shadow = {
-                    h: el.shadow.h * ratio,
-                    v: el.shadow.v * ratio,
-                    blur: el.shadow.blur * ratio,
-                    color: el.shadow.color
-                  }
-                }
-
-                if (shape) {
-                  element.path = shape.path
-                  element.viewBox = shape.viewBox
-
-                  if (shape.pathFormula) {
-                    element.pathFormula = shape.pathFormula
-                    element.viewBox = [el.width, el.height]
-
-                    const pathFormula = SHAPE_PATH_FORMULAS[shape.pathFormula]
-                    if ("editable" in pathFormula && pathFormula.editable) {
-                      element.path = pathFormula.formula(el.width, el.height, pathFormula.defaultValue)
-                      element.keypoints = pathFormula.defaultValue
-                    } else element.path = pathFormula.formula(el.width, el.height)
-                  }
-                }
-                if (el.shapType === "custom") {
-                  if (el.path!.indexOf("NaN") !== -1) element.path = ""
-                  else {
-                    element.special = true
-                    element.path = el.path!
-
-                    const { maxX, maxY } = getSvgPathRange(element.path)
-                    element.viewBox = [maxX || originWidth, maxY || originHeight]
-                  }
-                }
-
-                if (element.path) slide.elements.push(element)
-              }
-            } else if (el.type === "table") {
-              const row = el.data.length
-              const col = el.data[0].length
-
-              const style: TableCellStyle = {
-                fontname: theme.value.fontName,
-                color: theme.value.fontColor
-              }
-              const data: TableCell[][] = []
-              for (let i = 0; i < row; i++) {
-                const rowCells: TableCell[] = []
-                for (let j = 0; j < col; j++) {
-                  const cellData = el.data[i][j]
-
-                  let textDiv: HTMLDivElement | null = document.createElement("div")
-                  textDiv.innerHTML = cellData.text
-                  const p = textDiv.querySelector("p")
-                  const align = p?.style.textAlign || "left"
-
-                  const span = textDiv.querySelector("span")
-                  const fontsize = span?.style.fontSize ? (parseInt(span?.style.fontSize) * ratio).toFixed(1) + "px" : ""
-                  const fontname = span?.style.fontFamily || ""
-                  const color = span?.style.color || cellData.fontColor
-
-                  rowCells.push({
-                    id: nanoid(10),
-                    colspan: cellData.colSpan || 1,
-                    rowspan: cellData.rowSpan || 1,
-                    text: textDiv.innerText,
-                    style: {
-                      ...style,
-                      align: ["left", "right", "center"].includes(align) ? (align as "left" | "right" | "center") : "left",
-                      fontsize,
-                      fontname,
-                      color,
-                      bold: cellData.fontBold,
-                      backcolor: cellData.fillColor
-                    }
-                  })
-                  textDiv = null
-                }
-                data.push(rowCells)
-              }
-
-              const colWidths: number[] = new Array(col).fill(1 / col)
-
-              slide.elements.push({
-                type: "table",
-                id: nanoid(10),
-                width: el.width,
-                height: el.height,
-                left: el.left,
-                top: el.top,
-                colWidths,
-                rotate: 0,
-                data,
-                outline: {
-                  width: el.borderWidth || 2,
-                  style: el.borderType,
-                  color: el.borderColor || "#eeece1"
-                },
-                cellMinHeight: 36
-              })
-            } else if (el.type === "chart") {
-              let labels: string[]
-              let legends: string[]
-              let series: number[][]
-
-              if (el.chartType === "scatterChart" || el.chartType === "bubbleChart") {
-                labels = el.data[0].map((item, index) => `坐标${index + 1}`)
-                legends = ["X", "Y"]
-                series = el.data
+      },
+      (e: any) => {
+        console.log(e)
+        if (e.progress === 100) {
+          if (e.status === "done") {
+            getHttpJson(e.extra.url).then(res => {
+              if (res.code === 200) {
+                jsonToPpt(res.data)
               } else {
-                const data = el.data as ChartItem[]
-                labels = Object.values(data[0].xlabels)
-                legends = data.map(item => item.key)
-                series = data.map(item => item.values.map(v => v.y))
+                exporting.value = false
               }
-
-              const options: ChartOptions = {}
-
-              let chartType: ChartType = "bar"
-
-              switch (el.chartType) {
-                case "barChart":
-                case "bar3DChart":
-                  chartType = "bar"
-                  if (el.barDir === "bar") chartType = "column"
-                  if (el.grouping === "stacked" || el.grouping === "percentStacked") options.stack = true
-                  break
-                case "lineChart":
-                case "line3DChart":
-                  if (el.grouping === "stacked" || el.grouping === "percentStacked") options.stack = true
-                  chartType = "line"
-                  break
-                case "areaChart":
-                case "area3DChart":
-                  if (el.grouping === "stacked" || el.grouping === "percentStacked") options.stack = true
-                  chartType = "area"
-                  break
-                case "scatterChart":
-                case "bubbleChart":
-                  chartType = "scatter"
-                  break
-                case "pieChart":
-                case "pie3DChart":
-                  chartType = "pie"
-                  break
-                case "radarChart":
-                  chartType = "radar"
-                  break
-                case "doughnutChart":
-                  chartType = "ring"
-                  break
-                default:
-              }
-
-              slide.elements.push({
-                type: "chart",
-                id: nanoid(10),
-                chartType: chartType,
-                width: el.width,
-                height: el.height,
-                left: el.left,
-                top: el.top,
-                rotate: 0,
-                themeColors: [theme.value.themeColor],
-                textColor: theme.value.fontColor,
-                data: {
-                  labels,
-                  legends,
-                  series
-                },
-                options
-              })
-            } else if (el.type === "group" || el.type === "diagram") {
-              const elements = el.elements.map(_el => ({
-                ..._el,
-                left: _el.left + originLeft,
-                top: _el.top + originTop
-              }))
-              parseElements(elements)
-            }
+            })
+          } else {
+            exporting.value = false
           }
         }
-        parseElements(item.elements)
-        slides.push(slide)
       }
-      slidesStore.updateSlideIndex(0)
-      slidesStore.setSlides(slides)
+    ).catch(err => {
       exporting.value = false
-    }
-    reader.readAsArrayBuffer(file)
+      console.log(err)
+    })
+  }
+  //处理数据专黄
+  function jsonToPpt(jsonData: Record<string, any>) {
+    const { width, theme, slides } = jsonData
+    slidesStore.updateSlideIndex(0)
+    slidesStore.setViewportSize(width || 1920)
+    slidesStore.setViewportRatio(theme.viewportRatio)
+    slidesStore.setTheme(theme)
+    slidesStore.setSlides(slides)
   }
-
   return {
     importPPTXFile,
     exporting

+ 30 - 0
src/libs/jsonTool.ts

@@ -0,0 +1,30 @@
+import axios from "axios"
+/**
+ * http 请求json
+ */
+
+export function getHttpJson(url: string): Promise<{ code: number; data: Record<string, any> }> {
+  return new Promise((res, rej) => {
+    axios
+      .get(url)
+      .then(resData => {
+        if (resData.status === 200 && typeof resData.data === "object") {
+          res({
+            code: 200,
+            data: resData.data
+          })
+        } else {
+          res({
+            code: 500,
+            data: {}
+          })
+        }
+      })
+      .catch(() => {
+        res({
+          code: 500,
+          data: {}
+        })
+      })
+  })
+}

+ 11 - 965
src/mocks/layout.ts

@@ -7,977 +7,23 @@ export const layouts: Slide[] = [
     id: "template",
     elements: [
       {
-        type: "shape",
-        id: "4cbRxp",
-        left: 0,
-        top: 200,
-        width: 546,
-        height: 362.5,
-        viewBox: [200, 200],
-        path: "M 0 0 L 0 200 L 200 200 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: false,
-        opacity: 0.7,
-        rotate: 0
-      },
-      {
-        type: "shape",
-        id: "ookHrf",
-        left: 0,
-        top: 0,
-        width: 300,
-        height: 320,
-        viewBox: [200, 200],
-        path: "M 0 0 L 0 200 L 200 200 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: false,
-        flipV: true,
-        rotate: 0
-      },
-      {
-        type: "text",
-        id: "AkIh3E",
-        left: 355,
-        top: 95.11111111111111,
-        width: 585,
-        height: 116,
-        lineHeight: 1.2,
-        content: "<p style=''><strong><span style='font-size: 80px'>输入标题</span></strong></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        wordSpace: 6
-      },
-      {
-        type: "text",
-        id: "7stmVP",
-        left: 355,
-        top: 253.25,
-        width: 585,
-        height: 56,
-        content: "<p><span style='font-size:  24px'>请在此处输入副标题</span></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}"
-      },
-      {
-        type: "line",
-        id: "FnpZs4",
-        left: 361,
-        top: 238,
-        start: [0, 0],
-        end: [549, 0],
-        points: ["", ""],
-        color: "{{themeColor}}",
-        style: "solid",
-        width: 2
-      }
-    ],
-    background: {
-      type: "solid",
-      color: "{{backgroundColor}}"
-    }
-  },
-  {
-    id: "template",
-    elements: [
-      {
-        type: "text",
-        id: "ptNnUJ",
-        left: 145,
-        top: 148,
-        width: 711,
-        height: 77,
-        lineHeight: 1.2,
-        content: "<p style='text-align: center;'><strong><span style='font-size: 48px'>在此处添加标题</span></strong></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}"
-      },
-      {
-        type: "text",
-        id: "mRHvQN",
-        left: 207.50000000000003,
-        top: 249.84259259259264,
-        width: 585,
-        height: 56,
-        content: "<p style='text-align: center;'><span style='font-size: 24px'>在此处添加副标题</span></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}"
-      },
-      {
-        type: "line",
-        id: "7CQDwc",
-        left: 323.09259259259267,
-        top: 238.33333333333334,
-        start: [0, 0],
-        end: [354.8148148148148, 0],
-        points: ["", ""],
-        color: "{{themeColor}}",
-        style: "solid",
-        width: 4
-      },
-      {
-        type: "shape",
-        id: "09wqWw",
-        left: -27.648148148148138,
-        top: 432.73148148148147,
-        width: 1056.2962962962963,
-        height: 162.96296296296296,
-        viewBox: [200, 200],
-        path: "M 0 20 C 40 -40 60 60 100 20 C 140 -40 160 60 200 20 L 200 180 C 140 240 160 140 100 180 C 40 240 60 140 0 180 L 0 20 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: false,
-        rotate: 0
-      }
-    ],
-    background: {
-      type: "solid",
-      color: "{{backgroundColor}}"
-    }
-  },
-  {
-    id: "template",
-    elements: [
-      {
-        type: "shape",
-        id: "vSheCJ",
-        left: 183.5185185185185,
-        top: 175.5092592592593,
-        width: 605.1851851851851,
-        height: 185.18518518518516,
-        viewBox: [200, 200],
-        path: "M 0 0 L 200 0 L 200 200 L 0 200 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: false,
-        rotate: 0
-      },
-      {
-        type: "shape",
-        id: "Mpwv7x",
-        left: 211.29629629629628,
-        top: 201.80555555555557,
-        width: 605.1851851851851,
-        height: 185.18518518518516,
-        viewBox: [200, 200],
-        path: "M 0 0 L 200 0 L 200 200 L 0 200 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: false,
-        rotate: 0,
-        opacity: 0.7
-      },
-      {
-        type: "text",
-        id: "WQOTAp",
-        left: 304.9074074074074,
-        top: 198.10185185185182,
-        width: 417.9629629629629,
-        height: 140,
-        content:
-          "<p style='text-align: center;'><strong><span style='color: #ffffff;'><span style='font-size: 80px'>感谢观看</span></span></strong></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        wordSpace: 5
-      }
-    ],
-    background: {
-      type: "solid",
-      color: "{{backgroundColor}}"
-    }
-  },
-  {
-    id: "MZVO1kkj",
-    elements: [
-      {
-        type: "shape",
-        id: "cql0h8",
-        left: 0,
-        top: 0,
-        width: 352.59259259259255,
-        height: 562.5,
-        viewBox: [200, 200],
-        path: "M 0 0 L 200 0 L 200 200 L 0 200 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: false,
-        rotate: 0
-      },
-      {
-        type: "shape",
-        id: "_RTaF4",
-        left: 171.4814814814814,
-        top: 100.13888888888887,
-        width: 362.22222222222223,
-        height: 362.22222222222223,
-        viewBox: [200, 200],
-        path: "M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z",
-        fill: "rgba(255,255,255,0)",
-        fixedRatio: false,
-        rotate: 0,
-        outline: {
-          width: 10,
-          color: "{{backgroundColor}}",
-          style: "solid"
-        }
-      },
-      {
-        type: "shape",
-        id: "UZfo8N",
-        left: 216.66666666666663,
-        top: 145.32407407407408,
-        width: 271.85185185185185,
-        height: 271.85185185185185,
-        viewBox: [200, 200],
-        path: "M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z",
-        fill: "{{backgroundColor}}",
-        fixedRatio: false,
-        rotate: 0,
-        text: {
-          content: "<p style='text-align: center;'><span style='font-size: 80px'>01</span></p>",
-          defaultFontName: "{{fontName}}",
-          defaultColor: "{{fontColor}}",
-          align: "middle"
-        }
-      },
-      {
-        type: "text",
-        id: "ysqtBg",
-        left: 561.4814814814814,
-        top: 100.1388888888889,
-        width: 359.25925925925924,
-        height: 80,
-        content: "<p style=''><strong><span style='font-size: 40px'>在此处输入标题</span></strong></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}"
-      },
-      {
-        type: "text",
-        id: "lXsoHa",
-        left: 572.5925925925925,
-        top: 202.3611111111111,
-        width: 257.77777777777777,
-        height: 260,
-        content:
-          "<ol><li><p style=''>在此处输入内容</p></li><li><p style=''>在此处输入内容</p></li><li><p style=''>在此处输入内容</p></li><li><p style=''>在此处输入内容</p></li><li><p style=''>在此处输入内容</p></li><li><p style=''>在此处输入内容</p></li></ol>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        lineHeight: 2,
-        fill: "{{subColor}}"
-      }
-    ],
-    background: {
-      type: "solid",
-      color: "{{backgroundColor}}"
-    }
-  },
-  {
-    id: "template",
-    elements: [
-      {
-        type: "shape",
-        id: "EBBnTr",
-        left: 360.5996472663139,
-        top: 141.8496472663139,
-        width: 278.80070546737215,
-        height: 278.80070546737215,
-        viewBox: [200, 200],
-        path: "M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: true,
-        rotate: 0,
-        outline: {
-          width: 0,
-          color: "{{backgroundColor}}",
-          style: "solid"
-        }
-      },
-      {
-        type: "shape",
-        id: "gDIWDH",
-        left: 456.4373897707231,
-        top: 98.287037037037,
-        width: 87.12522045855381,
-        height: 87.12522045855381,
-        viewBox: [200, 200],
-        path: "M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: true,
-        rotate: 0,
-        outline: {
-          width: 4,
-          color: "{{backgroundColor}}",
-          style: "solid"
-        },
-        text: {
-          content: "<p style='text-align: center;'><span style='color: #ffffff;'>1</span></p>",
-          defaultFontName: "{{fontName}}",
-          defaultColor: "{{fontColor}}",
-          align: "middle"
-        }
-      },
-      {
-        type: "shape",
-        id: "DUWT7E",
-        left: 317.037037037037,
-        top: 237.68738977072314,
-        width: 87.12522045855381,
-        height: 87.12522045855381,
-        viewBox: [200, 200],
-        path: "M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: true,
-        rotate: 0,
-        outline: {
-          width: 4,
-          color: "{{backgroundColor}}",
-          style: "solid"
-        },
-        text: {
-          content: "<p style='text-align: center;'><span style='color: #ffffff;'>4</span></p>",
-          defaultFontName: "{{fontName}}",
-          defaultColor: "{{fontColor}}",
-          align: "middle"
-        }
-      },
-      {
-        type: "shape",
-        id: "pbhn38",
-        left: 456.43738977072303,
-        top: 377.08774250440916,
-        width: 87.12522045855381,
-        height: 87.12522045855381,
-        viewBox: [200, 200],
-        path: "M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: true,
-        rotate: 0,
-        outline: {
-          width: 4,
-          color: "{{backgroundColor}}",
-          style: "solid"
-        },
-        text: {
-          content: "<p style='text-align: center;'><span style='color: #ffffff;'>3</span></p>",
-          defaultFontName: "{{fontName}}",
-          defaultColor: "{{fontColor}}",
-          align: "middle"
-        }
-      },
-      {
-        type: "shape",
-        id: "CvMKrO",
-        left: 595.8377425044091,
-        top: 237.6873897707231,
-        width: 87.12522045855381,
-        height: 87.12522045855381,
-        viewBox: [200, 200],
-        path: "M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: true,
-        rotate: 0,
-        outline: {
-          width: 4,
-          color: "{{backgroundColor}}",
-          style: "solid"
-        },
-        text: {
-          content: "<p style='text-align: center;'><span style='color: #ffffff;'>2</span></p>",
-          defaultFontName: "{{fontName}}",
-          defaultColor: "{{fontColor}}",
-          align: "middle"
-        }
-      },
-      {
-        type: "text",
-        id: "adudHB",
-        left: 402.962962962963,
-        top: 39.39814814814815,
-        width: 194.07407407407408,
-        height: 50,
-        content: "<p style='text-align: center;'>在此输入内容</p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}"
-      },
-      {
-        type: "text",
-        id: "9UpDwg",
-        left: 402.962962962963,
-        top: 473.1018518518518,
-        width: 194.07407407407408,
-        height: 50,
-        content: "<p style='text-align: center;'>在此输入内容</p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}"
-      },
-      {
-        type: "text",
-        id: "GERdpB",
-        left: 111.48148148148151,
-        top: 256.25,
-        width: 194.07407407407408,
-        height: 50,
-        content: "<p style='text-align: center;'>在此输入内容</p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}"
-      },
-      {
-        type: "text",
-        id: "G5qoho",
-        left: 691.1111111111111,
-        top: 256.25,
-        width: 194.07407407407408,
-        height: 50,
-        content: "<p style='text-align: center;'>在此输入内容</p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}"
-      },
-      {
-        type: "shape",
-        id: "vdZcI6",
-        left: 415.18518518518516,
-        top: 196.4351851851852,
-        width: 169.62962962962962,
-        height: 169.62962962962962,
-        viewBox: [200, 200],
-        path: "M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z",
-        fill: "{{backgroundColor}}",
-        fixedRatio: false,
-        rotate: 0
-      }
-    ],
-    background: {
-      type: "solid",
-      color: "{{backgroundColor}}"
-    }
-  },
-  {
-    id: "template",
-    elements: [
-      {
-        type: "shape",
-        id: "tYUmrx",
-        left: 156.66666666666683,
-        top: 149.02777777777771,
-        width: 264.4444444444445,
-        height: 264.4444444444445,
-        viewBox: [200, 200],
-        path: "M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: false,
-        rotate: 0,
-        text: {
-          content: "<p style='text-align: center;'><span style='color: #ffffff;'><span style='font-size: 60px'>01</span></span></p>",
-          defaultFontName: "{{fontName}}",
-          defaultColor: "{{fontColor}}",
-          align: "middle"
-        }
-      },
-      {
-        type: "shape",
-        id: "0GVHf8",
-        left: 342.2222222222223,
-        top: 217.17592592592587,
-        width: 128.14814814814812,
-        height: 128.14814814814812,
-        viewBox: [200, 200],
-        path: "M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z",
-        fill: "{{backgroundColor}}",
-        fixedRatio: false,
-        rotate: 0
-      },
-      {
-        type: "text",
-        id: "BO33Sv",
-        left: 378.8888888888889,
-        top: 235.24999999999994,
-        width: 464.4444444444444,
-        height: 92,
-        content: "<p style=''><strong><span style='font-size: 48px'>在此处添加标题</span></strong></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}"
-      }
-    ],
-    background: {
-      type: "solid",
-      color: "{{backgroundColor}}"
-    }
-  },
-  {
-    id: "template",
-    elements: [
-      {
-        type: "text",
-        id: "Hj7ttp",
-        left: 69.35185185185185,
-        top: 49.21759259259262,
-        width: 420,
-        height: 63,
-        lineHeight: 1.2,
-        content:
-          "<p style='text-align: center;'><strong><span style='color: #ffffff;'><span style='font-size: 36px'>1.请输入标题</span></span></strong></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{themeColor}}"
-      },
-      {
-        type: "text",
-        id: "FmKMNB",
-        left: 69.35185185185185,
-        top: 129.28240740740745,
-        width: 420,
-        height: 384,
-        content:
-          "<p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{subColor}}"
-      },
-      {
-        type: "text",
-        id: "rI7ZeO",
-        left: 510.64814814814815,
-        top: 49.21759259259262,
-        width: 420,
-        height: 63,
-        lineHeight: 1.2,
-        content:
-          "<p style='text-align: center;'><strong><span style='color: #ffffff;'><span style='font-size: 36px'>2.请输入标题</span></span></strong></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{themeColor}}"
-      },
-      {
-        type: "text",
-        id: "KspwGc",
-        left: 510.64814814814815,
-        top: 129.28240740740745,
-        width: 420,
-        height: 384,
-        content:
-          "<p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 24px'>在此处输入内容</span></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{subColor}}"
-      }
-    ],
-    background: {
-      type: "solid",
-      color: "{{backgroundColor}}"
-    }
-  },
-  {
-    id: "template",
-    elements: [
-      {
-        type: "text",
-        id: "Rx63Jo",
-        left: 69.35185185185179,
-        top: 51.71759259259262,
-        width: 420,
-        height: 58,
-        lineHeight: 1.2,
-        content:
-          "<p style='text-align: center;'><strong><span style='color: #ffffff;'><span style='font-size: 32px'>1.请输入标题</span></span></strong></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{themeColor}}"
-      },
-      {
-        type: "text",
-        id: "ulyuzE",
-        left: 69.35185185185179,
-        top: 131.78240740740745,
-        width: 420,
-        height: 129,
-        content:
-          "<p style='text-align: center;'><span style='font-size: 22px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 22px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 22px'>在此处输入内容</span></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{subColor}}"
-      },
-      {
-        type: "text",
-        id: "kr35Ca",
-        left: 510.6481481481481,
-        top: 51.71759259259262,
-        width: 420,
-        height: 58,
-        lineHeight: 1.2,
-        content:
-          "<p style='text-align: center;'><strong><span style='color: #ffffff;'><span style='font-size: 32px'>2.请输入标题</span></span></strong></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{themeColor}}"
-      },
-      {
-        type: "text",
-        id: "BNQSpC",
-        left: 510.6481481481481,
-        top: 131.78240740740745,
-        width: 420,
-        height: 129,
-        content:
-          "<p style='text-align: center;'><span style='font-size: 22px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 22px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 22px'>在此处输入内容</span></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{subColor}}"
-      },
-      {
-        type: "text",
-        id: "Vr38Nu",
-        left: 69.35185185185185,
-        top: 301.71759259259255,
-        width: 420,
-        height: 58,
-        lineHeight: 1.2,
-        content:
-          "<p style='text-align: center;'><strong><span style='color: #ffffff;'><span style='font-size: 32px'>3.请输入标题</span></span></strong></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{themeColor}}"
-      },
-      {
-        type: "text",
-        id: "IwKRSu",
-        left: 69.35185185185185,
-        top: 381.7824074074074,
-        width: 420,
-        height: 129,
-        content:
-          "<p style='text-align: center;'><span style='font-size: 22px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 22px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 22px'>在此处输入内容</span></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{subColor}}"
-      },
-      {
-        type: "text",
-        id: "0Opr1v",
-        left: 510.64814814814815,
-        top: 301.71759259259255,
-        width: 420,
-        height: 58,
-        lineHeight: 1.2,
-        content:
-          "<p style='text-align: center;'><strong><span style='color: #ffffff;'><span style='font-size: 32px'>4.请输入标题</span></span></strong></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{themeColor}}"
-      },
-      {
-        type: "text",
-        id: "4L9Uzz",
-        left: 510.64814814814815,
-        top: 381.7824074074074,
-        width: 420,
-        height: 129,
-        content:
-          "<p style='text-align: center;'><span style='font-size: 22px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 22px'>在此处输入内容</span></p><p style='text-align: center;'><span style='font-size: 22px'>在此处输入内容</span></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{subColor}}"
-      }
-    ],
-    background: {
-      type: "solid",
-      color: "{{backgroundColor}}"
-    }
-  },
-  {
-    id: "template",
-    elements: [
-      {
-        type: "text",
-        id: "GdEGxg",
-        left: 134.53703703703704,
-        top: 127.25,
-        width: 152.77777777777777,
-        height: 308,
-        lineHeight: 1.8,
-        content:
-          "<p style='text-align: center;'><strong><span style='color: #ffffff;'><span style='font-size: 40px'>请在此处输入标题</span></span></strong></p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        wordSpace: 8,
-        fill: "{{themeColor}}"
-      },
-      {
-        type: "text",
-        id: "y5sAfw",
-        left: 332.8703703703704,
-        top: 127.25,
-        width: 532.5925925925926,
-        height: 50,
-        content: "<blockquote><p style=''>请在此处输入内容1</p></blockquote>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{subColor}}"
-      },
-      {
-        type: "text",
-        id: "VeuocM",
-        left: 332.8703703703704,
-        top: 212.0648148148148,
-        width: 532.5925925925926,
-        height: 50,
-        content: "<blockquote><p style=''>请在此处输入内容2</p></blockquote>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{subColor}}"
-      },
-      {
-        type: "text",
-        id: "RyFWQe",
-        left: 332.8703703703704,
-        top: 296.8796296296296,
-        width: 532.5925925925926,
-        height: 50,
-        content: "<blockquote><p style=''>请在此处输入内容3</p></blockquote>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{subColor}}"
-      },
-      {
-        type: "text",
-        id: "Q56viI",
-        left: 332.8703703703704,
-        top: 381.69444444444446,
-        width: 532.5925925925926,
-        height: 50,
-        content: "<blockquote><p style=''>请在此处输入内容4</p></blockquote>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{subColor}}"
-      }
-    ],
-    background: {
-      type: "solid",
-      color: "{{backgroundColor}}"
-    }
-  },
-  {
-    id: "template",
-    elements: [
-      {
-        type: "shape",
-        id: "SUWirT",
-        left: 73.8888888888889,
-        top: 64.21296296296302,
-        width: 49.629629629629626,
-        height: 49.629629629629626,
-        viewBox: [200, 200],
-        path: "M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: false,
-        rotate: 0,
-        text: {
-          content: "<p style='text-align: center;'><span style='color: #ffffff;'>1</span></p>",
-          defaultFontName: "{{fontName}}",
-          defaultColor: "{{fontColor}}",
-          align: "middle"
-        }
-      },
-      {
-        type: "text",
-        id: "YjzN1M",
-        left: 148.70370370370372,
-        top: 64.21296296296302,
-        width: 323.7037037037037,
-        height: 120,
-        content: "<p style=''>在此输入内容</p><p style=''>在此输入内容</p><p style=''>在此输入内容</p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{subColor}}"
-      },
-      {
-        type: "shape",
-        id: "fS09I7",
-        left: 527.5925925925926,
-        top: 64.21296296296302,
-        width: 49.629629629629626,
-        height: 49.629629629629626,
-        viewBox: [200, 200],
-        path: "M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: false,
-        rotate: 0,
-        text: {
-          content: "<p style='text-align: center;'><span style='color: #ffffff;'>2</span></p>",
-          defaultFontName: "{{fontName}}",
-          defaultColor: "{{fontColor}}",
-          align: "middle"
-        }
-      },
-      {
-        type: "text",
-        id: "qCnfB1",
-        left: 602.4074074074074,
-        top: 64.21296296296302,
-        width: 323.7037037037037,
-        height: 120,
-        content: "<p style=''>在此输入内容</p><p style=''>在此输入内容</p><p style=''>在此输入内容</p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{subColor}}"
-      },
-      {
-        type: "shape",
-        id: "difAAT",
-        left: 73.8888888888889,
-        top: 221.25000000000003,
-        width: 49.629629629629626,
-        height: 49.629629629629626,
-        viewBox: [200, 200],
-        path: "M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: false,
-        rotate: 0,
-        text: {
-          content: "<p style='text-align: center;'><span style='color: #ffffff;'>3</span></p>",
-          defaultFontName: "{{fontName}}",
-          defaultColor: "{{fontColor}}",
-          align: "middle"
-        }
-      },
-      {
-        type: "text",
-        id: "EUlvMo",
-        left: 148.70370370370372,
-        top: 221.25000000000003,
-        width: 323.7037037037037,
-        height: 120,
-        content: "<p style=''>在此输入内容</p><p style=''>在此输入内容</p><p style=''>在此输入内容</p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{subColor}}"
-      },
-      {
-        type: "shape",
-        id: "US_9jB",
-        left: 527.5925925925926,
-        top: 221.25000000000003,
-        width: 49.629629629629626,
-        height: 49.629629629629626,
-        viewBox: [200, 200],
-        path: "M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: false,
-        rotate: 0,
-        text: {
-          content: "<p style='text-align: center;'><span style='color: #ffffff;'>4</span></p>",
-          defaultFontName: "{{fontName}}",
-          defaultColor: "{{fontColor}}",
-          align: "middle"
-        }
-      },
-      {
-        type: "text",
-        id: "243MnQ",
-        left: 602.4074074074074,
-        top: 221.25000000000003,
-        width: 323.7037037037037,
-        height: 120,
-        content: "<p style=''>在此输入内容</p><p style=''>在此输入内容</p><p style=''>在此输入内容</p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{subColor}}"
-      },
-      {
-        type: "shape",
-        id: "Y_KUj0",
-        left: 73.8888888888889,
-        top: 378.287037037037,
-        width: 49.629629629629626,
-        height: 49.629629629629626,
-        viewBox: [200, 200],
-        path: "M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: false,
-        rotate: 0,
-        text: {
-          content: "<p style='text-align: center;'><span style='color: #ffffff;'>5</span></p>",
-          defaultFontName: "{{fontName}}",
-          defaultColor: "{{fontColor}}",
-          align: "middle"
-        }
-      },
-      {
-        type: "text",
-        id: "9GglMe",
-        left: 148.70370370370372,
-        top: 378.287037037037,
-        width: 323.7037037037037,
-        height: 120,
-        content: "<p style=''>在此输入内容</p><p style=''>在此输入内容</p><p style=''>在此输入内容</p>",
-        rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{subColor}}"
-      },
-      {
-        type: "shape",
-        id: "eSInje",
-        left: 527.5925925925926,
-        top: 378.287037037037,
-        width: 49.629629629629626,
-        height: 49.629629629629626,
-        viewBox: [200, 200],
-        path: "M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z",
-        fill: "{{themeColor}}",
-        fixedRatio: false,
-        rotate: 0,
-        text: {
-          content: "<p style='text-align: center;'><span style='color: #ffffff;'>6</span></p>",
-          defaultFontName: "{{fontName}}",
-          defaultColor: "{{fontColor}}",
-          align: "middle"
-        }
-      },
-      {
         type: "text",
-        id: "0S3yUg",
-        left: 602.4074074074074,
-        top: 378.287037037037,
-        width: 323.7037037037037,
-        height: 120,
-        content: "<p style=''>在此输入内容</p><p style=''>在此输入内容</p><p style=''>在此输入内容</p>",
+        id: "ztXt5soL6B",
+        left: 809.8,
+        top: 345.5256570713392,
+        width: 300.3754693366708,
+        height: 110,
+        content: "<p style=''><span style='font-size: 60px;'>我是模板</span></p>",
         rotate: 0,
-        defaultFontName: "{{fontName}}",
-        defaultColor: "{{fontColor}}",
-        fill: "{{subColor}}"
+        defaultFontName: "Microsoft Yahei",
+        defaultColor: "#333333",
+        vertical: false,
+        paragraphSpace: 10
       }
     ],
     background: {
       type: "solid",
-      color: "{{backgroundColor}}"
+      color: "#ffffff"
     }
   }
 ]

+ 1 - 176
src/mocks/slides.ts

@@ -3,185 +3,10 @@ import type { Slide } from "@/types/slides"
 export const slides: Slide[] = [
   {
     id: "test-slide-1",
-    elements: [
-      {
-        type: "shape",
-        id: "4cbRxp",
-        left: 0,
-        top: 200,
-        width: 546,
-        height: 362.5,
-        viewBox: [200, 200],
-        path: "M 0 0 L 0 200 L 200 200 Z",
-        fill: "#5b9bd5",
-        fixedRatio: false,
-        opacity: 0.7,
-        rotate: 0
-      },
-      {
-        type: "shape",
-        id: "ookHrf",
-        left: 0,
-        top: 0,
-        width: 300,
-        height: 320,
-        viewBox: [200, 200],
-        path: "M 0 0 L 0 200 L 200 200 Z",
-        fill: "#5b9bd5",
-        fixedRatio: false,
-        flipV: true,
-        rotate: 0
-      },
-      {
-        type: "text",
-        id: "idn7Mx",
-        left: 355,
-        top: 65.25,
-        width: 450,
-        height: 188,
-        lineHeight: 1.2,
-        content: '<p><strong><span style="font-size: 112px;">ppt</span></strong></p>',
-        rotate: 0,
-        defaultFontName: "Microsoft Yahei",
-        defaultColor: "#333"
-      },
-      {
-        type: "text",
-        id: "7stmVP",
-        left: 355,
-        top: 253.25,
-        width: 585,
-        height: 56,
-        content: '<p><span style="font-size: 24px;">在线演示文稿应用</span></p>',
-        rotate: 0,
-        defaultFontName: "Microsoft Yahei",
-        defaultColor: "#333"
-      },
-      {
-        type: "line",
-        id: "FnpZs4",
-        left: 361,
-        top: 238,
-        start: [0, 0],
-        end: [549, 0],
-        points: ["", ""],
-        color: "#5b9bd5",
-        style: "solid",
-        width: 2
-      }
-    ],
+    elements: [],
     background: {
       type: "solid",
       color: "#ffffff"
     }
-  },
-  {
-    id: "test-slide-2",
-    elements: [
-      {
-        type: "text",
-        id: "ptNnUJ",
-        left: 145,
-        top: 148,
-        width: 711,
-        height: 77,
-        lineHeight: 1.2,
-        content: '<p style="text-align: center;"><strong><span style="font-size: 48px;">在此处添加标题</span></strong></p>',
-        rotate: 0,
-        defaultFontName: "Microsoft Yahei",
-        defaultColor: "#333"
-      },
-      {
-        type: "text",
-        id: "mRHvQN",
-        left: 207.50000000000003,
-        top: 249.84259259259264,
-        width: 585,
-        height: 56,
-        content: '<p style="text-align: center;"><span style="font-size: 24px;">在此处添加副标题</span></p>',
-        rotate: 0,
-        defaultFontName: "Microsoft Yahei",
-        defaultColor: "#333"
-      },
-      {
-        type: "line",
-        id: "7CQDwc",
-        left: 323.09259259259267,
-        top: 238.33333333333334,
-        start: [0, 0],
-        end: [354.8148148148148, 0],
-        points: ["", ""],
-        color: "#5b9bd5",
-        style: "solid",
-        width: 4
-      },
-      {
-        type: "shape",
-        id: "09wqWw",
-        left: -27.648148148148138,
-        top: 432.73148148148147,
-        width: 1056.2962962962963,
-        height: 162.96296296296296,
-        viewBox: [200, 200],
-        path: "M 0 20 C 40 -40 60 60 100 20 C 140 -40 160 60 200 20 L 200 180 C 140 240 160 140 100 180 C 40 240 60 140 0 180 L 0 20 Z",
-        fill: "#5b9bd5",
-        fixedRatio: false,
-        rotate: 0
-      }
-    ],
-    background: {
-      type: "solid",
-      color: "#fff"
-    }
-  },
-  {
-    id: "test-slide-3",
-    elements: [
-      {
-        type: "shape",
-        id: "vSheCJ",
-        left: 183.5185185185185,
-        top: 175.5092592592593,
-        width: 605.1851851851851,
-        height: 185.18518518518516,
-        viewBox: [200, 200],
-        path: "M 0 0 L 200 0 L 200 200 L 0 200 Z",
-        fill: "#5b9bd5",
-        fixedRatio: false,
-        rotate: 0
-      },
-      {
-        type: "shape",
-        id: "Mpwv7x",
-        left: 211.29629629629628,
-        top: 201.80555555555557,
-        width: 605.1851851851851,
-        height: 185.18518518518516,
-        viewBox: [200, 200],
-        path: "M 0 0 L 200 0 L 200 200 L 0 200 Z",
-        fill: "#5b9bd5",
-        fixedRatio: false,
-        rotate: 0,
-        opacity: 0.7
-      },
-      {
-        type: "text",
-        id: "WQOTAp",
-        left: 304.9074074074074,
-        top: 198.10185185185182,
-        width: 417.9629629629629,
-        height: 140,
-        content:
-          '<p style="text-align: center;"><strong><span style="font-size: 80px;"><span style="color: rgb(255, 255, 255);">感谢观看</span></span></strong></p>',
-        rotate: 0,
-        defaultFontName: "Microsoft Yahei",
-        defaultColor: "#333",
-        wordSpace: 5
-      }
-    ],
-    background: {
-      type: "solid",
-      color: "#fff"
-    }
   }
 ]

+ 1 - 1
src/store/slides.ts

@@ -37,7 +37,7 @@ export const useSlidesStore = defineStore("slides", {
     theme: theme, // 主题样式
     slides: slides, // 幻灯片页面数据
     slideIndex: 0, // 当前页面索引
-    viewportSize: 1000, // 可视区域宽度基数
+    viewportSize: 1920, // 可视区域宽度基数
     viewportRatio: 0.5625 // 可视区域比例,默认16:9
   }),
 

+ 12 - 19
src/views/pptScreen/pptScreen.vue

@@ -6,32 +6,25 @@
 
 <script setup lang="ts">
 import Screen from "../Screen/index.vue"
-import Axios from "axios"
 import { ref } from "vue"
 import { useSlidesStore } from "@/store"
+import { getHttpJson } from "@/libs/jsonTool"
 
 const slidesStore = useSlidesStore()
 
 const initLoading = ref(true)
 
-Axios.get("data.json")
-  .then(res => {
-    if (res.status === 200 && typeof res.data === "object") {
-      const { title, width, height, theme, slides } = res.data
-      console.log(res.data)
-      slidesStore.setTitle(title)
-      slidesStore.setViewportSize(width)
-      slidesStore.setViewportRatio(height / width)
-      slidesStore.setTheme(theme)
-      slidesStore.setSlides(slides)
-      initLoading.value = false
-    } else {
-      console.log(res)
-    }
-  })
-  .catch(err => {
-    console.log(err)
-  })
+getHttpJson("data.json").then(res => {
+  if (res.code === 200) {
+    const { width, theme, slides } = res.data
+    console.log(res.data)
+    slidesStore.setViewportSize(width)
+    slidesStore.setViewportRatio(theme.viewportRatio)
+    slidesStore.setTheme(theme)
+    slidesStore.setSlides(slides)
+    initLoading.value = false
+  }
+})
 </script>
 
 <style lang="scss" scoped>

+ 14 - 0
src/worker/index.ts

@@ -0,0 +1,14 @@
+import addMethods from "workerize-loader/dist/rpc-wrapper.js"
+const methods = ["createTaskController", "destoryTaskController", "addCourseWareTask", "getRunJobIds", "initOssResource", "destroyOssResource"]
+export default function (strs: any[]) {
+  const urlBlob = new Blob(strs)
+  const w = new Worker(URL.createObjectURL(urlBlob), { name: "worker/mine1-coursewares.[contenthash:8].worker.js" })
+  URL.revokeObjectURL(URL.createObjectURL(urlBlob))
+  addMethods(w, methods)
+  w.ready = new Promise(function (r) {
+    w.addEventListener("ready", function () {
+      r(w)
+    })
+  })
+  return w
+}

+ 144 - 0
src/worker/useCoursewaresWorker.ts

@@ -0,0 +1,144 @@
+import myWorker from "./index"
+import axios from "axios"
+import { getToken } from "@/libs/auth"
+import { URL_API } from "@/config/index"
+
+let worker: any = null
+let useOnCoursewaresMessage = (e: any) => {}
+
+/**
+ * 初始化worker
+ */
+export const initWorker = () => {
+  // eslint-disable-next-line no-async-promise-executor
+  return new Promise(async (resolve, reject) => {
+    if (!worker) {
+      const str1 = await axios.get("./pptworker/cos-js-sdk-v5.min.js")
+      const str2 = await axios.get("./pptworker/upload.js")
+      const str3 = await axios.get("./pptworker/pptJson.js")
+      worker = new myWorker([str1.data, str2.data, str3.data])
+      worker.ready
+        .then(() => worker.createTaskController())
+        .then(() => {
+          worker.onmessage = onCoursewaresMessage
+        })
+        .then(() => {
+          worker.initOssResource(URL_API, getToken())
+        })
+        .then(resolve(worker))
+        .catch(reject)
+      return
+    }
+    resolve(worker)
+  })
+}
+/**
+ * 销毁worker
+ */
+export const destroyWorker = () => {
+  return new Promise((resolve, reject) => {
+    if (worker) {
+      worker
+        .destoryTaskController()
+        .then(() => worker.terminate())
+        .then(() => {
+          worker = null
+          resolve("")
+        })
+        .catch(reject)
+    } else {
+      resolve("")
+    }
+  })
+}
+/**
+ * 添加任务
+ */
+type downloadCoursewareType = {
+  type: "downloadCourseware"
+  extra: {
+    id: string
+    type: string
+    outputName: string
+    name: string
+    jsonUrl: string
+  }
+}
+/* {
+  type: "downloadCourseware",
+  qqq:"",  //可以自定义属性 结果返回的时候 同时返回
+  extra: {
+    id: "",
+    type: "pptx",
+    outputName: `test.pptx`,
+    name: "test",
+    jsonUrl: "https://oss.dayaedu.com/pptList/example.json"
+  }
+} */
+type uploadCoursewareType = {
+  type: "uploadCourseware"
+  extra: {
+    outputName: string
+    name: string
+    type: string
+    suffix: string
+    path: string
+    paths: string[]
+    files: File
+  }
+}
+/* {
+  type: "uploadCourseware",
+  qqq:"",  //可以自定义属性 结果返回的时候 同时返回
+  extra: {
+    files: File
+    name: "test",
+    outputName: "test.pptx",
+    path: "",
+    paths: [""],
+    suffix: ".pptx",
+    type: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
+  }
+} */
+export const addCourseWareTask = (task: downloadCoursewareType | uploadCoursewareType | null, onCoursewaresMessage: (e: any) => void) => {
+  return new Promise((resolve, reject) => {
+    if (worker) {
+      useOnCoursewaresMessage = onCoursewaresMessage
+      worker
+        .addCourseWareTask(task)
+        .then((result: any) => {
+          task = null
+          resolve(result)
+        })
+        .catch(reject)
+    } else {
+      reject("worker没有初始化")
+    }
+  })
+}
+
+/**
+ * 获取运行中的任务id
+ */
+/* getRunJobIds().then(res => {
+  // res.length 大于 1 时候 说明有任务运行
+}) */
+export const getRunJobIds = () => {
+  return new Promise((resolve, reject) => {
+    if (worker) {
+      worker.getRunJobIds().then(resolve).catch(reject)
+    } else {
+      reject("worker没有初始化")
+    }
+  })
+}
+
+/**
+ * 任务进度监控
+ */
+const onCoursewaresMessage = (e: any) => {
+  const { type, result = {} } = e.data
+  if (type === "progress") {
+    useOnCoursewaresMessage(result)
+  }
+}

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä