| | |
| | | :model="formData" |
| | | :rules="formRules" |
| | | label-width="200px" |
| | | v-loading="formLoading" |
| | | v-loading="formLoading"> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <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="背景图片" 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-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> |
| | | </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-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> |
| | |
| | | </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> |
| | |
| | | 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() // 消息弹窗 |
| | |
| | | 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({ |
| | |
| | | } |
| | | } |
| | | 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 () => { |
| | | 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')) |
| | |
| | | } finally { |
| | | formLoading.value = false |
| | | } |
| | | }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> |