2025-05-30 | Flex | ![]() |
2025-05-30 | Flex | ![]() |
2025-05-30 | Flex | ![]() |
2025-05-30 | Flex | ![]() |
2025-05-30 | du | ![]() |
2025-05-30 | du | ![]() |
2025-05-30 | 康鲁杰 | ![]() |
2025-05-30 | 康鲁杰 | ![]() |
2025-05-30 | Flex | ![]() |
2025-05-30 | Flex | ![]() |
2025-05-30 | du | ![]() |
2025-05-30 | du | ![]() |
2025-05-30 | Flex | ![]() |
2025-05-30 | Flex | ![]() |
2025-05-29 | du | ![]() |
2025-05-29 | du | ![]() |
2025-05-29 | 康鲁杰 | ![]() |
2025-05-29 | 康鲁杰 | ![]() |
2025-05-29 | 康鲁杰 | ![]() |
2025-05-29 | du | ![]() |
2025-05-28 | du | ![]() |
2025-05-28 | shenrongliang | ![]() |
easegen-front/src/assets/imgs/bei3-1.png
easegen-front/src/assets/imgs/bei3.pngBinary files differ
easegen-front/src/assets/imgs/bei4.pngBinary files differ
easegen-front/src/views/Login/Login.vue
@@ -1,9 +1,11 @@ <template> <div class="bei"> <div class=""> <div class="BeiArea"> <!-- 左侧图片 --> <div class="Left-img" > <img src="@/assets/imgs/bei4.png" alt=""/> <div class="Left-img"> <div class="TitleText" > <text> 数字人 </text>智能交互平台 </div> </div> <!-- 右边的登录界面 --> <Transition appear enter-active-class="animate__animated animate__bounceInRight"> @@ -25,7 +27,6 @@ </template> <script lang="ts" setup> import { underlineToHump } from '@/utils' import { useDesign } from '@/hooks/web/useDesign' import { useAppStore } from '@/store/modules/app' import { ThemeSwitch } from '@/layout/components/ThemeSwitch' @@ -58,45 +59,74 @@ .bei{ width: 100%; height: 100%; background-image: url('@/assets/imgs/bei3.png'); background-size: 100% 100%; background-repeat: no-repeat; background-color: #000; display: flex; justify-content: center; align-items: center; } .form-box{ width: 25%; padding: 20px; .bei .BeiArea{ width: 86%; height: 95%; display: flex; justify-content: center; align-items: center; background-color: #000a25; } .bei .BeiArea .form-box{ /* width: 25%; */ width: 400px; margin-top: -36px; padding: 70px 30px; box-sizing: border-box; background: #fff; float: right; margin-top: 30vh; margin-right: 10vw; margin-left: 300px; box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px; } .Left-img{ float: left; width: 50%; margin-top: 4vh; margin-left: 60px; .bei .BeiArea .Left-img{ width: 729px; height: 655px; margin-top: 138px; margin-left: -74px; background-image:url( "@/assets/imgs/bei3-1.png" ); /* background-size: 100%; */ background-size: contain; background-repeat: no-repeat; background-position: center center; display: flex; justify-content: center; align-items: center; } .Left-img img{ .bei .BeiArea .Left-img .TitleText{ margin-top: calc( -85% ); margin-left: -60px; color: #fff; width: 100%; text-align: left; font-size: 48px; padding-left: 10%; box-sizing: border-box; } .bei .BeiArea .Left-img .TitleText text{ color: #2d84fa; } @media screen and ( max-width: 1300px ) { .form-box{ width: 80%; float: left; margin-left: 10%; margin-right: 0; margin-top: calc( 50vh - 100px ); width: 50% !important; margin: 0 auto !important; } .Left-img{ display: none; display: none !important; } } @media (max-width:1650px) and ( min-width: 1300px ) { .bei .BeiArea .Left-img{ margin-left: 0; } } </style> easegen-front/src/views/Login/components/LoginForm.vue
@@ -4,7 +4,7 @@ ref="formLogin" :model="loginData.loginForm" :rules="LoginRules" class="login-form" class="login-form NewClass" label-position="top" label-width="120px" size="large" @@ -59,7 +59,7 @@ </el-checkbox> </el-col> <el-col :offset="6" :span="12"> <el-link style="float: right" type="primary">{{ t('login.forgetPassword') }}</el-link> <!-- <el-link style="float: right" type="primary">{{ t('login.forgetPassword') }}</el-link> --> </el-col> </el-row> </el-form-item> @@ -72,6 +72,7 @@ class="w-[100%]" type="primary" @click="getCode()" style="background-color: #1d78f4" /> </el-form-item> </el-col> @@ -82,9 +83,10 @@ mode="pop" @success="handleLogin" /> <el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <!-- 注册不显示 --> <!-- <el-col :span="24" > <el-form-item> <el-row :gutter="5" justify="space-between" style="width: 100%"> <el-row :gutter="5" justify="space-between" style="width: 100%; margin-left: 0 !important;"> <el-col :span="12"> <XButton :title="t('login.btnMobile')" @@ -92,14 +94,14 @@ @click="setLoginState(LoginStateEnum.MOBILE)" /> </el-col> <!-- <el-col :span="8">--> <!-- <XButton--> <!-- :title="t('login.btnQRCode')"--> <!-- class="w-[100%]"--> <!-- @click="setLoginState(LoginStateEnum.QR_CODE)"--> <!-- />--> <!-- </el-col>--> <el-col :span="12"> <el-col :span="8"> <XButton :title="t('login.btnQRCode')" class="w-[100%]" @click="setLoginState(LoginStateEnum.QR_CODE)" /> </el-col> <el-col :span="24" style="padding-right: 10px; padding-left: 10px" > <XButton :title="t('login.btnRegister')" class="w-[100%]" @@ -108,38 +110,38 @@ </el-col> </el-row> </el-form-item> </el-col> <!-- <el-divider content-position="center">{{ t('login.otherLogin') }}</el-divider>--> <!-- <el-col :span="24" style="padding-right: 10px; padding-left: 10px">--> <!-- <el-form-item>--> <!-- <div class="w-[100%] flex justify-between">--> <!-- <Icon--> <!-- v-for="(item, key) in socialList"--> <!-- :key="key"--> <!-- :icon="item.icon"--> <!-- :size="30"--> <!-- class="anticon cursor-pointer"--> <!-- color="#999"--> <!-- @click="doSocialLogin(item.type)"--> <!-- />--> <!-- </div>--> <!-- </el-form-item>--> <!-- </el-col>--> <!-- <el-divider content-position="center">萌新必读</el-divider>--> <!-- <el-col :span="24" style="padding-right: 10px; padding-left: 10px">--> <!-- <el-form-item>--> <!-- <div class="w-[100%] flex justify-between">--> <!-- <el-link href="https://doc.iocoder.cn/" target="_blank">📚开发指南</el-link>--> <!-- <el-link href="https://doc.iocoder.cn/video/" target="_blank">🔥视频教程</el-link>--> <!-- <el-link href="https://www.iocoder.cn/Interview/good-collection/" target="_blank">--> <!-- ⚡面试手册--> <!-- </el-link>--> <!-- <el-link href="http://static.yudao.iocoder.cn/mp/Aix9975.jpeg" target="_blank">--> <!-- 🤝外包咨询--> <!-- </el-link>--> <!-- </div>--> <!-- </el-form-item>--> <!-- </el-col>--> </el-col> --> <!-- <el-divider content-position="center">{{ t('login.otherLogin') }}</el-divider>--> <!-- <el-col :span="24" style="padding-right: 10px; padding-left: 10px">--> <!-- <el-form-item>--> <!-- <div class="w-[100%] flex justify-between">--> <!-- <Icon--> <!-- v-for="(item, key) in socialList"--> <!-- :key="key"--> <!-- :icon="item.icon"--> <!-- :size="30"--> <!-- class="anticon cursor-pointer"--> <!-- color="#999"--> <!-- @click="doSocialLogin(item.type)"--> <!-- />--> <!-- </div>--> <!-- </el-form-item>--> <!-- </el-col>--> <!-- <el-divider content-position="center">萌新必读</el-divider>--> <!-- <el-col :span="24" style="padding-right: 10px; padding-left: 10px">--> <!-- <el-form-item>--> <!-- <div class="w-[100%] flex justify-between">--> <!-- <el-link href="https://doc.iocoder.cn/" target="_blank">📚开发指南</el-link>--> <!-- <el-link href="https://doc.iocoder.cn/video/" target="_blank">🔥视频教程</el-link>--> <!-- <el-link href="https://www.iocoder.cn/Interview/good-collection/" target="_blank">--> <!-- ⚡面试手册--> <!-- </el-link>--> <!-- <el-link href="http://static.yudao.iocoder.cn/mp/Aix9975.jpeg" target="_blank">--> <!-- 🤝外包咨询--> <!-- </el-link>--> <!-- </div>--> <!-- </el-form-item>--> <!-- </el-col>--> </el-row> </el-form> </template> @@ -352,4 +354,42 @@ cursor: pointer; } } .NewClass { ::v-deep(.el-input__inner) { font-size: 20px; line-height: 40px; height: 60px; } ::v-deep(.el-button--large) { padding: 20px; box-sizing: border-box; font-size: 20px; height: 60px; margin-top: 20px; } } @media screen and (max-width: 1300px) { .NewClass { ::v-deep(.el-input__inner) { font-size: 20px; line-height: 40px; height: 50px; } ::v-deep(.el-form-item--large){ margin-bottom: 40px; } ::v-deep(.el-checkbox__label){ font-size: 18px; } ::v-deep(.el-button--large) { padding: 20px; box-sizing: border-box; font-size: 20px; height: 60px; margin-top: 10px; } } } </style> easegen-front/src/views/Login/components/LoginFormTitle.vue
@@ -1,5 +1,12 @@ <style> .c717a8a{ color: #717a8a } </style> <template> <h2 class="enter-x mb-3 text-center text-2xl font-bold xl:text-center xl:text-3xl"> <h2 class="enter-x mb-3 text-2xl font-bold xl:text-3xl c717a8a" > {{ getFormTitle }} </h2> </template> easegen-front/src/views/chooseTemplate/index.vue
@@ -474,6 +474,7 @@ }" /> </div> <el-empty v-if="hostList.length==0" description="暂无数据" /> <Pagination small="true" :total="total" @@ -487,10 +488,25 @@ <div class="template-box template-right" v-if="showTemplateTool"> <div class="tabs-2"> </div> <div class="template-list"> <div class="tabs-1"> <div class="tabs-item" v-for="item in tabs4" :key="item.itemValue" @click="tabs4Click(item)" > <div>{{ item.itemName }}</div> <span v-if="tabs4ActiveNum == item.itemValue"></span> </div> </div> <div class="template-item" v-for="(template, index) in templates" :key="index" :style="{ width: '90%', maxWidth: '90%', }" @click="handleTemplateSelection(template)" > <div class="list-index" :style="template.isActive ? 'background: #409eff' : ''"> @@ -498,10 +514,11 @@ </div> <el-image class="background" :src="template.previewImage" fit="contain" /> </div> <el-empty v-if="templates.length==0" description="暂无数据" /> </div> <div class="apply-all"> <el-checkbox v-model="applyAllTemplate" :label="t('courseCenter.uploadAudio')" /> </div> <!-- <div class="apply-all">--> <!-- <el-checkbox v-model="applyAllTemplate" :label="t('courseCenter.uploadAudio')" />--> <!-- </div>--> </div> <!-- 背景设置 --> <div class="template-box template-right" v-if="showHeadImageTool"> @@ -751,10 +768,25 @@ itemValue: '2' } ] const tabs4 = [ { itemName: t('我的'), itemValue: '2' }, { itemName: t('模板库'), itemValue: '1' } ] const tabs4ActiveNum = ref('2') const tabs1Click = (item) => { tabs1ActiveNum.value = item.itemValue getList() } const tabs4Click = (item) => { tabs4ActiveNum.value = item.itemValue queryParams1.zg=tabs4ActiveNum.value getList1() } const tabs2Click = (item) => { @@ -780,18 +812,18 @@ activeUrl: userActive, isActive: false }, { name: t('courseCenter.background'), url: bg, activeUrl: bgActive, isActive: false }, { name: t('courseCenter.pictureInPicture'), url: innerPicture, activeUrl: innerPictureActive, isActive: false } // { // name: t('courseCenter.background'), // url: bg, // activeUrl: bgActive, // isActive: false // }, // { // name: t('courseCenter.pictureInPicture'), // url: innerPicture, // activeUrl: innerPictureActive, // isActive: false // } ]) const showHeadImageTool = ref(false) @@ -828,8 +860,11 @@ if (item.name == t('courseCenter.digitalPeople')) { tabs1ActiveNum.value = '0' getList() }else if (item.name == t('courseCenter.template')) { tabs4ActiveNum.value = '2' queryParams1.zg=tabs4ActiveNum.value getList1() } showHeadImageTool.value = item.name === t('courseCenter.background') showTemplateTool.value = item.name === t('courseCenter.template') showDigitalHumanTool.value = item.name === t('courseCenter.digitalPeople') @@ -1187,7 +1222,11 @@ gender: '', posture: '' }) const queryParams1 = reactive({ pageNo: 1, pageSize: 100, zg:'' }) const selectHost = ref(null) const getList = async () => { @@ -1198,16 +1237,6 @@ queryParams.posture = tabs3ActiveNum.value queryParams.status = 0 let data = await pptTemplateApi.pageList(queryParams) if (data.list.length == 0) { queryParams.type = tabs1ActiveNum.value == '0' ? '1' : '0' tabs1ActiveNum.value = queryParams.type data = await pptTemplateApi.pageList(queryParams) if (data.list.length == 0) { message.error('没有有效的数字人,请联系管理员') return } } data.list.forEach((item) => { item.isActive = false }) @@ -1858,9 +1887,22 @@ uploadFileObj.size = pageInfo ? pageInfo.docInfo.fileSize : '' } } const getList1 = async () => { const data = await TemplateApi.getTemplatePage(queryParams1) if (data) { TEMPLATE_PRESETS.value = data.list.map((item) => ({ ...item, showBackground: item.showBackground === 1, showDigitalHuman: item.showDigitalHuman === 1, showPpt: item.showPpt === 1 })) templates.value = TEMPLATE_PRESETS.value.map((template) => cloneDeep(template)) selectTemplate.value = cloneDeep(templates.value[0]) } } onMounted(async () => { let data = await TemplateApi.getTemplatePage(queryParams) let data = await TemplateApi.getTemplatePage(queryParams1) TEMPLATE_PRESETS.value = data.list.map((item) => ({ ...item, showBackground: item.showBackground === 1, @@ -2175,14 +2217,14 @@ border-bottom: 1px solid #ebeef5; .tabs-item { width: 30px; width: 50px; font-size: 14px; text-align: center; cursor: pointer; span { display: block; width: 30px; width: 50px; height: 2px; margin-top: 5px; background: #409eff; @@ -2328,7 +2370,7 @@ z-index: 1; width: 100%; height: 100%; background-color: #f0f1fa; //background-color: #f0f1fa; } .template-tool { easegen-front/src/views/dialogue/index.vue
@@ -1,43 +1,69 @@ <template> <div> <div class="remote-container"></div> </div> <div> <div class="remote-container" classNamev> </div> </div> </template> <script> import DUIX from 'duix-guiji-light' import {getDuixSign} from "../../api/dialogue"; import * as pptTemplateApi from "../../api/pptTemplate"; import DUIX from 'duix-guiji-light'; import {getDuixSign} from "../../api/dialogue/index.ts"; export default { name: "index" } const tempConversationId ='dev-'+ Math.random().toString(36).substring(2, 11); const duix = new DUIX() name: "index", mounted() { this.$nextTick(() => { this.initDuix(); }); }, methods: { getDuixSign().then(res => { if (res.code === 200) { duix.init({ sign: res.data.sign, containerLable: '.remote-container', conversationId: tempConversationId, }) } }) duix.on('getDuixSign', () => { // start session duix.start({ conversationId: tempConversationId, openAsr: true }).then(res => { console.info(res) }) }) async initDuix() { // 1. 检查容器是否存在 const container = document.querySelector('.remote-container'); if (!container) { console.error("错误:未找到 .remote-container 元素"); return; } // 2. 获取 Token let token; try { const res = await getDuixSign(); token = res.data?.sign || res.sign || res; if (!token) throw new Error("Token 为空"); } catch (err) { console.error("获取签名失败:", err); return; } // 3. 初始化 DUIX const duix = new DUIX(); const conversationId = 'dev-'+ Math.random().toString(36).substring(2, 11);; try { await duix.init({ sign: token, containerLable: '.remote-container', // 注意:可能是 containerLabel(检查拼写) conversationId: conversationId, }); // 4. 监听事件 duix.on('getDuixSign', () => { duix.start({conversationId, openAsr: true}) .then(() => console.log("DUIX 启动成功")) .catch(err => console.error("DUIX 启动失败:", err)); }); } catch (initErr) { console.error("DUIX 初始化失败:", initErr); } }, }, }; </script> <style scoped> .remote-container{ .remote-container { width: 500px; height: 500px; border: 1px solid red; /* 调试时高亮容器 */ } </style> easegen-front/src/views/digitalcourse/digitalhumans/LookDigitalHumansForm.vue
@@ -35,15 +35,15 @@ </el-form-item> <el-form-item label="背景样式" prop="isTransparent"> <el-select v-model="formData.isTransparent" placeholder="请选择是否去除背景"> <el-option value="1" label="透明背景"/> <el-option value="2" label="绿幕背景"/> <el-option :value="1" label="透明背景"/> <el-option :value="2" label="绿幕背景"/> </el-select> </el-form-item> <el-form-item v-if="formData.useModel == 1" :label="t('digitalhumans.picture')" prop="pictureUrl"> <UploadImg v-if="formData" v-model="formData.fixPictureUrl" /> <UploadImg v-else v-model="formData.pictureUrl" /> </el-form-item> <!-- <el-form-item v-if="formData.useModel == 2" :label="t('digitalhumans.video')" prop="videoUrl"> --> <el-form-item v-if="false" :label="t('digitalhumans.video')" prop="videoUrl"> <!-- 原本自带的视频上传 --> @@ -85,8 +85,9 @@ </el-form-item> </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading" :loading="isUploading" >{{t('common.ok')}}</el-button> <el-button @click="dialogVisible = false">{{t('common.cancel')}}</el-button> <el-button @click="submitForm" type="primary" :disabled="formLoading" :loading="isUploading" v-if="formType=='updata'">{{t('common.ok')}}</el-button> <el-button @click="dialogVisible = false" v-if="formType=='updata'">{{t('common.cancel')}}</el-button> <el-button @click="dialogVisible = false" v-if="formType=='detail'">关闭</el-button> </template> </Dialog> </template> @@ -100,10 +101,10 @@ import { useUpload } from '@/components/UploadFile/src/useUpload' import { el } from 'element-plus/es/locale'; import { any } from 'vue-types'; const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 const { uploadUrl, httpRequest } = useUpload() //上传方法 const dialogVisible = ref(false) // 弹窗的是否展示 const dialogTitle = ref('') // 弹窗的标题 @@ -128,10 +129,10 @@ status: undefined, isTransparent: undefined, }) // 当前是否正在上传视频 const isUploading = ref(false) const videoProperty = { videoUrl: '', posterUrl: '', @@ -143,7 +144,7 @@ height: 300 } } as DiyComponent<VideoPlayerProperty> watch(()=> formData.value.videoUrl,(newVal,oldValue)=>{ if (newVal && newVal.length > 0){ videoProperty.videoUrl = formData.value.fixVideoUrl || newVal @@ -164,12 +165,12 @@ videoUrl: [{ required: true, message: '视频不能为空', trigger: 'blur' }] }) const formRef = ref() // 表单 Ref const StartCes = () => { console.log( " ----- 开始 ----- " ) isUploading.value = true } const End = (res)=>{ const FileObject = { file:res @@ -180,9 +181,9 @@ } ).finally( res => { isUploading.value = false } ) } /** 打开弹窗 */ const open = async (type: string, id?: number) => { dialogVisible.value = true @@ -202,7 +203,7 @@ } } defineExpose({ open }) // 提供 open 方法,用于打开弹窗 /** 提交表单 */ const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 const submitForm = async () => { @@ -226,7 +227,7 @@ formLoading.value = false } } /** 重置表单 */ const resetForm = () => { formData.value = { @@ -254,9 +255,9 @@ formData.value.videoUrl = response.data; } }; // 修改默认的数字人视频模式为视频 const InitHumMODEL = ()=>{ let ModelList = getIntDictOptions( DICT_TYPE.USE_MODEL ) for (let index = 0; index < ModelList.length; index++) { @@ -266,6 +267,5 @@ } } } </script> easegen-front/src/views/digitalcourse/digitalhumans/index.vue
@@ -161,6 +161,23 @@ > {{t('digitalhumans.handle')}} </el-button> <el-button link type="primary" @click="OpenLookformRef('detail', scope.row.id)" v-hasPermi="['digitalcourse:digital-humans:delete']" > {{t('digitalhumans.view')}} </el-button> <el-button link type="primary" @click="OpenLookformRef('updata', scope.row.id)" v-hasPermi="['digitalcourse:digital-humans:delete']" > 修改 </el-button> <el-button :disabled="scope.row.status == 3 || scope.row.status == 6" link @@ -169,14 +186,6 @@ v-hasPermi="['digitalcourse:digital-humans:delete']" > {{ t('action.del') }} </el-button> <el-button link type="primary" @click="OpenLookformRef('detail', scope.row.id)" v-hasPermi="['digitalcourse:digital-humans:delete']" > {{t('digitalhumans.view')}} </el-button> </template> </el-table-column> easegen-front/src/views/digitalcourse/template/TemplateForm.vue
@@ -96,7 +96,6 @@ </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')" /> @@ -119,8 +118,16 @@ <el-input type="number" v-model="formData.humanY" :placeholder="t('common.inputText') + t('template.leftPositionDigitalPeople')" /> </el-form-item> </el-col> </el-row> <el-row v-if="ishasAdminRole==true"> <el-col :span="12"> <el-form-item label="模板类型" prop="zg"> <el-select v-model="formData.zg"> <el-option label="公用模板" :value="1">公用模板</el-option> <el-option label="我的模板" :value="2">我的模板</el-option> </el-select> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="12"> @@ -135,13 +142,6 @@ </el-col> </el-row> </el-form> <template #footer> <el-button @click="submitForm" type="primary" :disabled="formLoading">{{ t('common.ok') }}</el-button> @@ -152,6 +152,7 @@ <script setup lang="ts"> import { TemplateApi, TemplateVO } from '@/api/digitalcourse/template' import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict' import {getUserProfile} from "@/api/system/user/profile"; /** 模板 表单 */ defineOptions({ name: 'TemplateForm' }) @@ -192,15 +193,27 @@ humanH: [{ required: true, message: t('template.digitalPeopleHeight') + t('common.notEmpty'), trigger: 'blur' }], humanX: [{ required: true, message: t('template.topPositionDigitalPeople') + t('common.notEmpty'), trigger: 'blur' }], humanY: [{ required: true, message: t('template.leftPositionDigitalPeople') + t('common.notEmpty'), trigger: 'blur' }], zg: [{ required: true, message: '模板类型', trigger: 'blur' }], }) const formRef = ref() // 表单 Ref let ishasAdminRole = ref(false) let userInfo = ref() /** 打开弹窗 */ const open = async (type: string, id?: number) => { dialogVisible.value = true dialogTitle.value = t('action.' + type) formType.value = type //获取当前登录人的信息 userInfo.value = await getUserProfile() let hasAdminRole = userInfo.value.roles.some(role => role.name === '数字人管理员') console.log(hasAdminRole) resetForm() if (hasAdminRole) { ishasAdminRole=true }else { ishasAdminRole=false formData.value.zg = '2' } // 修改时,设置数据 if (id) { formLoading.value = true @@ -261,13 +274,14 @@ showPpt: undefined, pptW: undefined, pptH: undefined, pptX: undefined, pptY: undefined, bgImage: undefined, pptX: '40', pptY: '77', humanW: undefined, humanH: undefined, humanX: undefined, humanY: undefined, bgImage: undefined, humanX: '349', humanY: '92', zg:1, } formRef.value?.resetFields() } easegen-front/src/views/digitalcourse/template/index.vue
@@ -45,87 +45,180 @@ <!-- 列表 --> <ContentWrap> <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> <el-table-column :label="t('table.index')" align="center" type='index' width="60"/> <el-table-column :label="t('template.name')" align="center" prop="templateName" /> <el-table-column :label="t('template.isShowBackground')" align="center" prop="showBackground" > <template #default="scope"> <dict-tag :type="DICT_TYPE.IS_OR_NOT" :value="scope.row.showBackground" /> </template> </el-table-column> <el-table-column :label="t('template.isShowDigitalPeople')" align="center" prop="showDigitalHuman" > <template #default="scope"> <dict-tag :type="DICT_TYPE.IS_OR_NOT" :value="scope.row.showDigitalHuman" /> </template> </el-table-column> <el-table-column :label="t('template.isShowPPt')" align="center" prop="showPpt" > <template #default="scope"> <dict-tag :type="DICT_TYPE.IS_OR_NOT" :value="scope.row.showPpt" /> </template> </el-table-column> <!-- <el-table-column label="ppt宽" align="center" prop="pptW" /> <el-table-column label="ppt高" align="center" prop="pptH" /> <el-table-column label="ppt距离顶部位置" align="center" prop="pptX" /> <el-table-column label="ppt距离左侧位置" align="center" prop="pptY" /> <el-table-column label="数字人宽" align="center" prop="humanW" /> <el-table-column label="数字人高" align="center" prop="humanH" /> <el-table-column label="数字人距离顶部位置" align="center" prop="humanX" /> <el-table-column label="数字人距离左侧位置" align="center" prop="humanY" />--> <el-table-column label="背景图片" align="center" prop="bgImage"> <template #default="scope"> <el-image :src="scope.row.bgImage" :preview-src-list="[scope.row.bgImage]" fit="cover" preview-teleported <el-tabs v-model="activeName" class="demo-tabs" @click="iszg"> <el-tab-pane label="我的模板" name="first"> <el-table :default-sort="{ prop: 'createTime', order: 'descending' }" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> <el-table-column :label="t('table.index')" align="center" type='index' width="60"/> <el-table-column :label="t('template.name')" align="center" prop="templateName" /> <el-table-column :label="t('template.isShowBackground')" align="center" prop="showBackground" > <template #default="scope"> <dict-tag :type="DICT_TYPE.IS_OR_NOT" :value="scope.row.showBackground" /> </template> </el-table-column> <el-table-column :label="t('template.isShowDigitalPeople')" align="center" prop="showDigitalHuman" > <template #default="scope"> <dict-tag :type="DICT_TYPE.IS_OR_NOT" :value="scope.row.showDigitalHuman" /> </template> </el-table-column> <el-table-column :label="t('template.isShowPPt')" align="center" prop="showPpt" > <template #default="scope"> <dict-tag :type="DICT_TYPE.IS_OR_NOT" :value="scope.row.showPpt" /> </template> </el-table-column> <!-- <el-table-column label="ppt宽" align="center" prop="pptW" /> <el-table-column label="ppt高" align="center" prop="pptH" /> <el-table-column label="ppt距离顶部位置" align="center" prop="pptX" /> <el-table-column label="ppt距离左侧位置" align="center" prop="pptY" /> <el-table-column label="数字人宽" align="center" prop="humanW" /> <el-table-column label="数字人高" align="center" prop="humanH" /> <el-table-column label="数字人距离顶部位置" align="center" prop="humanX" /> <el-table-column label="数字人距离左侧位置" align="center" prop="humanY" />--> <el-table-column label="背景图片" align="center" prop="bgImage"> <template #default="scope"> <el-image :src="scope.row.bgImage" :preview-src-list="[scope.row.bgImage]" fit="cover" preview-teleported /> </template> </el-table-column> <el-table-column label="预览图片" align="center" prop="previewImage"> <template #default="scope"> <el-image :src="scope.row.previewImage" :preview-src-list="[scope.row.previewImage]" fit="cover" preview-teleported /> </template> </el-table-column> <el-table-column :label="t('table.createTime')" align="center" prop="createTime" sortable :formatter="dateFormatter" width="180px" /> </template> </el-table-column> <el-table-column label="预览图片" align="center" prop="previewImage"> <template #default="scope"> <el-image :src="scope.row.previewImage" :preview-src-list="[scope.row.previewImage]" fit="cover" preview-teleported <el-table-column label="创建人" align="center" prop="creator" /> <el-table-column :label="t('table.action')" align="center" min-width="120px"> <template #default="scope"> <el-button link type="primary" @click="openForm('update', scope.row.id)" v-hasPermi="['digitalcourse:template:update']" > {{ t('action.edit') }} </el-button> <el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['digitalcourse:template:delete']" > {{ t('action.del') }} </el-button> </template> </el-table-column> </el-table> <!-- 分页 --> <Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" @pagination="getList" /> </el-tab-pane> <el-tab-pane label="模板库模板" name="second"> <el-table :default-sort="{ prop: 'createTime', order: 'descending' }" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> <el-table-column :label="t('table.index')" align="center" type='index' width="60"/> <el-table-column :label="t('template.name')" align="center" prop="templateName" /> <el-table-column :label="t('template.isShowBackground')" align="center" prop="showBackground" > <template #default="scope"> <dict-tag :type="DICT_TYPE.IS_OR_NOT" :value="scope.row.showBackground" /> </template> </el-table-column> <el-table-column :label="t('template.isShowDigitalPeople')" align="center" prop="showDigitalHuman" > <template #default="scope"> <dict-tag :type="DICT_TYPE.IS_OR_NOT" :value="scope.row.showDigitalHuman" /> </template> </el-table-column> <el-table-column :label="t('template.isShowPPt')" align="center" prop="showPpt" > <template #default="scope"> <dict-tag :type="DICT_TYPE.IS_OR_NOT" :value="scope.row.showPpt" /> </template> </el-table-column> <!-- <el-table-column label="ppt宽" align="center" prop="pptW" /> <el-table-column label="ppt高" align="center" prop="pptH" /> <el-table-column label="ppt距离顶部位置" align="center" prop="pptX" /> <el-table-column label="ppt距离左侧位置" align="center" prop="pptY" /> <el-table-column label="数字人宽" align="center" prop="humanW" /> <el-table-column label="数字人高" align="center" prop="humanH" /> <el-table-column label="数字人距离顶部位置" align="center" prop="humanX" /> <el-table-column label="数字人距离左侧位置" align="center" prop="humanY" />--> <el-table-column label="背景图片" align="center" prop="bgImage"> <template #default="scope"> <el-image :src="scope.row.bgImage" :preview-src-list="[scope.row.bgImage]" fit="cover" preview-teleported /> </template> </el-table-column> <el-table-column label="预览图片" align="center" prop="previewImage"> <template #default="scope"> <el-image :src="scope.row.previewImage" :preview-src-list="[scope.row.previewImage]" fit="cover" preview-teleported /> </template> </el-table-column> <el-table-column :label="t('table.createTime')" align="center" prop="createTime" sortable :formatter="dateFormatter" width="180px" /> </template> </el-table-column> <el-table-column :label="t('table.createTime')" align="center" prop="createTime" :formatter="dateFormatter" width="180px" /> <el-table-column :label="t('table.action')" align="center" min-width="120px"> <template #default="scope"> <el-button link type="primary" @click="openForm('update', scope.row.id)" v-hasPermi="['digitalcourse:template:update']" > {{ t('action.edit') }} </el-button> <el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['digitalcourse:template:delete']" > {{ t('action.del') }} </el-button> </template> </el-table-column> </el-table> <!-- 分页 --> <Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" @pagination="getList" /> <el-table-column label="创建人" align="center" prop="creator" /> <el-table-column :label="t('table.action')" align="center" min-width="120px" v-if="hasAdminRole"> <template #default="scope"> <el-button link type="primary" @click="openForm('update', scope.row.id)" v-hasPermi="['digitalcourse:template:update']" > {{ t('action.edit') }} </el-button> <el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['digitalcourse:template:delete']" > {{ t('action.del') }} </el-button> </template> </el-table-column> </el-table> <!-- 分页 --> <Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" @pagination="getList" /> </el-tab-pane> </el-tabs> </ContentWrap> <!-- 表单弹窗:添加/修改 --> @@ -138,13 +231,14 @@ import { TemplateApi, TemplateVO } from '@/api/digitalcourse/template' import TemplateForm from './TemplateForm.vue' import {DICT_TYPE} from "@/utils/dict"; import type { TabsPaneContext } from 'element-plus' import {getUserProfile} from "@/api/system/user/profile"; /** 模板 列表 */ defineOptions({ name: 'Template' }) const message = useMessage() // 消息弹窗 const { t } = useI18n() // 国际化 const activeName = ref('first') const loading = ref(true) // 列表的加载中 const list = ref<TemplateVO[]>([]) // 列表的数据 const total = ref(0) // 列表的总页数 @@ -164,13 +258,18 @@ humanY: undefined, bgImage: undefined, createTime: [], zg: 2 }) const queryFormRef = ref() // 搜索的表单 const exportLoading = ref(false) // 导出的加载中 let userInfo = ref() let hasAdminRole = ref(false) /** 查询列表 */ const getList = async () => { loading.value = true userInfo.value = await getUserProfile() hasAdminRole = userInfo.value.roles.some(role => role.name === '数字人管理员') try { const data = await TemplateApi.getTemplatePage(queryParams) list.value = data.list @@ -189,6 +288,7 @@ /** 重置按钮操作 */ const resetQuery = () => { queryFormRef.value.resetFields() queryParams.zg = '2' handleQuery() } @@ -225,6 +325,16 @@ exportLoading.value = false } } const iszg = () => { console.log(activeName) if (activeName.value == 'first') { queryParams.zg = 2 getList() }else if (activeName.value == 'second') { queryParams.zg = 1 getList() } } /** 初始化 **/ onMounted(() => { yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/controller/admin/digitalhumans/vo/DigitalHumansRespVO.java
@@ -104,4 +104,5 @@ //过期时间 private Date expireDate; } private Integer isTransparent; } yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/controller/admin/template/TemplateController.java
@@ -92,4 +92,4 @@ BeanUtils.toBean(list, TemplateRespVO.class)); } } } yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/controller/admin/template/vo/TemplatePageReqVO.java
@@ -64,4 +64,5 @@ @Schema(description = "模板名称") private String templateName;; } private Integer zg; } yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/controller/admin/template/vo/TemplateRespVO.java
@@ -76,4 +76,6 @@ @Schema(description = "模板尺寸") private String templateSize; } private Integer zg; private String creator; } yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/controller/admin/template/vo/TemplateSaveReqVO.java
@@ -57,8 +57,8 @@ @NotNull(message = "数字人距离左侧位置不能为空") private BigDecimal humanY; @Schema(description = "背景图片", requiredMode = Schema.RequiredMode.REQUIRED) @NotEmpty(message = "背景图片不能为空") //@Schema(description = "背景图片", requiredMode = Schema.RequiredMode.REQUIRED) //@NotEmpty(message = "背景图片不能为空") private String bgImage; @Schema(description = "效果图") @@ -70,4 +70,5 @@ @Schema(description = "模板尺寸") private String templateSize; } private Integer zg; } yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/dal/dataobject/template/TemplateDO.java
@@ -86,4 +86,5 @@ // 模板尺寸 private String templateSize; } private Integer zg; } yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/dal/mysql/template/TemplateMapper.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; import cn.iocoder.yudao.module.digitalcourse.dal.dataobject.template.TemplateDO; import org.apache.ibatis.annotations.Mapper; import cn.iocoder.yudao.module.digitalcourse.controller.admin.template.vo.*; @@ -18,7 +19,8 @@ public interface TemplateMapper extends BaseMapperX<TemplateDO> { default PageResult<TemplateDO> selectPage(TemplatePageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX<TemplateDO>() Long loginUserId = WebFrameworkUtils.getLoginUserId(); LambdaQueryWrapperX<TemplateDO> wrapper = new LambdaQueryWrapperX<TemplateDO>() .eqIfPresent(TemplateDO::getShowBackground, reqVO.getShowBackground()) .eqIfPresent(TemplateDO::getShowDigitalHuman, reqVO.getShowDigitalHuman()) .eqIfPresent(TemplateDO::getShowPpt, reqVO.getShowPpt()) @@ -31,8 +33,34 @@ .eqIfPresent(TemplateDO::getHumanX, reqVO.getHumanX()) .eqIfPresent(TemplateDO::getHumanY, reqVO.getHumanY()) .eqIfPresent(TemplateDO::getBgImage, reqVO.getBgImage()) .betweenIfPresent(TemplateDO::getCreateTime, reqVO.getCreateTime()) .orderByAsc(TemplateDO::getId)); .betweenIfPresent(TemplateDO::getCreateTime, reqVO.getCreateTime()); Integer zgParam = reqVO.getZg(); if (loginUserId != 1) { if (zgParam != null) { if (zgParam == 1) { wrapper.eq(TemplateDO::getZg, 1); } else if (zgParam == 2) { wrapper.eq(TemplateDO::getZg, 2).eq(TemplateDO::getCreator, loginUserId); } } else { // zg 未传,查 zg = 1 或 (zg = 2 且 creator = 自己) wrapper.and(w -> w.eq(TemplateDO::getZg, 1) .or() .eq(TemplateDO::getZg, 2).eq(TemplateDO::getCreator, loginUserId)); } }else { if (zgParam != null) { if (zgParam == 1) { wrapper.eq(TemplateDO::getZg, 1); } else if (zgParam == 2) { wrapper.eq(TemplateDO::getZg, 2); } } } wrapper.orderByAsc(TemplateDO::getId); return selectPage(reqVO, wrapper); } } } yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceUtil.java
@@ -170,10 +170,10 @@ // 当没有人像时,视频放在 cover 的下层 builder = new ProcessBuilder( "ffmpeg", "-i", cover1, // 背景图 1 "-i", substring1, // 视频(含音频) "-i", cover1, // 背景图 2 "-i", cover, // PPT 内容 "-loop", "1", "-i", cover1, // 背景图1(动态持续) "-i", substring1, // 视频(含音频) "-loop", "1", "-i", cover1, // 背景图2(动态持续) "-loop", "1", "-i", cover, // PPT 内容(动态持续) "-filter_complex", "[0:v]scale=" + Math.round(scene.getBackground().getWidth()) + ":" + Math.round(scene.getBackground().getHeight()) + "[bg1];" + "[1:v]scale=" + Math.round(scene.getComponents().get(0).getWidth()) + ":" + Math.round(scene.getComponents().get(0).getHeight()) + "[v1];" + yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/digitalhumans/DigitalHumansServiceImpl.java
@@ -137,7 +137,7 @@ } @Override public PageResult<DigitalHumansDO> getDigitalHumansPage(DigitalHumansPageReqVO pageReqVO) { if(pageReqVO.getType()==1){ if(pageReqVO.getType() != null && pageReqVO.getType()==1){ //查询非公共数字人,只能查询自己的,公共数字人,可以查询所有的 if (WebFrameworkUtils.getLoginUserId() != 1) pageReqVO.setCreator(String.valueOf(WebFrameworkUtils.getLoginUserId())); } yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/template/TemplateServiceImpl.java
@@ -1,5 +1,8 @@ package cn.iocoder.yudao.module.digitalcourse.service.template; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import jakarta.annotation.Resource; import org.springframework.validation.annotation.Validated; @@ -31,6 +34,10 @@ @Override public Long createTemplate(TemplateSaveReqVO createReqVO) { Integer zg = createReqVO.getZg(); if (zg != null && zg != 1) { createReqVO.setZg(2); } // 插入 TemplateDO template = BeanUtils.toBean(createReqVO, TemplateDO.class); templateMapper.insert(template); @@ -66,9 +73,16 @@ return templateMapper.selectById(id); } @Autowired private AdminUserApi adminUserApi; @Override public PageResult<TemplateDO> getTemplatePage(TemplatePageReqVO pageReqVO) { return templateMapper.selectPage(pageReqVO); PageResult<TemplateDO> templateDOPageResult = templateMapper.selectPage(pageReqVO); for (TemplateDO templateDO : templateDOPageResult.getList()) { AdminUserRespDTO user = adminUserApi.getUser(Long.valueOf(templateDO.getCreator())); templateDO.setCreator(user.getNickname()); } return templateDOPageResult; } } } yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/util/PPTUtil.java
@@ -193,7 +193,7 @@ File pdfFile = File.createTempFile("ppt_to_pdf_"+tempFileName, ".pdf"); String command; if (isWindows()) { command = String.format("\"E:\\LibreOffice\\LibreOffice\\program\\soffice.exe\" --headless --convert-to pdf --outdir %s %s", pdfFile.getParent(), pptFile.getAbsolutePath()); command = String.format("\"C:\\Program Files\\LibreOffice\\program\\soffice.com\" --headless --convert-to pdf --outdir %s %s", pdfFile.getParent(), pptFile.getAbsolutePath()); } else { command = String.format("libreoffice --headless --convert-to pdf --outdir %s %s", pdfFile.getParent(), pptFile.getAbsolutePath()); }