du
2025-06-10 09cd9c30b4ddf4b29365c2d8693b2a2f0726552d
模板中心的新增弹窗的修改
已修改3个文件
已添加2个文件
478 ■■■■■ 文件已修改
easegen-front/package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
easegen-front/src/assets/imgs/1.png 补丁 | 查看 | 原始文档 | blame | 历史
easegen-front/src/assets/imgs/2.png 补丁 | 查看 | 原始文档 | blame | 历史
easegen-front/src/views/digitalcourse/digitalhumans/AuditForm.vue 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
easegen-front/src/views/digitalcourse/template/TemplateForm.vue 459 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
easegen-front/package.json
@@ -57,6 +57,7 @@
    "face-api.js": "^0.22.2",
    "fast-xml-parser": "^4.3.2",
    "highlight.js": "^11.9.0",
    "html2canvas": "^1.4.1",
    "jsencrypt": "^3.3.2",
    "lodash-es": "^4.17.21",
    "markdown-it": "^14.1.0",
easegen-front/src/assets/imgs/1.png
easegen-front/src/assets/imgs/2.png
easegen-front/src/views/digitalcourse/digitalhumans/AuditForm.vue
@@ -18,8 +18,8 @@
      </el-form-item>
      <el-form-item v-if="(formData.useModel == 2 || formData.useModel == 3) && !(formData.videoUrl || formData.fixVideoUrl) " :label="t('digitalhumans.video')" prop="videoUrl">
        <UploadFile v-if="!(formData.videoUrl || formData.fixVideoUrl)" v-model="formData.videoUrl" :fileType="['mp4']" :limit="1" @on-success="handleFileSuccess('fixVideoUrl', $event)"/>
        <!-- 播放mov视频 -->
         <!-- 下面的是原本的 -->
@@ -63,11 +63,11 @@
            </el-input>
            <!-- 保留原有的上传功能 -->
            <div class="text-gray-500 text-sm">{{ t('digitalhumans.orUploadVideo') }}</div>
            <UploadFile
              v-model="formData.fixVideoUrl"
              :fileType="['mp4','mov']"
              :limit="1"
              :file-size="1024"
            <UploadFile
              v-model="formData.fixVideoUrl"
              :fileType="['mp4','mov']"
              :limit="1"
              :file-size="1024"
              @on-success="handleFileSuccess('fixVideoUrl', $event)"
            />
          </div>
@@ -181,7 +181,7 @@
    message.warning(t('digitalhumans.pleaseInputVideoUrl'))
    return
  }
  // 验证URL格式
  try {
    new URL(formData.value.fixVideoUrl)
@@ -216,7 +216,7 @@
      message.success(t('common.createSuccess'))
    } else {
      await DigitalHumansApi.updateDigitalHumans(data)
      message.success(t('common.updateSuccess'))
      message.success('开始受理')
    }
    dialogVisible.value = false
    // 发送操作成功的事件
easegen-front/src/views/digitalcourse/template/TemplateForm.vue
@@ -5,117 +5,34 @@
      :model="formData"
      :rules="formRules"
      label-width="200px"
      v-loading="formLoading"
    >
      v-loading="formLoading">
      <el-row>
        <el-col :span="12">
          <el-form-item :label="t('template.name')" prop="templateName">
            <el-input v-model="formData.templateName" maxlength="50" :placeholder="t('common.inputText') + t('template.name')" />
          <el-form-item label="模板名称" prop="templateName">
            <el-input v-model="formData.templateName" maxlength="50" placeholder="请输入模板名称" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item :label="t('template.isShowBackground')" prop="showBackground">
            <el-select v-model="formData.showBackground" :placeholder="t('common.selectText') + t('template.isShowBackground')">
              <el-option
                v-for="dict in getIntDictOptions(DICT_TYPE.IS_OR_NOT)"
                :key="dict.value"
                :label="dict.label"
                :value="dict.value"
              />
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item :label="t('template.isShowDigitalPeople')" prop="showDigitalHuman">
            <el-select v-model="formData.showDigitalHuman" :placeholder="t('common.selectText') + t('template.isShowDigitalPeople')">
              <el-option
                v-for="dict in getIntDictOptions(DICT_TYPE.IS_OR_NOT)"
                :key="dict.value"
                :label="dict.label"
                :value="dict.value"
              />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item :label="t('template.isShowPPt')" prop="showPpt">
            <el-select v-model="formData.showPpt" :placeholder="t('common.selectText') + t('template.isShowPPt')">
              <el-option
                v-for="dict in getIntDictOptions(DICT_TYPE.IS_OR_NOT)"
                :key="dict.value"
                :label="dict.label"
                :value="dict.value"
              />
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="24">
          <el-form-item :label="t('template.templateSize')" prop="templateSize">
            <el-select
              style="width: 100%"
              v-model="formData.templateSize"
              clearable
              :placeholder="t('common.selectText') + t('template.templateSize')"
              class="!w-240px"
              @change="changeTemplateSize"
          <el-form-item label="背景图片" prop="templateName">
            <el-upload
              v-model:file-list="fileList"
              class="upload-demo"
              :action="getUploadUrl"
              :auto-upload="false"
              :limit="2"
              :on-exceed="handleExceed"
              :before-upload="beforeUpload"
              :on-change="handleChange"
              accept="image/*"
              :show-file-list="false"
            >
              <el-option
                v-for="dict in getStrDictOptions(DICT_TYPE.TEMPLATE_SIZE)"
                :key="dict.value"
                :label="dict.label"
                :value="dict.value"
              />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item :label="t('template.pptWidth')" prop="pptW">
            <el-input type="number" v-model="formData.pptW" :placeholder="t('common.inputText') + t('template.pptWidth')" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item :label="t('template.pptHeight')" prop="pptH">
            <el-input type="number" v-model="formData.pptH" :placeholder="t('common.inputText') + t('template.pptHeight')" />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item :label="t('template.leftPositionPPT')" prop="pptX">
            <el-input type="number" v-model="formData.pptX" :placeholder="t('common.inputText') + t('template.topPositionPPT')" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item  :label="t('template.topPositionPPT')"  prop="pptY">
            <el-input type="number" v-model="formData.pptY" :placeholder="t('common.inputText') + t('template.leftPositionPPT')" />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item :label="t('template.digitalPeopleWidth')" prop="humanW">
            <el-input type="number" disabled v-model="formData.humanW" :placeholder="t('common.inputText') + t('template.digitalPeopleWidth')" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item :label="t('template.digitalPeopleHeight')" prop="humanH">
            <el-input type="number" disabled v-model="formData.humanH" :placeholder="t('common.inputText') + t('template.digitalPeopleHeight')" />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item  :label="t('template.leftPositionDigitalPeople')" prop="humanX">
            <el-input type="number" v-model="formData.humanX" :placeholder="t('common.inputText') + t('template.topPositionDigitalPeople')" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item :label="t('template.topPositionDigitalPeople')" prop="humanY">
            <el-input type="number" v-model="formData.humanY" :placeholder="t('common.inputText') + t('template.leftPositionDigitalPeople')" />
              <el-button type="primary">上传图片</el-button>
              <template #tip>
                <div class="el-upload__tip">
                  只能上传jpg/png文件
                </div>
              </template>
            </el-upload>
          </el-form-item>
        </el-col>
      </el-row>
@@ -130,18 +47,72 @@
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item :label="t('template.backgroundImage')" prop="bgImage">
            <UploadImg v-model="formData.bgImage" />
          </el-form-item>
        <el-col :span="18">
          <div ref="captureElement" style="width: 802px; height: 452px;border: 1px solid #C0C0C0;box-sizing: border-box;position: relative">
            <img :src="lastUploadedFileUrl" style="width: 100%; height: 100%" v-if="lastUploadedFileUrl!=''" />
            <Vue3DraggableResizable
              v-if="isChecked1"
              :w="640"
              :h="360"
              :x="349"
              :y="92"
              v-model:w="formData.humanW"
              v-model:h="formData.humanH"
              v-model:x="formData.humanX"
              v-model:y="formData.humanY"
              :lock-aspect-ratio="true"
              :minW="350"
            >
              <img
                src="@/assets/imgs/1.png"
                style="width: 100%; height: 100%; object-fit: contain;"
              />
            </Vue3DraggableResizable>
            <Vue3DraggableResizable
              v-if="isChecked"
              :w="505"
              :h="290"
              :x="40"
              :y="50"
              v-model:w="formData.pptW"
              v-model:h="formData.pptH"
              v-model:x="formData.pptX"
              v-model:y="formData.pptY"
              :lock-aspect-ratio="false"
              :parent="true"
              :minW="350"
            >
              <img
                src="@/assets/imgs/2.png"
                style="width: 100%; height: 100%; object-fit: cover;"
              />
            </Vue3DraggableResizable>
          </div>
        </el-col>
        <el-col :span="12">
          <el-form-item :label="t('template.reviewImage')" prop="previewImage">
            <UploadImg v-model="formData.previewImage" />
          </el-form-item>
        <el-col :span="6">
          <div style="width: 100%;height: 452px;border: 1px solid #C0C0C0;box-sizing: border-box">
            <div class="image-checkbox-wrapper" @click="toggleCheck">
              <img src="@/assets/imgs/2.png" alt="ppt示例图片" class="checkbox-image" />
              <input
                type="checkbox"
                v-model="isChecked"
                class="checkbox-input"
                @click.stop
              />
            </div>
            <div class="image-checkbox-wrapper" @click="toggleCheck1">
              <img src="@/assets/imgs/1.png" alt="数字人示例图片" class="checkbox-image" />
              <input
                type="checkbox"
                v-model="isChecked1"
                class="checkbox-input"
                @click.stop
              />
            </div>
          </div>
        </el-col>
      </el-row>
    </el-form>
    <template #footer>
      <el-button @click="submitForm" type="primary" :disabled="formLoading">{{ t('common.ok') }}</el-button>
@@ -153,8 +124,77 @@
import { TemplateApi, TemplateVO } from '@/api/digitalcourse/template'
import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
import {getUserProfile} from "@/api/system/user/profile";
import html2canvas from 'html2canvas';
/** 模板 表单 */
defineOptions({ name: 'TemplateForm' })
import { ElMessage } from 'element-plus';
import {updateFile} from "@/api/infra/file";
import Vue3DraggableResizable from 'vue3-draggable-resizable'
import 'vue3-draggable-resizable/dist/Vue3DraggableResizable.css'
const getUploadUrl = import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/infra/file/upload'
const fileList = ref([]);
const lastUploadedFileUrl = ref('');
const handleExceed = () => {
  ElMessage.warning('只能上传一个文件');
};
const isChecked = ref(false);
const isChecked1 = ref(false);
const toggleCheck = () => {
  isChecked.value = !isChecked.value;
  if (isChecked.value==true) {
    formData.value.showPpt=1
  }else if (isChecked.value==false) {
    formData.value.showPpt=0
  }
};
const toggleCheck1 = () => {
  isChecked1.value =!isChecked1.value;
  if (isChecked1.value==true) {
    formData.value.showDigitalHuman=1
  }else if (isChecked1.value==false) {
    formData.value.showDigitalHuman=0
  }
}
const beforeUpload = (file) => {
  const isImage = file.type.startsWith('image/');
  const isLt2M = file.size / 1024 / 1024 < 2;
  if (!isImage) {
    ElMessage.error('只能上传图片格式的文件!');
    return false;
  }
  if (!isLt2M) {
    ElMessage.error('图片大小不能超过2MB!');
    return false;
  }
  return true;
};
async function updataImage(formData1) {
  const response= await updateFile(formData1)
  console.log(response.data)
  if (response) {
    formData.value.bgImage=response.data
    ElMessage.success('上传成功');
  }
}
const handleChange = (file, files) => {
  // 当文件变化时,只保留最后一个文件
  if (files.length > 1) {
    fileList.value = [files[files.length - 1]];
  }
  console.log(file)
  const raw=file.raw
  console.log(raw)
  lastUploadedFileUrl.value = URL.createObjectURL(raw);
  console.log(lastUploadedFileUrl.value)
  const formData1 = new FormData();
  formData1.append('file', raw);
  updataImage(formData1)
};
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
@@ -165,18 +205,18 @@
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
  id: undefined,
  showBackground: undefined,
  showBackground: 1,
  templateName: undefined,
  showDigitalHuman: undefined,
  showPpt: undefined,
  pptW: undefined,
  pptH: undefined,
  pptX: '40',
  pptY: '77',
  humanW: undefined,
  humanH: undefined,
  humanX: '349',
  humanY: '92',
  pptW: 505,
  pptH: 290,
  pptX: 40,
  pptY: 50,
  humanW: 640,
  humanH: 360,
  humanX: 349,
  humanY: 92,
  bgImage: undefined,
})
const formRules = reactive({
@@ -225,64 +265,155 @@
  }
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
const changeTemplateSize = () => {
  console.log(formData.value.templateSize)
  const screenWidth = window.screen.width;
  const screenHeight = window.screen.height;
  if(formData.value.templateSize=='16:9'){
    formData.value.humanW = screenWidth / 3;
    formData.value.humanH = screenHeight / 3;
    formData.value.pptW = '505';
    formData.value.pptH = '290';
  }else if(formData.value.templateSize=='9:16'){
    formData.value.humanH = screenWidth / 3;
    formData.value.humanW = screenHeight / 3;
    formData.value.pptH = '505';
    formData.value.pptW = '290';
  }
}
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
  // 校验表单
  await formRef.value.validate()
  // 提交请求
  formLoading.value = true
  try {
    const data = formData.value as unknown as TemplateVO
    if (formType.value === 'create') {
      await TemplateApi.createTemplate(data)
      message.success(t('common.createSuccess'))
    } else {
      await TemplateApi.updateTemplate(data)
      message.success(t('common.updateSuccess'))
  const imageFile = await saveAsImage();
  if (imageFile) {
    const formData1 = new FormData();
    formData1.append('file', imageFile);
    const response = await updateFile(formData1);
    formData.value.previewImage = response.data;
    // 校验表单
    await formRef.value.validate()
    // 提交请求
    formLoading.value = true
    try {
      formData.value.humanX=formData.value.humanX.toString()
      formData.value.humanY=formData.value.humanY.toString()
      formData.value.pptX=formData.value.pptX.toString()
      formData.value.pptY=formData.value.pptY.toString()
      formData.value.pptW=formData.value.pptW.toString()
      formData.value.pptH=formData.value.pptH.toString()
      const data = formData.value as unknown as TemplateVO
      console.log(formData.value)
      if (formType.value === 'create') {
        await TemplateApi.createTemplate(data)
        message.success(t('common.createSuccess'))
      } else {
        await TemplateApi.updateTemplate(data)
        message.success(t('common.updateSuccess'))
      }
      dialogVisible.value = false
      // 发送操作成功的事件
      emit('success')
    } finally {
      formLoading.value = false
    }
    dialogVisible.value = false
    // 发送操作成功的事件
    emit('success')
  } finally {
  }else {
    ElMessage.error('保存截图失败');
    formLoading.value = false
  }
}
/** 重置表单 */
const resetForm = () => {
  formData.value = {
    id: undefined,
    showBackground: undefined,
    showBackground: 1,
    showDigitalHuman: undefined,
    showPpt: undefined,
    pptW: undefined,
    pptH: undefined,
    pptW: 505,
    pptH: 290,
    pptX: 40,
    pptY: 50,
    humanW: 640,
    humanH: 360,
    humanX: 349,
    humanY: 92,
    bgImage: undefined,
    pptX: '40',
    pptY: '77',
    humanW: undefined,
    humanH: undefined,
    humanX: '349',
    humanY: '92',
    zg:1,
    templateSize: '16:9',
  }
  formRef.value?.resetFields()
}
const captureElement = ref<HTMLElement | null>(null);
const saveAsImage = async () => {
  if (!captureElement.value) {
    console.error('DOM 元素未找到!');
    return;
  }
  try {
    const canvas = await html2canvas(captureElement.value, {
      backgroundColor: null,
      scale: 1,
      useCORS: true,
    });
    return new Promise((resolve) => {
      canvas.toBlob((blob) => {
        const file = new File([blob], 'screenshot.png', {
          type: 'image/png',
        });
        resolve(file);
      }, 'image/png');
    });
  }
  catch (error) {
    console.error('生成图片失败:', error);
    return null;
  }
}
onMounted(() => {
  console.log('DOM 已渲染:', captureElement.value);
});
</script>
<style scoped lang="scss">
.image-checkbox-container {
  display: inline-block;
  position: relative;
}
.image-checkbox-wrapper {
  position: relative;
  cursor: pointer;
}
.checkbox-image {
  width: 230px;
  height: 150px;
  object-fit: cover;
  border-radius: 4px;
  border: 1px solid #ddd;
  transition: all 0.3s;
  margin-left: 20px;
  margin-top: 20px;
}
.checkbox-input {
  position: absolute;
  top: 30px;
  left: 30px;
  width: 20px;
  height: 20px;
  cursor: pointer;
  z-index: 2;
  appearance: none; /* 隐藏默认样式 */
  -webkit-appearance: none;
  background-color: #fff;
  border: 1px solid #ddd;
  border-radius: 4px;
}
/* 选中状态样式 */
.checkbox-input:checked {
  background-color: #409eff; /* 蓝色背景 */
  border-color: #409eff; /* 蓝色边框 */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 14px;
}
.image-checkbox-wrapper:hover .checkbox-image {
  border-color: #ddd;
}
.checkbox-input:checked ~ .checkbox-image {
  border: 1px solid #ddd; /* 选中时保持灰色1px边框 */
}
</style>