Flex
2025-06-12 1d29cc7ed2fd9e9f102298fbb08c0f7fe7db50b7
easegen-front/src/views/chooseTemplate/index.vue
@@ -16,7 +16,7 @@
          @blur="saveEdit"
          @keydown.enter="saveEdit"
        />
        <!-- 如果不在编辑,显示文本 -->
        <div
          v-else
@@ -193,7 +193,7 @@
                :src="selectPPT.pictureUrl"
                style="z-index: 1"
              />
              <!-- 画中画 -->
              <Vue3DraggableResizable
                :key="selectPPT.digitalHuman.x + '-' + selectPPT.digitalHuman.y"
@@ -588,6 +588,7 @@
            <div class="SoundModelAreaBox" v-for="(value, key, index) in audioList" :key="index">
              <div class="SoundClassTit">
                <el-divider content-position="center"> {{ languageClass(key) }} </el-divider>
                <!-- <el-divider content-position="center"> 123 </el-divider> -->
              </div>
              <div class="SoundClassContent">
                <div
@@ -695,7 +696,7 @@
    </el-dialog>
  </div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted, computed, watch, nextTick, shallowRef } from 'vue'
import draggable from 'vuedraggable'
@@ -747,7 +748,7 @@
import { ElMessage, ElMessageBox } from 'element-plus'
import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
import { any } from 'vue-types'
const editorHtml = useEditorHtml()
const router = useRouter()
const route = useRoute()
@@ -794,9 +795,9 @@
  hostList.value.forEach((el) => {
    el.isActive = el.id === host.id
  })
  const pagesToUpdate = applyAllHost.value ? PPTArr.value : [selectPPT.value]
  pagesToUpdate.forEach((page) => {
    page.digitalHuman.host = host
    initHumanPositon(host, page.digitalHuman)
@@ -813,22 +814,22 @@
  width: 800,
  height: (800 * 9) / 16
})
const thumViewSize = reactive({
  width: 152,
  height: (152 * 9) / 16
})
const digitalHumanSize = reactive({
  width: 640,
  height: 360
})
const scaleRatio = computed(() => ({
  width: courseInfo.value.width / viewSize.width,
  height: courseInfo.value.height / viewSize.height
}))
const courseInfo = ref({
  id: 0,
  accountId: userId.value,
@@ -841,7 +842,7 @@
  width: 1920,
  height: 1080
})
watch(
  () => courseInfo.value.aspect,
  (newAspect) => {
@@ -849,15 +850,15 @@
    courseInfo.value.height = newAspect === '16:9' ? 1080 : 1920
  }
)
const state = reactive({
  dragging: false
})
const TEMPLATE_PRESETS = ref([])
const templates = ref([])
const selectTemplate = ref([])
const tabs1 = [
  {
    itemName: t('courseCenter.model'),
@@ -868,7 +869,7 @@
    itemValue: '1'
  }
]
const tabs1ActiveNum = ref('0')
const tabs1ActiveNum = ref('')
const tabs2ActiveNum = ref('')
const tabs2 = [
  {
@@ -915,17 +916,17 @@
  queryParams1.zg = tabs4ActiveNum.value
  getList1()
}
const tabs2Click = (item) => {
  tabs2ActiveNum.value = item.itemValue
  getList()
}
const tabs3Click = (item) => {
  tabs3ActiveNum.value = item.itemValue
  getList()
}
const rightTools = reactive([
  {
    name: t('courseCenter.template'),
@@ -959,7 +960,7 @@
  //   isActive: false
  // }
])
const showHeadImageTool = ref(false)
const showDigitalHumanTool = ref(false)
// 声音
@@ -974,7 +975,7 @@
  templateSelection.value = template
  console.log(templateSelection.value)
}
const handleTemplateSelection1 = (index) => {
  if (index == 2) {
    applyAllTemplate.value = true
@@ -1003,7 +1004,7 @@
    getList1()
  } else if (item.name == t('courseCenter.sound')) {
    // 声音的处理       selectAudio
    if (selectLanguage.value === undefined) {
      // 获取语言种类
      getLanguageList()
@@ -1047,7 +1048,7 @@
  audioType.value = list
  changeAudio.value = list[0].value
}
// 可选的声音类别列表
const SoundTypeList = ref()
// 当前选择的声音类别
@@ -1113,7 +1114,7 @@
      }
    })
    console.log(LanguageArr)
    audioList.value = LanguageArr
    total.value = data.total
    if (selectList.value !== undefined && selectList.value !== null) {
@@ -1124,7 +1125,7 @@
      SoundcurrentAudio.value.pause()
      SoundcurrentAudio.value = null
    }
    // 重置当前播放状态
    if (SoundcurrentlyPlaying.value) {
      SoundcurrentlyPlaying.value.isPlay = false
@@ -1162,7 +1163,7 @@
  SoundTypeList.value.forEach((element) => {
    if (element.value === event) {
      ChangeSoundTypeList.value = { ...element }
      if (selectList.value !== undefined && selectList.value !== null) {
        selectList.value = null //初始化
      }
@@ -1171,7 +1172,7 @@
        SoundcurrentAudio.value.pause()
        SoundcurrentAudio.value = null
      }
      // 重置当前播放状态
      if (SoundcurrentlyPlaying.value) {
        SoundcurrentlyPlaying.value.isPlay = false
@@ -1214,7 +1215,7 @@
      message.warning('请选择声音模型')
      return false
    }
    selectAudio(selectList.value)
  } else if (ChangeSoundTypeList.value.value === 1) {
    // 清除选中的音频
@@ -1225,19 +1226,19 @@
    //     item.isSelect = false
    //   })
    // }
    // 停止当前播放的音频
    if (SoundcurrentAudio.value) {
      SoundcurrentAudio.value.pause()
      SoundcurrentAudio.value = null
    }
    // 重置当前播放状态
    if (SoundcurrentlyPlaying.value) {
      SoundcurrentlyPlaying.value.isPlay = false
      SoundcurrentlyPlaying.value = null
    }
    selectAudio(undefined)
  }
  // 记录用户选择的声音信息
@@ -1247,9 +1248,9 @@
    type: ChangeSoundTypeList.value, //类型
    model: selectList.value !== undefined ? selectList.value : '' //声音模型
  }
  // SoundTool.value = false
  // rightTools.forEach((child) => {
  //   if (child.name == '声音' || child.name == 'sound') {
  //     child.isActive = false
@@ -1271,37 +1272,37 @@
    }
  })
}
// 音频管理
const SoundcurrentAudio = ref<HTMLAudioElement | null>(null)
const SoundcurrentlyPlaying = ref<any>(null)
const playAudio = async (item: any) => {
  // 如果点击的是当前正在播放的项目,则暂停
  if (SoundcurrentlyPlaying.value && SoundcurrentlyPlaying.value.id === item.id) {
    SoundpauseAudio(item)
    return
  }
  // 停止当前播放的音频
  if (SoundcurrentAudio.value) {
    SoundcurrentAudio.value.pause()
    SoundcurrentAudio.value = null
  }
  // 如果之前有播放的项目,重置其状态
  if (SoundcurrentlyPlaying.value) {
    SoundcurrentlyPlaying.value.isPlay = false
  }
  // 设置新的播放项目
  SoundcurrentlyPlaying.value = item
  item.isPlay = true
  // 创建新的音频对象并播放
  SoundcurrentAudio.value = new Audio(item.auditionUrl)
  SoundcurrentAudio.value.play()
  // 添加播放结束事件监听
  SoundcurrentAudio.value.addEventListener('ended', () => {
    item.isPlay = false
@@ -1309,7 +1310,7 @@
    SoundcurrentAudio.value = null
  })
}
const SoundpauseAudio = (item: any) => {
  if (
    SoundcurrentAudio.value &&
@@ -1321,14 +1322,14 @@
    SoundcurrentlyPlaying.value = null
  }
}
const PPTArr = ref([])
const percentagePPT = ref(0)
const showLeftList = ref(true)
//是否进行过删除操作
const DeleteD = ref(false)
const selectPPT = ref({
  pictureUrl: '',
  innerPicture: {
@@ -1366,46 +1367,46 @@
    host: null
  }
})
const videoText = ref(0)
const videoDuration = ref('')
watch(
  () => PPTArr.value,
  (val) => {
    if (!val) return
    videoText.value = val.reduce((prev, curr) => {
      if (!curr.pptRemark) return prev
      const plainText = curr.pptRemark.replace(/<[^>]+>/g, '')
      return prev + plainText.length
    }, 0)
    let videoTime = (videoText.value / 200) * 60
    videoDuration.value = formateVideoTime(Math.ceil(videoTime))
  },
  { deep: true }
)
const formateVideoTime = (times: any) => {
  let hours: any = parseInt(`${times / 60 / 60}`)
  let restMinutes: any = parseInt(`${(times / 60) % 60}`)
  let seconds: any = parseInt(`${times % 60}`)
  if (hours < 10) hours = '0' + hours
  if (restMinutes < 10) restMinutes = '0' + restMinutes
  if (seconds < 10) seconds = '0' + seconds
  return hours + ':' + restMinutes + ':' + seconds
}
const uploadRef = ref()
const headers = {
  Accept: 'application/json, text/plain, */*',
  Authorization: 'Bearer ' + getAccessToken(),
  'tenant-id': getTenantId()
}
const uploadFileObj = reactive({
  filename: '',
  size: 0,
@@ -1417,7 +1418,7 @@
  extInfo: '{"addMode":true,"docType":1,"pptNotes":true,"pptContent":false,"notesPolish":false}',
  resolveType: 1
})
//智能讲稿组件begin
//添加ref
const rewriterRef = ref()
@@ -1459,14 +1460,14 @@
  file.uid = genFileId()
  uploadRef.value!.handleStart(file)
}
const handleChange = (files) => {
  const extension = files.name.split('.').pop().toLowerCase()
  uploadFileObj.docType = extension === 'pdf' ? 2 : 1
  uploadFileObj.filename = files.name
  uploadFileObj.size = files.size
}
const uploadExplainRef = ref()
const handleSuccess = (rawFile) => {
  message.success('上传成功!')
@@ -1474,12 +1475,12 @@
  uploadExplainRef.value.open()
  uploadRef.value!.clearFiles()
}
const handleError = (err) => {
  message.error('上传失败,请重试')
  console.error('Upload error:', err)
}
const uploadSubmit = (uploadForm) => {
  pptTemplateApi.createPPT(uploadFileObj).then((res) => {
    if (res) {
@@ -1489,7 +1490,7 @@
    }
  })
}
const chooseTemplate = (currTemplate) => {
  console.log('currTemplate', currTemplate)
  selectTemplate.value = cloneDeep(currTemplate)
@@ -1499,7 +1500,7 @@
  currTemplate.isActive = true
  applyTemplate()
}
const schedulePPTTimer = ref()
const schedulePPT = (id) => {
  percentagePPT.value = 0
@@ -1529,7 +1530,7 @@
          item.businessId = generateUUID()
          item.width = courseInfo.value.width
          item.height = courseInfo.value.height
          // 初始化独立的数字人配置
          item.digitalHuman = {
            show: true,
@@ -1540,14 +1541,14 @@
            active: false,
            host: selectHost.value
          }
          let ppturl = item.pictureUrl
          if (selectTemplate.value.showBackground) {
            item.pictureUrl = selectTemplate.value.bgImage
          } else {
            item.pictureUrl = ''
          }
          if (item.pictureUrl && selectTemplate.value.showPpt) {
            item.innerPicture = {
              name: '画中画',
@@ -1578,16 +1579,16 @@
            }
          }
        })
        PPTArr.value = res
        PPTArr.value[0].isActive = true
        selectPPT.value = PPTArr.value[0]
        showLeftList.value = true
        clearInterval(schedulePPTTimer.value)
        //ppt人脸校验
        PPtIsHaveFace()
        //轮询保存课程
        /**
         * 后端数据库压力过大,暂时停止定时保存
@@ -1597,12 +1598,12 @@
    })
  }, 2000)
}
const cancelAnalyze = () => {
  showLeftList.value = true
  clearInterval(schedulePPTTimer.value)
}
const copyDocument = (item, index) => {
  ElMessageBox.confirm('是否复制该页面?', '提示', {
    confirmButtonText: '是',
@@ -1624,7 +1625,7 @@
      })
    })
}
const deleteDocument = (item) => {
  ElMessageBox.confirm('是否删除该页面?', '提示', {
    confirmButtonText: '是',
@@ -1643,15 +1644,15 @@
      })
    })
}
const deleteDigitalHuman = () => {
  selectPPT.value.digitalHuman.show = false
}
const deleteInnerPicture = () => {
  selectPPT.value.innerPicture.src = ''
}
const deleteMore = () => {
  let selected = PPTArr.value.filter((child) => child.isChecked == true)
  if (selected.length == 0) {
@@ -1660,7 +1661,7 @@
    PPTArr.value = PPTArr.value.filter((child) => child.isChecked !== true)
  }
}
const hostList = ref([])
const loading = ref(true)
const total = ref(0)
@@ -1677,7 +1678,7 @@
  zg: ''
})
const selectHost = ref(null)
const getList = async () => {
  loading.value = true
  try {
@@ -1689,7 +1690,7 @@
    data.list.forEach((item) => {
      item.isActive = false
    })
    hostList.value = data.list
    if (hostList.value.length > 0 && !selectHost.value) {
      selectHost.value = hostList.value[0]
@@ -1698,20 +1699,20 @@
        selectPPT.value.digitalHuman.host = selectHost.value
      }
    }
    total.value = data.total
  } finally {
    loading.value = false
  }
}
const choosePPt = (item) => {
  PPTArr.value.forEach((child) => {
    child.isActive = child.id === item.id
  })
  selectPPT.value = item
}
// const chooseHost = (item) => {
//   hostList.value.forEach((el) => {
//     el.isActive = el.id === item.id
@@ -1733,14 +1734,14 @@
    digitalHuman.y = viewSize.height - digitalHuman.h
  }
}
const audioSelect = ref()
const audioSelectData = ref()
const openSelect = () => {
  audioSelect.value.open()
}
const selectAudio = (data) => {
  console.log(data)
  audioSelectData.value = data
@@ -1752,7 +1753,7 @@
    })
  }
}
const coursesCreate = () => {
  const params = {
    accountId: userId.value
@@ -1763,24 +1764,24 @@
    }
  })
}
const saveTime = ref()
const getSaveTime = () => {
  const date = new Date()
  let h: any = date.getHours()
  let m: any = date.getMinutes()
  let s: any = date.getSeconds()
  if (h < 10) h = '0' + h
  if (m < 10) m = '0' + m
  if (s < 10) s = '0' + s
  return h + ':' + m + ':' + s
}
const warningDialog = ref()
const voiceData = reactive({
  show: false,
  speechRate: 1,
@@ -1798,21 +1799,16 @@
    2: '2'
  }
})
const removeHtmlTags = (html) => {
  const parser = new DOMParser()
  const doc = parser.parseFromString(html, 'text/html')
  return doc.body.textContent || ''
}
const saveSubmit = async (type) => {
  console.log('是否删除', DeleteD.value)
  if ( selectLanguage.value?.value === undefined || selectLanguage.value.value === 'all_Language' ) {
    message.error('请先选择语种与声音类型')
    return
  }
  if (ChangeSoundTypeList.value.value === 2) {
    //此时为通用
    if (selectList.value === undefined || selectList.value === null) {
@@ -1820,19 +1816,19 @@
      return false
    }
  }
  if (!PPTArr.value || PPTArr.value.length === 0) {
    message.warning('场景为空,请先上传PPT!')
    return false
  }
  //人脸校验
  while (!IsEndCheckFace.value) {} //一个空循环,主要为了避免极端情况下当用户点击保存按钮或者视频合成按钮时,人脸校验未完成的问题
  if (IsHaveFace.value && !DeleteD.value) {
    message.warning('当前ppt中存在人脸元素,为方便后续视频生成,请去除该元素')
    return
  }
  //保存课程
  let saveSubmitForm = {
    accountId: courseInfo.value.accountId,
@@ -1850,9 +1846,9 @@
    thumbnail: '',
    subtitlesStyle: '{}'
  }
  saveSubmitForm.id = courseInfo.value.id
  const scenes = []
  const pageInfo = {
    docInfo: {
@@ -1862,18 +1858,18 @@
    },
    scenes: []
  }
  let thumbnail = ''
  PPTArr.value.forEach((item, index) => {
    try {
      pageInfo.scenes.push(item.businessId)
      if (index === 0) {
        thumbnail = item.pictureUrl
      }
      item.pptRemark = removeHtmlTags(item.pptRemark)
      const formatItem = {
        background: {
          backgroundType: item.backgroundType,
@@ -1952,18 +1948,18 @@
        },
        businessId: item.businessId
      }
      scenes.push(formatItem)
    } catch (error) {
      console.error(`处理第 ${index + 1} 个场景时出错:`, error)
      throw error
    }
  })
  saveSubmitForm.pageInfo = JSON.stringify(pageInfo)
  saveSubmitForm.thumbnail = thumbnail
  saveSubmitForm.scenes = cloneDeep(scenes)
  if (type == 'save') {
    if (DeleteD.value) {
      //如果进行过ppt删除操作则需要进行二次查看
@@ -1973,7 +1969,7 @@
        return
      }
    }
    try {
      const res = await pptTemplateApi.coursesSave(JSON.stringify(saveSubmitForm))
      if (res) {
@@ -1989,6 +1985,7 @@
    }
  } else {
    try {
      if (
        ChangeSoundTypeList.value?.value === undefined ||
        selectLanguage.value?.value === undefined
@@ -1996,13 +1993,13 @@
        message.error('请先选择语种与声音类型')
        return
      }
      const saveResult = await saveSubmit('save')
      if (!saveResult) {
        message.error('保存失败,请重试后再合成视频')
        return
      }
      let warningStrArr = []
      for (let i = 0; i < PPTArr.value.length; i++) {
        const item = PPTArr.value[i]
@@ -2024,12 +2021,12 @@
          }
        }
      }
      if (warningStrArr.length > 0) {
        warningDialog.value.open(warningStrArr.map((warning) => `<div>${warning}</div>`).join(''))
        return
      }
      try {
        const res = await pptTemplateApi.megerMedia(saveSubmitForm)
        if (res) {
@@ -2045,12 +2042,12 @@
    }
  }
}
const applyTemplate = (ppt = null) => {
  const template = selectTemplate.value
  console.log('template', selectTemplate.value)
  const pptList = applyAllTemplate.value ? PPTArr.value : [selectPPT.value]
  pptList.forEach((item) => {
    const originalPPT = item.innerPicture?.src || item.pictureUrl
    console.log(template)
@@ -2087,17 +2084,17 @@
      }
  })
}
const replaceDialog = ref(null)
const openReplaceDialog = () => {
  replaceDialog.value.open()
}
const escapeRegExp = (string) => {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}
const handleReplacement = (replacements) => {
  PPTArr.value.forEach((item) => {
    if (item.pptRemark) {
@@ -2110,25 +2107,25 @@
  })
  message.success('批量替换成功!')
}
const showAudioPlay = ref(false)
const showAudioPlay1 = ref(false)
const startAudioPlay = ref(false)
const textareaRef = ref()
const selectTextarea = ref('')
const audioExceed = () => {
  message.warning('最多上传一个声音驱动文件!')
}
const currentAudio = ref()
const createAudio = async () => {
  if (ChangeSoundTypeList.value?.value === undefined || selectLanguage.value?.value === undefined || selectLanguage.value.value === 'all_Language' ) {
    message.error('请先选择语种与声音类型')
    return
  }
  if (ChangeSoundTypeList.value.value === 2) {
    //此时为通用
    if (selectList.value === undefined || selectList.value === null) {
@@ -2137,42 +2134,49 @@
    }
  }
  if( ChangeSoundTypeList.value.value === 1 ){
    if( selectPPT.value.digitalHuman.host === null || selectPPT.value.digitalHuman.host.id === undefined || selectPPT.value.digitalHuman.host.id === null ){
      message.warning('请选择数字人')
      return false
    }
  }
  const text = editorRef.value.getText()
  if (!text) {
    message.warning('请输入需要试听文本的内容…')
    return false
  }
  const truncatedText = text.length > 100 ? text.substring(0, 100) : text
  const params = {
    text: truncatedText,
    humanId: selectPPT.value.digitalHuman?.host?.id || null,
    voiceId: audioSelectData.value == undefined ? null : audioSelectData.value[0].id,
    language: selectLanguage.value?.value
  }
  if (ChangeSoundTypeList.value.value === 2) {
    //此时选取了声音模型
    params.humanId = null
  } else if (ChangeSoundTypeList.value.value === 1) {
    params.voiceId = null
  }
  try {
    showAudioPlay1.value = true
    const res = await pptTemplateApi.createAudio(params)
    if (res && !res.error) {
      showAudioPlay1.value = false
      showAudioPlay.value = true
      currentAudio.value = new Audio(res)
      currentAudio.value.addEventListener('ended', () => {
        showAudioPlay.value = false
        currentAudio.value = null
      })
      currentAudio.value.play()
    } else {
      showAudioPlay1.value = false
@@ -2182,37 +2186,37 @@
    showAudioPlay1.value = false
  }
}
const pauseAudio = () => {
  currentAudio.value.pause()
  currentAudio.value = null
  showAudioPlay.value = false
}
const currentAudioFile = ref()
const audioPlay = (file) => {
  if (!file.response.data) {
    message.error('未获取到文件')
    return
  }
  if (currentAudioFile.value) {
    currentAudioFile.value.pause()
    currentAudioFile.value.currentTime = 0
  }
  const audio = new Audio(file.response.data)
  currentAudioFile.value = audio
  audio.addEventListener('ended', () => {
    cancelAudio()
  })
  startAudioPlay.value = true
  audio.play()
}
const cancelAudio = () => {
  if (currentAudioFile.value) {
    currentAudioFile.value.pause()
@@ -2221,7 +2225,7 @@
  }
  startAudioPlay.value = false
}
const goBack = () => {
  if (PPTArr.value.length == 0) {
    pptTemplateApi.coursesDelete(courseInfo.value.id).then((res) => {
@@ -2231,17 +2235,17 @@
    router.go(-1)
  }
}
const editorRef = shallowRef()
const editorConfig = { placeholder: '请输入内容...' }
const handleCreated = (editor) => {
  editorRef.value = editor
}
const dialogVisible = ref(false)
const textList = ref([])
const handleWord = () => {
  editorRef.value.focus()
  selectTextarea.value = editorRef.value.getSelectionText()
@@ -2261,7 +2265,7 @@
    message.warning(`${selectTextarea.value}不是多音字`)
  }
}
const handleTag = (name) => {
  dialogVisible.value = false
  const node = {
@@ -2276,7 +2280,7 @@
const onDragMove = (evt, data) => {
  console.log(evt)
  console.log(data)
  // 限制坐标
  if (data.x < -100) {
    data.x = -100 // 可以设置最小坐标为 -100
@@ -2285,13 +2289,13 @@
    data.y = -100 // 可以设置最小坐标为 -100
  }
}
const getCourseDetail = async (id) => {
  const res = await pptTemplateApi.coursesDetail(id)
  console.log( "res当前信息", res )
  if (res) {
    courseInfo.value = res
    if (res.scenes && res.scenes.length > 0) {
      res.scenes.forEach((item) => {
        item.isActive = false
@@ -2301,7 +2305,7 @@
        item.backgroundType = item.background.backgroundType
        item.width = item.background.width
        item.height = item.background.height
        const hostInfo = item.components.find((p) => p.category == 2)
        if (hostInfo) {
          item.digitalHuman = {
@@ -2318,7 +2322,7 @@
            }
          }
        }
        const innerPicture = item.components.find((p) => p.category == 1)
        if (innerPicture) {
          item.innerPicture = {
@@ -2330,11 +2334,11 @@
          }
        }
      })
      PPTArr.value = res.scenes
      PPTArr.value[0].isActive = true
      selectPPT.value = PPTArr.value[0]
      PPTArr.value.forEach((scene, index) => {
        if (res.scenes[index].voice) {
          scene.selectAudio = res.scenes[index].voice
@@ -2343,7 +2347,7 @@
        }
        scene.uploadAudioUrl = res.scenes[index].audioDriver?.audioUrl
      })
      if (PPTArr.value[0].audioDriver?.fileName && PPTArr.value[0].audioDriver?.audioUrl) {
        selectPPT.value.fileList = [
          {
@@ -2352,7 +2356,7 @@
          }
        ]
      }
      // 设置音频选择数据
      const firstScene = res.scenes[0]
      if (firstScene.voice) {
@@ -2365,7 +2369,7 @@
        ]
      }
    }
    const pageInfo = res.pageInfo ? JSON.parse(res.pageInfo) : ''
    uploadFileObj.filename = pageInfo ? pageInfo.docInfo.fileName : ''
    uploadFileObj.size = pageInfo ? pageInfo.docInfo.fileSize : ''
@@ -2392,19 +2396,19 @@
    showDigitalHuman: item.showDigitalHuman === 1,
    showPpt: item.showPpt === 1
  }))
  templates.value = TEMPLATE_PRESETS.value.map((template) => cloneDeep(template))
  selectTemplate.value = cloneDeep(templates.value[0])
  await getList()
  if (route.query.id) {
    await getCourseDetail(route.query.id)
  } else {
    coursesCreate()
  }
})
onUnmounted(() => {
  if (schedulePPTTimer.value) {
    clearInterval(schedulePPTTimer.value)
@@ -2415,19 +2419,19 @@
  }
})
</script>
<style scoped lang="scss">
.pages {
  height: 100%;
  background-color: #f5f7fa;
}
.minddle-host-image {
  z-index: 5;
  width: 100%;
  height: 100%;
}
.template-top {
  display: flex;
  height: 60px;
@@ -2437,57 +2441,57 @@
  border: 1px solid #ebeef5;
  box-shadow: 0 3px 6px rgb(175 175 175 / 16%);
  justify-content: space-between;
  .top-left {
    display: flex;
    align-items: center;
    .top-icon {
      display: flex;
      align-items: center;
    }
    .back-text {
      margin-right: 20px;
      margin-left: 10px;
      cursor: pointer;
    }
    span {
      margin: 0 25px;
    }
  }
  .top-right {
    span {
      margin: 0 20px;
    }
  }
}
.template-main {
  display: flex;
  height: calc(100% - 82px);
  padding: 10px;
  justify-content: space-around;
  .template-left {
    position: relative;
    width: 180px;
    background-color: #fff;
    border: 1px solid #ebeef5;
    box-shadow: 0 3px 6px rgb(175 175 175 / 16%);
    .page {
      margin: 0;
      div {
        padding: 5px 10px;
        margin: 0;
        line-height: 30px;
        border-bottom: 1px solid #ebeef5;
      }
      .line {
        width: 30px;
        height: 3px;
@@ -2495,12 +2499,12 @@
        margin: 0;
        background-color: aqua;
      }
      .upload-demo {
        text-align: center;
      }
    }
    .left-upload-setting {
      display: flex;
      height: calc(100% - 86px);
@@ -2509,32 +2513,32 @@
      flex-direction: column;
      justify-content: center;
      align-items: center;
      div {
        line-height: 40px;
      }
      ::v-deep(.el-progress-bar) {
        width: 180px;
      }
      .el-button {
        margin: 20px 0;
      }
    }
    .image-list {
      height: calc(100% - 70px);
      padding: 10px;
      overflow: hidden auto;
      border-bottom: 1px solid #ebeef5;
      .list {
        position: relative;
        height: calc(152px * 9 / 16);
        margin: 20px 0;
        box-sizing: content-box;
        .list-index {
          position: absolute;
          top: 10px;
@@ -2548,7 +2552,7 @@
          background: #122121;
          border-radius: 5px;
        }
        .background {
          position: absolute;
          width: 100%;
@@ -2560,16 +2564,16 @@
          height: 100%;
          background-color: rgba(0, 0, 0, 0);
        }
        .ppt-bg {
          z-index: 2;
        }
        .host {
          position: absolute;
          z-index: 3;
        }
        .icon-content {
          position: absolute;
          top: 0;
@@ -2581,7 +2585,7 @@
        }
      }
    }
    .page-btn {
      position: absolute;
      bottom: 10px;
@@ -2589,7 +2593,7 @@
      padding: 0 10px;
    }
  }
  .template-middle {
    display: flex;
    width: 56%;
@@ -2598,36 +2602,36 @@
    flex-grow: 1;
    flex-direction: column;
    justify-content: flex-start;
    .middle-top {
      padding: 5px 20px;
    }
    .main-box {
      display: flex;
      padding: 10px 20px;
      border: 1px solid #ebeef5;
      justify-content: center;
      .main-image-box {
        position: relative;
        border: 1px solid #ebeef5;
        box-sizing: content-box;
      }
      .list {
        position: relative;
        display: flex;
        width: 95%;
        justify-content: center;
      }
      .ppt-bg {
        z-index: 2;
        width: 100%;
        height: 100%;
      }
      .host {
        position: absolute;
        right: 0;
@@ -2635,41 +2639,41 @@
        width: 300px;
      }
    }
    .voice-main {
      display: flex;
      justify-content: space-between;
      padding: 10px;
      .media-box {
        display: flex;
        align-items: center;
      }
    }
    .audio-upload {
      display: flex;
      align-items: center;
      justify-content: center;
      height: 200px;
    }
    .middle-textarea {
      padding: 5px 20px;
    }
    .tool-box {
      display: flex;
      padding: 10px;
      border-top: 1px solid #ebeef5;
      justify-content: space-between;
      .tool-btn {
        display: flex;
        align-items: center;
      }
    }
    .audio-play {
      position: absolute;
      top: 0;
@@ -2686,25 +2690,25 @@
      flex-direction: column;
    }
  }
  .template-right {
    position: relative;
    width: 20%;
    background-color: #fff;
    box-shadow: 0 3px 6px rgb(175 175 175 / 16%);
    .tabs-1 {
      display: flex;
      justify-content: space-around;
      padding: 10px 30px;
      border-bottom: 1px solid #ebeef5;
      .tabs-item {
        width: 50px;
        font-size: 14px;
        text-align: center;
        cursor: pointer;
        span {
          display: block;
          width: 50px;
@@ -2714,12 +2718,12 @@
        }
      }
    }
    .tabs-2 {
      display: flex;
      padding: 10px;
      justify-content: space-around;
      div {
        width: 60px;
        height: 30px;
@@ -2728,13 +2732,13 @@
        cursor: pointer;
        border-radius: 5px;
      }
      .tabs-active {
        color: #fff !important;
        background-color: #409eff;
      }
    }
    .apply-all {
      position: absolute;
      bottom: 80px;
@@ -2742,12 +2746,12 @@
      width: 100%;
      justify-content: center;
    }
    .host-list {
      height: 80%;
      overflow-y: auto;
      border-top: 1px solid #ebeef5;
      .host-item {
        position: relative;
        display: inline-block;
@@ -2755,7 +2759,7 @@
        margin: 5px 0;
        margin-left: 10px;
        cursor: pointer;
        .background {
          position: absolute;
          top: 0;
@@ -2765,7 +2769,7 @@
          height: 100%;
          background-color: #f0f1fa;
        }
        .host-name {
          position: absolute;
          bottom: 10px;
@@ -2779,7 +2783,7 @@
          background: rgb(225 225 225 / 70%);
          border-radius: 5px;
        }
        .ppt-bg {
          z-index: 2;
          width: 100%;
@@ -2788,31 +2792,31 @@
      }
    }
  }
  .image-setting {
    padding: 10px 20px;
    .img-setting {
      display: flex;
      align-items: center;
      line-height: 40px;
      .setting-label {
        width: 120px;
      }
      ::v-deep(.el-input) {
        width: 170px;
        margin-left: 10px;
      }
    }
  }
  .template-list {
    height: 90%;
    overflow-y: auto;
    border-top: 1px solid #ebeef5;
    .template-item {
      position: relative;
      display: inline-block;
@@ -2838,14 +2842,14 @@
        position: absolute;
        z-index: 2;
      }
      .human-image {
        position: absolute;
        z-index: 3;
      }
    }
  }
  .background {
    position: absolute;
    top: 0;
@@ -2855,25 +2859,25 @@
    height: 100%;
    //background-color: #f0f1fa;
  }
  .template-tool {
    width: 60px;
    padding: 10px;
    background-color: #fff;
    box-shadow: 0 3px 6px rgb(175 175 175 / 16%);
    .tool-item {
      display: flex;
      padding: 10px 20px;
      cursor: pointer;
      flex-direction: column;
      align-items: center;
      img {
        width: 32px;
        height: 32px;
      }
      .tool-name {
        width: 60px;
        margin-top: 6px;
@@ -2884,47 +2888,47 @@
    }
  }
}
::v-deep(.el-pagination) {
  position: absolute;
  bottom: 0;
}
::-webkit-scrollbar {
  width: 4px;
}
::-webkit-scrollbar-thumb {
  background-color: #888;
  border-radius: 6px;
}
::-webkit-scrollbar-track {
  background-color: #f2f2f2;
  border-radius: 6px;
}
.voice-card {
  z-index: 1000 !important;
}
.voice-card :deep(.el-card__body) {
  padding: 0;
}
.speech-slider {
  &:deep(.el-slider__bar) {
    display: none;
  }
  &:deep(.el-slider__runway) {
    height: 2px;
  }
  &:deep(.el-slider__button-wrapper) {
    top: -17px;
  }
  &:deep(.el-slider__marks-stop) {
    top: -5px;
    width: 12px;
@@ -2935,7 +2939,7 @@
.dialog-footer {
  float: right;
}
// 声音部分
.SoundArea {
  margin-top: 16px;
@@ -2959,7 +2963,7 @@
    height: 86%;
    margin: 10px 0;
    overflow-y: scroll;
    .SoundModelAreaBox {
      width: 100%;
      .SoundClassTit {
@@ -2971,27 +2975,16 @@
        display: flex;
        flex-wrap: wrap;
        align-content: flex-start;
        padding: 0 1%;
        box-sizing: border-box;
        > .ModealBox {
          width: 48%;
          margin: 5px 1%;
          width: 47%;
          margin: 10px 1%;
          display: flex;
          justify-content: space-around;
          align-items: center;
          position: relative;
          background-color: #F7F8FA;
          border-radius: 12px;
          padding: 10px 0;
          box-sizing: border-box;
          > .ImgBox {
            width: 40%;
            width: 26%;
            margin: 0 auto;
            border-radius: 50%;
            overflow: hidden;
            display: flex;
            justify-content: center;
            align-items: center;
            img {
              width: 100%;
            }
@@ -2999,7 +2992,7 @@
          > .TextArea {
            width: 48%;
            p {
              font-size: 14px;
              font-size: 12px;
              margin: 4px 0;
              padding-left: 6px;
              box-sizing: border-box;
@@ -3050,4 +3043,4 @@
    }
  }
}
</style>
</style>