Merge branch 'main' into zouyf_dev
This commit is contained in:
commit
66853327cd
|
@ -11,15 +11,21 @@
|
||||||
:default-expanded-keys="defaultExpandedKeys" :current-node-key="curNode.data.id" highlight-current
|
:default-expanded-keys="defaultExpandedKeys" :current-node-key="curNode.data.id" highlight-current
|
||||||
@node-click="handleNodeClick">
|
@node-click="handleNodeClick">
|
||||||
<template #default="{ node, data }">
|
<template #default="{ node, data }">
|
||||||
<div v-if="props.isClassTask && (data.bookId == '' || data.bookId == '0')">
|
<div v-if="props.isClassTask && (data.bookId == '' || data.bookId == '0')" class="tree-label-wrap">
|
||||||
<el-tooltip effect="light" placement="right" content="该单元章节无自主试题">
|
<el-tooltip effect="light" placement="right" >
|
||||||
<span class="tree-label" style="color: #A5B3CA">
|
<template #content> {{ node.label }}<br /><span style="color: red;">-该单元章节无自主试题-</span> </template>
|
||||||
|
<span class="tree-label" style="color: #A5B3CA" >
|
||||||
{{ node.label }}
|
{{ node.label }}
|
||||||
</span>
|
</span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else class="tree-label-wrap">
|
||||||
<span class="tree-label">{{ node.label }}</span>
|
<el-tooltip effect="light" placement="right" >
|
||||||
|
<template #content> {{ node.label }}</template>
|
||||||
|
<span class="tree-label">
|
||||||
|
{{ node.label }}
|
||||||
|
</span>
|
||||||
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-tree>
|
</el-tree>
|
||||||
|
@ -334,7 +340,7 @@ onMounted( async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tree-label {
|
.tree-label-wrap, .tree-label {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
|
@ -107,9 +107,13 @@ const getBackGroundV2 = async () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const createOutlineV2 = async (data) => {
|
const createOutlineV2 = async (params) => {
|
||||||
try {
|
try {
|
||||||
const response = await req("/api/aipptV2/createOutlineV2", "POST", data);
|
const response = await request({
|
||||||
|
url:"/api/aipptV2/createOutlineV2",
|
||||||
|
method: "POST",
|
||||||
|
params
|
||||||
|
});
|
||||||
console.log("createOutline response:", response);
|
console.log("createOutline response:", response);
|
||||||
|
|
||||||
return response.data;
|
return response.data;
|
||||||
|
|
|
@ -680,6 +680,7 @@ onMounted(async () => {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
|
padding: 10px;
|
||||||
.content-body-right-item{
|
.content-body-right-item{
|
||||||
&:hover{
|
&:hover{
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
@ -1,30 +1,120 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-dialog class="ppt-dialog" v-model="model" :show-close="false" width="800" destroy-on-close :top="'3vh'">
|
<el-dialog class="ppt-dialog" v-model="model" title="生成PPT(试验版)"
|
||||||
<template #header="{ close, titleId, titleClass }">
|
:close-on-click-modal="false"
|
||||||
<div class="dialog-header">
|
:close-on-press-escape="false"
|
||||||
<h4 :id="titleId" :class="titleClass">生成PPT(试验版)</h4>
|
:show-close="false" width="1000" destroy-on-close >
|
||||||
<i class="iconfont icon-guanbi" @click="close"></i>
|
<!-- <AiPptist @add-success="addAiPPT" :dataList="dataList"/>-->
|
||||||
|
<div class="ppt-dialog-content">
|
||||||
|
<div v-if="activeStep === 1" class="ppt-dialog-cover-wrap">
|
||||||
|
<div v-for="(item,index) in backGroundList"
|
||||||
|
:key="item.templateIndexId"
|
||||||
|
@click="outlineData.templateId = item.templateIndexId"
|
||||||
|
:class="outlineData.templateId === item.templateIndexId?'active-mode':''"
|
||||||
|
class="ppt-dialog-cover-item">
|
||||||
|
<div class="icon-select">
|
||||||
|
<Select />
|
||||||
|
</div>
|
||||||
|
<img :src="getBackGroundImg(item.detailImage)">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<div class="ppt-dialog-prog-wrap" v-if="activeStep === 2">
|
||||||
<AiPptist @add-success="addAiPPT" :dataList="dataList"/>
|
<el-progress :percentage="percentage" type="circle"></el-progress>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ppt-dialog-footer" v-if="activeStep === 1">
|
||||||
|
<el-button @click="closeDialog">关闭</el-button>
|
||||||
|
<el-button type="primary" :loading="createPPTLoading" @click="createPPT">生成课件</el-button>
|
||||||
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import AiPptist from './ai-pptistV2.vue';
|
import AiPptist from './ai-pptistV2.vue';
|
||||||
|
import {Select} from "@element-plus/icons-vue";
|
||||||
|
import {ref, defineEmits, onMounted} from "vue";
|
||||||
|
import {createPPTV2, getBackGroundV2, getProgressV2} from "@/utils/ppt-request";
|
||||||
|
import {ElMessage} from "element-plus";
|
||||||
const model = defineModel()
|
const model = defineModel()
|
||||||
const emit = defineEmits(['addSuccess'])
|
const emit = defineEmits(['addSuccess', 'close-dialogs'])
|
||||||
|
const backGroundList = ref([]);
|
||||||
|
const activeStep = ref(1);
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
dataList: {
|
dataList: {
|
||||||
type: Array,
|
type: Object,
|
||||||
default: () => []
|
default: () => {}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const closeDialog = () => {
|
||||||
|
emit('close-dialogs')
|
||||||
|
}
|
||||||
const addAiPPT = (data) => {
|
const addAiPPT = (data) => {
|
||||||
emit('addSuccess', data)
|
emit('addSuccess', data)
|
||||||
}
|
}
|
||||||
|
const getBackGroundImg = (imgUrlStr) => {
|
||||||
|
return JSON.parse(imgUrlStr).titleCoverImage
|
||||||
|
};
|
||||||
|
|
||||||
|
const createPPTLoading = ref(false);
|
||||||
|
const outlineData = ref({
|
||||||
|
query: '', // 用户要求(最多8000字)
|
||||||
|
templateId: '', // ppt生成主题
|
||||||
|
author: 'AIX平台',
|
||||||
|
isFigure: false, // 是否自动配图
|
||||||
|
search: true,
|
||||||
|
language: "cn"
|
||||||
|
})
|
||||||
|
const percentage = ref(0);
|
||||||
|
const outlineCreatePPT = () => {
|
||||||
|
const newOutlineData = { ...outlineData.value, };
|
||||||
|
newOutlineData.query = props.dataList.outline;
|
||||||
|
createPPTLoading.value = true;
|
||||||
|
createPPTV2(newOutlineData).then((res) => {
|
||||||
|
console.log(res, "正在生成中");
|
||||||
|
createPPTLoading.value = false;
|
||||||
|
activeStep.value = 2
|
||||||
|
|
||||||
|
const checkProgress = () => {
|
||||||
|
getProgressV2(res.sid).then(response => {
|
||||||
|
percentage.value = Math.round(response?.donePages*100/response?.totalPages);
|
||||||
|
if (response.pptStatus === "done") {
|
||||||
|
emit('addSuccess',{...res,url:response.pptUrl})
|
||||||
|
ElMessage.success("生成成功");
|
||||||
|
} else {
|
||||||
|
const sleepTime = 2000;
|
||||||
|
let remainingTime = sleepTime;
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
remainingTime -= 100;
|
||||||
|
if (remainingTime <= 0) {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
checkProgress();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
checkProgress();
|
||||||
|
}).finally(()=>{
|
||||||
|
createPPTLoading.value = false
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const createPPT = () => {
|
||||||
|
if (outlineData.value.templateId) {
|
||||||
|
outlineCreatePPT()
|
||||||
|
}else {
|
||||||
|
ElMessage.warning("请选择模板");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// emit('addSuccess',{url:'https://bjcdn.openstorage.cn/xinghuo-privatedata/%2Ftmp/apiTempFileba724e0344f74e1480535eedf3ebec661601807661085006275/%E9%87%91%E9%A9%AC%E5%A5%96%E5%B0%B4%E5%B0%AC%E4%BA%8B%E4%BB%B6%E5%88%86%E6%9E%90%E4%B8%8E%E5%BA%94%E5%AF%B9%E7%AD%96%E7%95%A5.pptx'})
|
||||||
|
getBackGroundV2().then((res) => {
|
||||||
|
backGroundList.value = res.records;
|
||||||
|
});
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -40,4 +130,50 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.ppt-dialog-footer{
|
||||||
|
padding-top: 20px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.ppt-dialog-content{
|
||||||
|
height: 500px;
|
||||||
|
overflow: auto;
|
||||||
|
.ppt-dialog-prog-wrap{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.ppt-dialog-cover-wrap{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-around;
|
||||||
|
.ppt-dialog-cover-item{
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border: 10px solid #ccc;
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
img{
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
.icon-select{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.active-mode{
|
||||||
|
border: 10px solid #5e24d0;
|
||||||
|
position: relative;
|
||||||
|
.icon-select{
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
right: 10px;
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
color: #fff;
|
||||||
|
background: #5e24d0;
|
||||||
|
border-radius: 5px;
|
||||||
|
display: block!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<el-button type="primary" @click="createAi">
|
<el-button type="primary" @click="createAi">
|
||||||
<i class="iconfont icon-chuangzuo"></i>生成教学大纲
|
<i class="iconfont icon-chuangzuo"></i>生成教学大纲
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="danger" :disabled="curItem.parentId" @click="delAnswer">
|
<el-button type="danger" :disabled="!!curItem.parentId" @click="delAnswer">
|
||||||
<i class="iconfont icon-shanchu"></i>
|
<i class="iconfont icon-shanchu"></i>
|
||||||
删除大纲
|
删除大纲
|
||||||
</el-button>
|
</el-button>
|
||||||
|
@ -20,8 +20,20 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="center-con" v-loading="loading">
|
<div class="center-con" v-loading="loading">
|
||||||
<TypingEffect v-if="answer" :text="answer" :delay="10" :aiShow="aiShow"/>
|
<!-- <TypingEffect v-if="answer" :text="answer" :delay="10" :aiShow="aiShow"/> -->
|
||||||
<el-empty v-if="!answer" description="请选择符合您需要的教学模式,生成教学大纲" />
|
<div style="font-size: 18px;color: #409eff;">封面页</div>
|
||||||
|
<div class="con-item mb-5">
|
||||||
|
<div class="item-name">标题:{{ answer.title }}</div>
|
||||||
|
<div class="item-name">副标题:{{answer.subTitle }}</div>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 18px;color: #409eff;">目录页</div>
|
||||||
|
<div class="con-item" v-for="(item,index) in answer.chapters">
|
||||||
|
<div class="item-name">{{index + 1}}:{{ item.chapterTitle }}</div>
|
||||||
|
<div class="item-text">
|
||||||
|
<p v-for="(el,i) in item.chapterContents">{{ index + 1 }} - {{ i + 1}} : {{ el.chapterTitle }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-empty v-if="!answer.title" description="请选择符合您需要的教学模式,生成教学大纲" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<EditDialog v-model="isEdit" :item="curItem" />
|
<EditDialog v-model="isEdit" :item="curItem" />
|
||||||
|
@ -36,7 +48,7 @@ import emitter from '@/utils/mitt'
|
||||||
import * as commUtils from '@/utils/comm.js'
|
import * as commUtils from '@/utils/comm.js'
|
||||||
import { createChart, sendChart } from '@/api/ai/index'
|
import { createChart, sendChart } from '@/api/ai/index'
|
||||||
import { completion, addSyllabus, syllabuss, removeSyllabus } from '@/api/mode/index.js'
|
import { completion, addSyllabus, syllabuss, removeSyllabus } from '@/api/mode/index.js'
|
||||||
import TypingEffect from '@/components/typing-effect/index.vue'
|
import { createOutlineV2 } from '@/utils/ppt-request.js'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
|
||||||
const curMode = ref(2)
|
const curMode = ref(2)
|
||||||
|
@ -64,8 +76,9 @@ emitter.on('selected', (data)=>{
|
||||||
// 回显大纲
|
// 回显大纲
|
||||||
const curItem = reactive({})
|
const curItem = reactive({})
|
||||||
emitter.on('onShow', (data)=>{
|
emitter.on('onShow', (data)=>{
|
||||||
|
console.log(data)
|
||||||
aiShow.value = false
|
aiShow.value = false
|
||||||
answer.value = getResult(data.outline)
|
Object.assign(answer, JSON.parse(data.outline))
|
||||||
Object.assign(curItem, data)
|
Object.assign(curItem, data)
|
||||||
curItem.answer = curItem.outline
|
curItem.answer = curItem.outline
|
||||||
getDetails(data.id)
|
getDetails(data.id)
|
||||||
|
@ -88,10 +101,9 @@ const params = reactive(
|
||||||
|
|
||||||
// 研读
|
// 研读
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const answer = ref('')
|
const answer = reactive({})
|
||||||
|
|
||||||
const createAi = async ()=>{
|
const createAi = async ()=>{
|
||||||
console.log(selectedData.value)
|
|
||||||
if(selectedData.value.length == 0){
|
if(selectedData.value.length == 0){
|
||||||
ElMessage.warning('请先选择教学环节后再生成教学大纲')
|
ElMessage.warning('请先选择教学环节后再生成教学大纲')
|
||||||
return
|
return
|
||||||
|
@ -119,17 +131,17 @@ const createAi = async ()=>{
|
||||||
// 知识库模型
|
// 知识库模型
|
||||||
else {
|
else {
|
||||||
const res = await completion(params)
|
const res = await completion(params)
|
||||||
|
|
||||||
data = res.data
|
data = res.data
|
||||||
}
|
}
|
||||||
console.log(data)
|
const res = await createOutlineV2({query: data.answer})
|
||||||
emitter.emit('onResult', data.answer)
|
console.log(res)
|
||||||
answer.value = getResult(data.answer)
|
emitter.emit('onResult', res)
|
||||||
|
Object.assign(answer, res.outline)
|
||||||
onSaveTemp(data.answer)
|
onSaveTemp(JSON.stringify(res.outline))
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -170,11 +182,6 @@ const delAnswer = () =>{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 去掉字符串中的 ### **
|
|
||||||
let getResult = (str) => {
|
|
||||||
let newStr = str.replace(/#+|(\*\*)/g, '');
|
|
||||||
return newStr
|
|
||||||
}
|
|
||||||
|
|
||||||
// 千帆创建对话
|
// 千帆创建对话
|
||||||
const conversation_id = ref('')
|
const conversation_id = ref('')
|
||||||
|
@ -227,6 +234,18 @@ onMounted(() => {
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
padding: 15px;
|
||||||
|
.con-item{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-top: 15px;
|
||||||
|
.item-text{
|
||||||
|
background: #F2F2F2;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,11 @@
|
||||||
<span>教学模式</span>
|
<span>教学模式</span>
|
||||||
<div>
|
<div>
|
||||||
<el-button type="primary" link @click="resetSelect">重置</el-button>
|
<el-button type="primary" link @click="resetSelect">重置</el-button>
|
||||||
<el-button type="primary" link @click="addVisible = true; addChild = false; isEdit = false"><i class="iconfont icon-jiahao"
|
<el-button type="primary" link @click="
|
||||||
></i>新增</el-button>
|
addVisible = true,
|
||||||
|
addChild = false,
|
||||||
|
isEdit = false
|
||||||
|
"><i class="iconfont icon-jiahao"></i>新增</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="left-list">
|
<div class="left-list">
|
||||||
|
@ -13,33 +16,34 @@
|
||||||
<div class="item-name flex" @mouseenter="item.isAdd = true" @mouseleave="item.isAdd = false"
|
<div class="item-name flex" @mouseenter="item.isAdd = true" @mouseleave="item.isAdd = false"
|
||||||
@click="toggleParent(item)">
|
@click="toggleParent(item)">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<span>{{ item.name }}</span>
|
<span>{{ item.name }}</span>
|
||||||
<!--个人教学模式才会有添加、编辑-->
|
<!--个人教学模式才会有添加、编辑-->
|
||||||
<div v-if="!item.ex3" class="ml-3">
|
<div v-if="!item.ex3" class="ml-3">
|
||||||
<el-button type="primary" link @click.stop="addModeChild(item)">添加</el-button>
|
<el-button type="primary" link @click.stop="addModeChild(item)">添加</el-button>
|
||||||
<el-button type="primary" link @click.stop="editMode(item)">编辑</el-button>
|
<el-button type="primary" link @click.stop="editMode(item)">编辑</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--加号 数鼠标悬浮 显示-->
|
<!--加号 数鼠标悬浮 显示-->
|
||||||
<i v-show="item.isAdd && !item.selected" class="iconfont icon-jiahao"></i>
|
<i v-show="item.isAdd && !item.selected" class="iconfont icon-jiahao"></i>
|
||||||
<!--减号 选中之后显示-->
|
<!--减号 选中之后显示-->
|
||||||
<i v-show="item.selected" class="iconfont icon-zuixiaohua"></i>
|
<i v-show="item.selected" class="iconfont icon-zuixiaohua"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="item-child flex" :class="child.selected ? 'act-child' : ''" v-for="child in item.children" :key="child.id"
|
<div class="item-child flex" :class="child.selected ? 'act-child' : ''" v-for="child in item.children"
|
||||||
@mouseenter="child.isAdd = true" @mouseleave="child.isAdd = false" @click="toggleChild(item,child)">
|
:key="child.id" @mouseenter="child.isAdd = true" @mouseleave="child.isAdd = false"
|
||||||
|
@click="toggleChild(item, child)">
|
||||||
<div>
|
<div>
|
||||||
<span>{{ child.name }}</span>
|
<span>{{ child.name }}</span>
|
||||||
<!--个人教学模式才会有编辑-->
|
<!--个人教学模式才会有编辑-->
|
||||||
<el-button v-if="!child.ex3" class="ml-3" type="primary" link @click.stop="editMode(child)">编辑</el-button>
|
<el-button v-if="!child.ex3" class="ml-3" type="primary" link @click.stop="editMode(child)">编辑</el-button>
|
||||||
</div>
|
</div>
|
||||||
<i v-show="child.isAdd && !child.selected" class="iconfont icon-jiahao"></i>
|
<i v-show="child.isAdd && !child.selected" class="iconfont icon-jiahao"></i>
|
||||||
<i v-show="child.selected" class="iconfont icon-zuixiaohua "></i>
|
<i v-show="child.selected" class="iconfont icon-zuixiaohua"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--弹窗-->
|
<!--弹窗-->
|
||||||
<el-dialog v-model="addVisible" append-to-body :show-close="false" width="550" :before-close="handleBeforeClose"
|
<el-dialog v-model="addVisible" append-to-body :show-close="false" width="550" :before-close="handleBeforeClose"
|
||||||
style="border-radius: 10px; padding: 10px 15px;">
|
style="border-radius: 10px; padding: 10px 15px">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="mode-dialog-header flex">
|
<div class="mode-dialog-header flex">
|
||||||
<span>{{ addChild ? '教学环节' : '教学模式' }}</span>
|
<span>{{ addChild ? '教学环节' : '教学模式' }}</span>
|
||||||
|
@ -48,8 +52,8 @@
|
||||||
</template>
|
</template>
|
||||||
<el-form ref="ruleFormRef" style="max-width: 600px" :model="ruleForm" :rules="rules" label-width="auto"
|
<el-form ref="ruleFormRef" style="max-width: 600px" :model="ruleForm" :rules="rules" label-width="auto"
|
||||||
class="demo-ruleForm">
|
class="demo-ruleForm">
|
||||||
<el-form-item :label="`教学${ addChild ? '环节' : '模式'}名称`" prop="name">
|
<el-form-item :label="`教学${addChild ? '环节' : '模式'}名称`" prop="name">
|
||||||
<div class="flex" style="width: 100%;">
|
<div class="flex" style="width: 100%">
|
||||||
<el-input v-model="ruleForm.name" />
|
<el-input v-model="ruleForm.name" />
|
||||||
<el-button v-if="isEdit" link type="danger" class="ml-5 mr-3" @click="onDel">删除</el-button>
|
<el-button v-if="isEdit" link type="danger" class="ml-5 mr-3" @click="onDel">删除</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -64,7 +68,6 @@
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
@ -73,131 +76,134 @@ import { modelList } from '@/api/mode/index'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import { sessionStore } from '@/utils/store'
|
import { sessionStore } from '@/utils/store'
|
||||||
import emitter from '@/utils/mitt'
|
import emitter from '@/utils/mitt'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { addChildTemp, editChildTemp, removeChildTemp, syllabusList } from '@/api/mode'
|
import { addChildTemp, editChildTemp, removeChildTemp, syllabusList } from '@/api/mode'
|
||||||
import { cloneDeep } from 'lodash'
|
import { cloneDeep } from 'lodash'
|
||||||
|
|
||||||
|
|
||||||
const { user } = useUserStore()
|
const { user } = useUserStore()
|
||||||
|
|
||||||
// 获取主模板
|
// 获取模板
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
let list = ref([])
|
|
||||||
const getTemplate = async (id) => {
|
|
||||||
loading.value = true
|
|
||||||
const { rows } = await modelList({ createUser: user.userId, model: 4, type: 1, pageNum: 1, pageSize: 10000 })
|
|
||||||
loading.value = false
|
|
||||||
list.value = rows
|
|
||||||
if (list.value.length) {
|
|
||||||
getChildTemp(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取子模板
|
|
||||||
const templateList = ref([])
|
const templateList = ref([])
|
||||||
const getChildTemp = async () => {
|
const getTemplate = async () => {
|
||||||
templateList.value = []
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
const { rows } = await modelList({
|
||||||
|
createUser: user.userId,
|
||||||
|
model: 4,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10000
|
||||||
|
})
|
||||||
|
loading.value = false
|
||||||
|
|
||||||
for (let item of list.value) {
|
// 过滤出 type 为 1 的项
|
||||||
try {
|
let ary1 = rows.filter((item) => item.type === 1)
|
||||||
let { rows } = await modelList({ model: 4, type: 2, parentId: item.id })
|
templateList.value = ary1.map((parent) => {
|
||||||
templateList.value.push({
|
parent.children = rows.filter((child) => child.type === 2 && child.parentId === parent.id)
|
||||||
...item,
|
return parent
|
||||||
children: rows
|
})
|
||||||
})
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getSyllabus()
|
getSyllabus()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询生成大纲记录
|
// 查询生成大纲记录
|
||||||
const getSyllabus = async () =>{
|
const getSyllabus = async () => {
|
||||||
const { rows } = await syllabusList()
|
const { rows } = await syllabusList({
|
||||||
if(rows && rows.length){
|
createUserId: user.userId,
|
||||||
const idsAry = rows.at(-1).modelIds.split(',').map(item => Number(item));
|
eduId: curNode.id,
|
||||||
|
sourceType: 1,
|
||||||
|
pageSize: 1,
|
||||||
|
orderByColumn: 'createTime',
|
||||||
|
isAsc: 'desc'
|
||||||
|
})
|
||||||
|
if (rows && rows.length) {
|
||||||
|
const idsAry = rows
|
||||||
|
.at(-1)
|
||||||
|
.modelIds.split(',')
|
||||||
|
.map((item) => Number(item))
|
||||||
// 设置选中
|
// 设置选中
|
||||||
|
let ary = []
|
||||||
templateList.value.forEach((parent) => {
|
templateList.value.forEach((parent) => {
|
||||||
parent.children.forEach((child) => {
|
parent.children.forEach((child) => {
|
||||||
child.selected = idsAry.includes(child.id);
|
child.selected = idsAry.includes(child.id)
|
||||||
});
|
if (child.selected) {
|
||||||
|
ary.push(child)
|
||||||
|
}
|
||||||
|
})
|
||||||
// 更新父项的 selected 状态
|
// 更新父项的 selected 状态
|
||||||
parent.selected = parent.children.every((child) => child.selected);
|
parent.selected = parent.children.every((child) => child.selected)
|
||||||
});
|
})
|
||||||
// 回显大纲数据
|
// 回显大纲数据
|
||||||
emitter.emit('onShow', rows.at(-1))
|
emitter.emit('onShow', rows.at(-1))
|
||||||
|
emitter.emit('selected', ary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置选中
|
// 重置选中
|
||||||
const resetSelect = () =>{
|
const resetSelect = () => {
|
||||||
templateList.value.forEach(item =>{
|
templateList.value.forEach((item) => {
|
||||||
item.selected = false
|
item.selected = false
|
||||||
item.children.forEach( el=>{
|
item.children.forEach((el) => {
|
||||||
el.selected = false
|
el.selected = false
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
emitter.emit('selected', [])
|
emitter.emit('selected', [])
|
||||||
}
|
}
|
||||||
|
|
||||||
emitter.on('resetSelect', () =>{
|
emitter.on('resetSelect', () => {
|
||||||
resetSelect()
|
resetSelect()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 点击教学模式
|
// 点击教学模式
|
||||||
const toggleParent = (parent) => {
|
const toggleParent = (parent) => {
|
||||||
parent.selected = !parent.selected;
|
parent.selected = !parent.selected
|
||||||
parent.children.forEach(child => {
|
parent.children.forEach((child) => {
|
||||||
child.selected = parent.selected;
|
child.selected = parent.selected
|
||||||
});
|
})
|
||||||
const selectedData = templateList.value.map((item) => item.children.filter((child) => child.selected)).flat()
|
const selectedData = templateList.value
|
||||||
// .flat()将二维数组扁平化为一维数组
|
.map((item) => item.children.filter((child) => child.selected))
|
||||||
|
.flat()
|
||||||
|
// .flat()将二维数组扁平化为一维数组
|
||||||
emitter.emit('selected', selectedData)
|
emitter.emit('selected', selectedData)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 点击教学环节
|
// 点击教学环节
|
||||||
const toggleChild = (parent,child) =>{
|
const toggleChild = (parent, child) => {
|
||||||
child.selected = !child.selected;
|
child.selected = !child.selected
|
||||||
updateParentSelection(parent)
|
updateParentSelection(parent)
|
||||||
const selectedData = templateList.value.map((item) => item.children.filter((child) => child.selected)).flat()
|
const selectedData = templateList.value
|
||||||
|
.map((item) => item.children.filter((child) => child.selected))
|
||||||
|
.flat()
|
||||||
emitter.emit('selected', selectedData)
|
emitter.emit('selected', selectedData)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查父项(教学模式)的所有子项(教学环节)是否都被选中,如果是,则设置父项为选中状态,否则取消选中
|
// 检查父项(教学模式)的所有子项(教学环节)是否都被选中,如果是,则设置父项为选中状态,否则取消选中
|
||||||
const updateParentSelection = (parent) => {
|
const updateParentSelection = (parent) => {
|
||||||
parent.selected = parent.children.every((child) => child.selected);
|
parent.selected = parent.children.every((child) => child.selected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 弹窗
|
// 弹窗
|
||||||
const addVisible = ref(false)
|
const addVisible = ref(false)
|
||||||
|
|
||||||
const ruleFormRef = ref()
|
const ruleFormRef = ref()
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
name: [
|
name: [{ required: true, message: '不能为空', trigger: 'blur' }]
|
||||||
{ required: true, message: '不能为空', trigger: 'blur' },
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
const ruleForm = reactive({
|
const ruleForm = reactive({
|
||||||
name: '',
|
name: '',
|
||||||
prompt: ''
|
prompt: ''
|
||||||
})
|
})
|
||||||
// 提交
|
// 提交
|
||||||
const onSubmit = async (formEl) =>{
|
const onSubmit = async (formEl) => {
|
||||||
if (!formEl) return
|
if (!formEl) return
|
||||||
await formEl.validate(async (valid, fields) => {
|
await formEl.validate(async (valid, fields) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
|
|
||||||
// 修改
|
// 修改
|
||||||
if(isEdit.value){
|
if (isEdit.value) {
|
||||||
let data = cloneDeep(curEditItem)
|
let data = cloneDeep(curEditItem)
|
||||||
data.name = ruleForm.name
|
data.name = ruleForm.name
|
||||||
await editChildTemp(data)
|
await editChildTemp(data)
|
||||||
}
|
}
|
||||||
// 新增
|
// 新增
|
||||||
else{
|
else {
|
||||||
let obj = {
|
let obj = {
|
||||||
ex1: curNode.edustage,
|
ex1: curNode.edustage,
|
||||||
ex2: curNode.edusubject,
|
ex2: curNode.edusubject,
|
||||||
|
@ -207,13 +213,12 @@ const onSubmit = async (formEl) =>{
|
||||||
type: 1
|
type: 1
|
||||||
}
|
}
|
||||||
// 新增教学环节
|
// 新增教学环节
|
||||||
if(addChild.value){
|
if (addChild.value) {
|
||||||
obj.parentId = curEditItem.id
|
obj.parentId = curEditItem.id
|
||||||
obj.prompt = ruleForm.prompt
|
obj.prompt = ruleForm.prompt
|
||||||
obj.type = 2
|
obj.type = 2
|
||||||
}
|
}
|
||||||
await addChildTemp(obj)
|
await addChildTemp(obj)
|
||||||
|
|
||||||
}
|
}
|
||||||
ElMessage.success('操作成功')
|
ElMessage.success('操作成功')
|
||||||
resetForm()
|
resetForm()
|
||||||
|
@ -231,7 +236,7 @@ const isEdit = ref(false)
|
||||||
const addChild = ref(false)
|
const addChild = ref(false)
|
||||||
|
|
||||||
const curEditItem = reactive({})
|
const curEditItem = reactive({})
|
||||||
const addModeChild = (item) =>{
|
const addModeChild = (item) => {
|
||||||
isEdit.value = false
|
isEdit.value = false
|
||||||
Object.assign(curEditItem, item)
|
Object.assign(curEditItem, item)
|
||||||
addChild.value = true
|
addChild.value = true
|
||||||
|
@ -240,7 +245,7 @@ const addModeChild = (item) =>{
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
const editMode = (item)=>{
|
const editMode = (item) => {
|
||||||
isEdit.value = true
|
isEdit.value = true
|
||||||
addChild.value = false
|
addChild.value = false
|
||||||
Object.assign(curEditItem, item)
|
Object.assign(curEditItem, item)
|
||||||
|
@ -249,16 +254,12 @@ const editMode = (item)=>{
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除 教学模式 教学环节
|
// 删除 教学模式 教学环节
|
||||||
const onDel = () =>{
|
const onDel = () => {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm('确定要删除模板吗?', '温馨提示', {
|
||||||
'确定要删除模板吗?',
|
confirmButtonText: '确定',
|
||||||
'温馨提示',
|
cancelButtonText: '取消',
|
||||||
{
|
type: 'warning'
|
||||||
confirmButtonText: '确定',
|
})
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
await removeChildTemp(curEditItem.id)
|
await removeChildTemp(curEditItem.id)
|
||||||
ElMessage.success('操作成功')
|
ElMessage.success('操作成功')
|
||||||
|
@ -266,36 +267,33 @@ const onDel = () =>{
|
||||||
addVisible.value = false
|
addVisible.value = false
|
||||||
getTemplate()
|
getTemplate()
|
||||||
})
|
})
|
||||||
.catch(() => {})
|
.catch(() => { })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const closeDialog = () => {
|
||||||
const closeDialog = () =>{
|
handleBeforeClose(() => (addVisible.value = false))
|
||||||
handleBeforeClose(() => addVisible.value = false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleBeforeClose = (done) =>{
|
const handleBeforeClose = (done) => {
|
||||||
resetForm()
|
resetForm()
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
if (ruleFormRef.value) {
|
if (ruleFormRef.value) {
|
||||||
ruleFormRef.value.resetFields();
|
ruleFormRef.value.resetFields()
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const curNode = reactive({})
|
const curNode = reactive({})
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
let data = sessionStore.get('subject.curNode')
|
let data = sessionStore.get('subject.curNode')
|
||||||
Object.assign(curNode, data);
|
Object.assign(curNode, data)
|
||||||
getTemplate()
|
getTemplate()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
onUnmounted(()=>{
|
|
||||||
emitter.off('resetSelect')
|
emitter.off('resetSelect')
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -342,7 +340,7 @@ onUnmounted(()=>{
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: "";
|
content: '';
|
||||||
width: 5px;
|
width: 5px;
|
||||||
height: 5px;
|
height: 5px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
@ -352,7 +350,6 @@ onUnmounted(()=>{
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate(0, -50%);
|
transform: translate(0, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
|
@ -389,7 +386,8 @@ onUnmounted(()=>{
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.form-btn{
|
|
||||||
|
.form-btn {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-dialog v-model="open" v-bind="dAttrs">
|
<el-dialog class="aix-progress-dialog" v-model="open" v-bind="dAttrs">
|
||||||
<el-progress type="dashboard" v-bind="$attrs.pg" />
|
<el-progress type="dashboard" v-bind="$attrs.pg" />
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
@ -29,5 +29,10 @@ const dAttrs = computed(() => {
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
||||||
|
<style>
|
||||||
|
.aix-progress-dialog .el-dialog__body{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -4,22 +4,73 @@
|
||||||
<span>课件预览</span>
|
<span>课件预览</span>
|
||||||
<div>
|
<div>
|
||||||
<el-button type="danger" @click="onCreate">一键生成</el-button>
|
<el-button type="danger" @click="onCreate">一键生成</el-button>
|
||||||
<el-button>编辑课件</el-button>
|
<el-button :disabled="!result?.parentId" @click="openAiPPT">编辑课件</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="right-con">
|
<div class="right-con">
|
||||||
<el-empty v-if="!result" description="请先生成教学大纲,再生成教学课件" />
|
<el-empty v-if="!result?.parentId" description="请先生成教学大纲,再生成教学课件" />
|
||||||
|
<div v-for="(item,index) in pptSlides" class="right-con-item">
|
||||||
|
<div>{{index+1}}</div><img :src="item.fileurl">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<PptDialog @close-dialogs="pptDialog = false" @add-success="addAiPPT" :dataList="result" v-model="pptDialog" />
|
||||||
|
<progress-dialog v-model:visible="pgDialog.visible" v-bind="pgDialog" />
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onUnmounted } from 'vue'
|
import {ref, onUnmounted, onMounted, reactive} from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import emitter from '@/utils/mitt'
|
import emitter from '@/utils/mitt'
|
||||||
|
import progressDialog from './progress-dialog.vue'
|
||||||
|
import PptDialog from "@/views/prepare/container/pptist-dialog.vue";
|
||||||
|
import msgUtils from "@/plugins/modal";
|
||||||
|
import {PPTXFileToJson} from "@/AixPPTist/src/hooks/useImport";
|
||||||
|
import {slidesToImg} from "@/utils/ppt";
|
||||||
|
import {getEntpcoursefile, listEntpcoursefileNew} from "@/api/education/entpcoursefile";
|
||||||
|
import {sessionStore} from "@/utils/store";
|
||||||
|
import * as API_smarttalk from "@/api/file";
|
||||||
|
import * as API_entpcourse from "@/api/education/entpcourse";
|
||||||
|
import * as API_entpcoursefile from "@/api/education/entpcoursefile";
|
||||||
|
import * as commUtils from '@/utils/comm.js'
|
||||||
|
import * as Api_server from '@/api/apiService' // 相关api
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import {createWindow} from "@/utils/tool";
|
||||||
|
import {editSyllabus} from "@/api/mode";
|
||||||
|
import { getSmarttalkPage } from "@/api/file"
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const pptDialog = ref(false)
|
||||||
|
const result = ref(null)
|
||||||
|
const courseObj = reactive({
|
||||||
|
node: null, // 选择的课程节点
|
||||||
|
})
|
||||||
|
const curNode = reactive({})
|
||||||
|
const pgDialog = reactive({ // 弹窗-进度条
|
||||||
|
visible: false,
|
||||||
|
title: 'PPT解析中...',
|
||||||
|
width: 300,
|
||||||
|
showClose: false,
|
||||||
|
draggable: true,
|
||||||
|
beforeClose: done => { }, // 阻止-弹窗事件
|
||||||
|
pg: { // 进度条-参数
|
||||||
|
percentage: 0, // 百分比
|
||||||
|
color: [
|
||||||
|
{ color: '#1989fa', percentage: 50 }, // 蓝色
|
||||||
|
{ color: '#e6a23c', percentage: 80 }, // 橙色
|
||||||
|
{ color: '#5cb87a', percentage: 100 }, // 绿色
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const pptSlides = ref([])
|
||||||
|
|
||||||
const result = ref('')
|
|
||||||
emitter.on('onResult', (data)=>{
|
emitter.on('onResult', (data)=>{
|
||||||
result.value = data
|
result.value = data
|
||||||
|
if (!!result.value.parentId) {
|
||||||
|
listEntpcoursefileNew({parentid: result.value.parentId}).then(res=>{
|
||||||
|
pptSlides.value = res.rows
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const onCreate = () =>{
|
const onCreate = () =>{
|
||||||
|
@ -27,8 +78,232 @@ const onCreate = () =>{
|
||||||
ElMessage.warning('请先进行研读')
|
ElMessage.warning('请先进行研读')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
pptDialog.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const addAiPPT = async (res) => {
|
||||||
|
// res = { url: 'https://bjcdn.openstorage.cn/xinghuo-privatedata/%2Ftmp/apiTempFileba724e0344f74e1480535eedf3ebec661601807661085006275/%E9%87%91%E9%A9%AC%E5%A5%96%E5%B0%B4%E5%B0%AC%E4%BA%8B%E4%BB%B6%E5%88%86%E6%9E%90%E4%B8%8E%E5%BA%94%E5%AF%B9%E7%AD%96%E7%95%A5.pptx' }
|
||||||
|
let node = courseObj.node
|
||||||
|
pptDialog.value = false;
|
||||||
|
if (!node) return msgUtils.msgWarning('请选择章节?')
|
||||||
|
pgDialog.visible = true
|
||||||
|
pgDialog.pg.percentage = 0
|
||||||
|
//TODO res中有PPT地址
|
||||||
|
const params = { evalid: node.id, edituserid: userStore.id, pageSize: 1 }
|
||||||
|
const resEnpt = await HTTP_SERVER_API('getCourseList', params)
|
||||||
|
if (!(resEnpt?.rows?.[0] || null)) { // 创建
|
||||||
|
const resid = await HTTP_SERVER_API('addEntpcourse')
|
||||||
|
courseObj.entp.id = resid
|
||||||
|
} else courseObj.entp = resEnpt?.rows?.[0] || null
|
||||||
|
// 下载PPT 并解析json转换到我们自己数据库
|
||||||
|
fetch(res.url)
|
||||||
|
.then(res => res.arrayBuffer())
|
||||||
|
.then(async buffer => {
|
||||||
|
const resPptJson = await PPTXFileToJson(buffer)
|
||||||
|
const { def, slides, ...content } = resPptJson
|
||||||
|
// 生成缩略图
|
||||||
|
const thumbnails = await slidesToImg(slides, content.width)
|
||||||
|
// 转换图片|音频|视频 为线上地址
|
||||||
|
let completed = 0
|
||||||
|
const total = slides.length
|
||||||
|
for (let o of slides) {
|
||||||
|
completed++
|
||||||
|
await toRousrceUrl(o)
|
||||||
|
// 设置进度条
|
||||||
|
pgDialog.pg.percentage = Math.floor(completed / total * 100)
|
||||||
|
}
|
||||||
|
pgDialog.pg.percentage = 0
|
||||||
|
pgDialog.visible = false
|
||||||
|
// 生成ppt课件-父级
|
||||||
|
const p_params = { parentContent: JSON.stringify(content) }
|
||||||
|
const parentid = await HTTP_SERVER_API('addEntpcoursefile', p_params)
|
||||||
|
if (!!parentid ?? null) { // 生成内容幻灯片
|
||||||
|
// 生成备课资源-Smarttalk
|
||||||
|
const smarttalk = await HTTP_SERVER_API('addSmarttalk', { fileId: parentid })
|
||||||
|
if (slides.length > 0) {
|
||||||
|
const resSlides = slides.map(({ id, ...slide }) => JSON.stringify(slide))
|
||||||
|
const params = { parentid, filetype: 'slide', title: '', thumbnails, slides: resSlides }
|
||||||
|
const res_3 = await HTTP_SERVER_API('batchAddNew', params)
|
||||||
|
if (res_3 && res_3.code == 200) {
|
||||||
|
msgUtils.msgSuccess('生成PPT课件成功')
|
||||||
|
//TODO 打开生成的课件
|
||||||
|
updateGen(parentid)
|
||||||
|
listEntpcoursefileNew({parentid: parentid}).then(res=>{
|
||||||
|
pptSlides.value = res.rows
|
||||||
|
})
|
||||||
|
const res = await getEntpcoursefile(parentid)
|
||||||
|
if (res && res.code === 200) {
|
||||||
|
openPublicScreen('edit', res.data, smarttalk.resData) // 打开公屏-窗口
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(res.msg||'文件获取异常!')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
msgUtils.msgWarning('生成PPT课件失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).finally(()=>{
|
||||||
|
pgDialog.visible = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const openAiPPT = async () =>{
|
||||||
|
let parentid = result.value.parentId
|
||||||
|
const res = await getEntpcoursefile(parentid)
|
||||||
|
if (res && res.code === 200) {
|
||||||
|
const smarttalk = getSmarttalkPage({fileId: parentid})
|
||||||
|
openPublicScreen('edit', res.data, smarttalk.resData) // 打开公屏-窗口
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(res.msg||'文件获取异常!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateGen = (parentid)=>{
|
||||||
|
result.value.parentId = parentid
|
||||||
|
editSyllabus(result.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const openPublicScreen = (type, resource, currData)=> {
|
||||||
|
sessionStore.set('curr.resource', resource) // 缓存当前资源信息
|
||||||
|
if (type=='edit') sessionStore.set('curr.smarttalk', currData) // 缓存当前文件smarttalk
|
||||||
|
else sessionStore.set('curr.classcourse', currData) // 缓存当前当前上课
|
||||||
|
createWindow('open-win', {
|
||||||
|
url: '/pptist', // 窗口关闭时,清除缓存
|
||||||
|
close: () => {
|
||||||
|
sessionStore.set('curr.resource', null) // 清除缓存
|
||||||
|
if (type=='edit') {
|
||||||
|
sessionStore.set('curr.smarttalk', null) // 清除缓存
|
||||||
|
} else sessionStore.set('curr.classcourse', null) // 清除缓存
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图片|音频|视频 转换为在线地址
|
||||||
|
const toRousrceUrl = async (o) => {
|
||||||
|
if (!!o.src) { // 如果有src就转换
|
||||||
|
const isBase64 = /^data:image\/(\w+);base64,/.test(o.src)
|
||||||
|
const isBlobUrl = /^blob:/.test(o.src)
|
||||||
|
console.log('isBase64', o, isBase64)
|
||||||
|
if (isBase64) {
|
||||||
|
const bolb = commUtils.base64ToBlob(o.src)
|
||||||
|
const fileName = Date.now() + '.png'
|
||||||
|
const file = commUtils.blobToFile(bolb, fileName)
|
||||||
|
// o.src = fileName
|
||||||
|
// console.log('file', file)
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
const res = await Api_server.Other.uploadFile(formData)
|
||||||
|
if (res && res.code == 200) {
|
||||||
|
const url = res?.url
|
||||||
|
url && (o.src = url)
|
||||||
|
}
|
||||||
|
} else if (isBlobUrl) { // 视频和音频
|
||||||
|
const res = await fetch(o.src)
|
||||||
|
const blob = await res.blob()
|
||||||
|
const fileName = o.type == 'video' ? Date.now() + '.mp4' : Date.now() + '.mp3'
|
||||||
|
const file = commUtils.blobToFile(blob, fileName)
|
||||||
|
// o.src = fileName
|
||||||
|
// console.log('file', file)
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
const ress = await Api_server.Other.uploadFile(formData)
|
||||||
|
if (ress && ress.code == 200) {
|
||||||
|
const url = ress?.url
|
||||||
|
url && (o.src = url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (o?.background?.image) await toRousrceUrl(o.background.image)
|
||||||
|
if (o?.elements) {
|
||||||
|
for (let element of o.elements) {
|
||||||
|
await toRousrceUrl(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const HTTP_SERVER_API = (type, params = {}) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'addSmarttalk': { // 获取课程
|
||||||
|
const node = courseObj.node || {}
|
||||||
|
const def = {
|
||||||
|
fileId: '', // 文件id - Entpcoursefile 对应id
|
||||||
|
fileFlag: 'aippt',
|
||||||
|
fileShowName: node.itemtitle + '.aippt',
|
||||||
|
textbookId: node.rootid,
|
||||||
|
levelFirstId: node.parentid || node.id,
|
||||||
|
levelSecondId: node.parentid && node.id,
|
||||||
|
fileSource: '个人',
|
||||||
|
fileRoot: '备课'
|
||||||
|
}
|
||||||
|
return API_smarttalk.creatAPT({ ...def, ...params })
|
||||||
|
}
|
||||||
|
case 'addEntpcourse': { // 添加课程
|
||||||
|
const node = courseObj.node || {}
|
||||||
|
if (!node) return msgUtils.msgWarning('请选择章节?')
|
||||||
|
const def = { // 默认参数
|
||||||
|
entpid: userStore.user.deptId, // 部门id
|
||||||
|
level: 1, // 层级
|
||||||
|
parentid: 0, // 父级id
|
||||||
|
dictid: 0, // 字典id
|
||||||
|
evalid: node.id, // 章节id
|
||||||
|
evalparentid: node.parentid, // 单元id(父级id)
|
||||||
|
edusubject: node.edusubject, // 学科
|
||||||
|
edudegree: node.edudegree, // 年级
|
||||||
|
edustage: node.edustage, // 阶段
|
||||||
|
coursetype: '课标学科', // 课程类型
|
||||||
|
coursetitle: node.itemtitle, // 课程名称
|
||||||
|
coursedesc: '', // 课程描述
|
||||||
|
status: '', // 状态
|
||||||
|
dflag: 0, // 状态
|
||||||
|
edituserid: userStore.id, // 编辑人id
|
||||||
|
createblankfile: 'no', // 创建空白文件
|
||||||
|
}
|
||||||
|
courseObj.entp = def
|
||||||
|
return API_entpcourse.addEntpcourse(def)
|
||||||
|
}
|
||||||
|
case 'addEntpcoursefile': { // 添加课程文件
|
||||||
|
params = getDefParams(params)
|
||||||
|
return API_entpcoursefile.addEntpcoursefileReturnId(params)
|
||||||
|
}
|
||||||
|
case 'batchAddNew': { // 批量添加课程文件
|
||||||
|
params = getDefParams(params)
|
||||||
|
return API_entpcoursefile.batchAddNew(params)
|
||||||
|
}
|
||||||
|
case 'getCourseList': { // 获取课程列表
|
||||||
|
return API_entpcourse.listEntpcourse(params)
|
||||||
|
}
|
||||||
|
case 'getCourseFileList': { // 获取课程文件列表
|
||||||
|
return API_entpcoursefile.listEntpcoursefileNew(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 获取默认参数
|
||||||
|
const getDefParams = (params) => {
|
||||||
|
const enpt = courseObj.entp
|
||||||
|
const def = {
|
||||||
|
parentid: 0,
|
||||||
|
entpid: userStore.user.deptId,
|
||||||
|
entpcourseid: enpt.id,
|
||||||
|
ppttype: 'file',
|
||||||
|
title: enpt.coursetitle,
|
||||||
|
fileurl: '',
|
||||||
|
filetype: 'aippt',
|
||||||
|
datacontent: '',
|
||||||
|
filekey: '',
|
||||||
|
filetag: '',
|
||||||
|
fileidx: 0,
|
||||||
|
dflag: 0,
|
||||||
|
status: '',
|
||||||
|
edituserid: userStore.id
|
||||||
|
}
|
||||||
|
return Object.assign(def, params)
|
||||||
|
}
|
||||||
|
onMounted(()=>{
|
||||||
|
let data = sessionStore.get('subject.curNode')
|
||||||
|
Object.assign(curNode, data);
|
||||||
|
courseObj.node = data
|
||||||
|
})
|
||||||
|
|
||||||
onUnmounted(()=>{
|
onUnmounted(()=>{
|
||||||
emitter.off('onResult')
|
emitter.off('onResult')
|
||||||
})
|
})
|
||||||
|
@ -51,6 +326,14 @@ onUnmounted(()=>{
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
.right-con-item{
|
||||||
|
display: flex;
|
||||||
|
margin: 10px 0;
|
||||||
|
justify-content: space-around;
|
||||||
|
img{
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,10 +12,10 @@
|
||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<Left/>
|
<Left/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="13">
|
<el-col :span="15">
|
||||||
<Center/>
|
<Center/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="4">
|
||||||
<Right/>
|
<Right/>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
Loading…
Reference in New Issue