Merge pull request 'zdg_dev' (#174) from zdg_dev into main

Reviewed-on: #174
This commit is contained in:
zhengdegang 2024-12-24 12:05:02 +08:00
commit 33e38f8992
6 changed files with 114 additions and 14 deletions

View File

@ -14,7 +14,8 @@ export default () => {
const courseId = classcourse?.id // 课堂id const courseId = classcourse?.id // 课堂id
const timgroupid = classcourse?.timgroupid // 群组id const timgroupid = classcourse?.timgroupid // 群组id
const classcourseStore = useClasscourseStore() // 课堂信息-状态管理 const classcourseStore = useClasscourseStore() // 课堂信息-状态管理
if (!ChatWs.ws) ChatWs.init() // 上课状态才-初始化socket
if (!ChatWs.ws && !!courseId) ChatWs.init()
// 开课消息 // 开课消息
const startCourse = async() => { const startCourse = async() => {
// await API_classcourse.updateClasscourse({ id: classcourse.id, status: 'open' }) // await API_classcourse.updateClasscourse({ id: classcourse.id, status: 'open' })

View File

@ -14,8 +14,8 @@
mainMenuVisible = false mainMenuVisible = false
}"> }">
<PopoverMenuItem>导入 pptx 文件</PopoverMenuItem> <PopoverMenuItem>导入 pptx 文件</PopoverMenuItem>
</FileInput> --> </FileInput>
<PopoverMenuItem @click="setDialogForExport('pptx')">导出文件</PopoverMenuItem> <PopoverMenuItem @click="setDialogForExport('pptx')">导出文件</PopoverMenuItem> -->
<PopoverMenuItem @click="resetSlides(); mainMenuVisible = false">重置幻灯片</PopoverMenuItem> <PopoverMenuItem @click="resetSlides(); mainMenuVisible = false">重置幻灯片</PopoverMenuItem>
<!-- <PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/issues')">意见反馈</PopoverMenuItem> --> <!-- <PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/issues')">意见反馈</PopoverMenuItem> -->
<!-- <PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/blob/master/doc/Q&A.md')">常见问题</PopoverMenuItem> --> <!-- <PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/blob/master/doc/Q&A.md')">常见问题</PopoverMenuItem> -->
@ -54,9 +54,9 @@
<div class="arrow-btn"><IconDown class="arrow" /></div> <div class="arrow-btn"><IconDown class="arrow" /></div>
</Popover> </Popover>
</div> </div>
<div class="menu-item" v-tooltip="'导出'" @click="setDialogForExport('pptx')"> <!-- <div class="menu-item" v-tooltip="'导出'" @click="setDialogForExport('pptx')">
<IconDownload class="icon" /> <IconDownload class="icon" />
</div> </div> -->
<div class="menu-item" v-tooltip="`${userStore.user.parentDeptName}-${userStore.user.nickName}`"> <div class="menu-item" v-tooltip="`${userStore.user.parentDeptName}-${userStore.user.nickName}`">
<el-avatar size="small" :src="avatar" /> <el-avatar size="small" :src="avatar" />
</div> </div>

View File

@ -0,0 +1,87 @@
/**
* ppt 转换为图片
*/
import { h, render, getCurrentInstance } from 'vue'
import { toPng, toJpeg } from 'html-to-image' // 引入html-to-image库
import { PPTXFileToJson } from "@/AixPPTist/src/hooks/useImport"
import ThumbnailSlide from '@/AixPPTist/src/views/components/ThumbnailSlide/index.vue'
import { useSlidesStore } from '@/AixPPTist/src/store'
// 延时
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
/**
* @description: 渲染组件
* @param {*} node 节点或属性
* @param {*} props 属性
* @param {*} children 子元素
* @param {*} container 容器
*/
const renderComponent = (node, props = {}, children, container) => {
let vNode, body
if (!node) throw new Error('vNode is required')
if (typeof container == 'string') {
if (node == 'slide') {
vNode = h(ThumbnailSlide, props, children)
} else throw new Error('vNode has no corresponding component')
} else {
vNode = h(node, props, children)
}
if (!container) body = document.body // 默认为body
else { // 判断是否为字符串
if (typeof container == 'string') {
body = document.querySelector(container)
} else {
body = container
}
}
return render(vNode, body)
}
/**
* @description: 幻灯片转换为图片
* 提示icon组件找不到是应为 h() 渲染是底层创建的虚拟节点找不到全局组件
* @param {*} slides 幻灯片数据
* @param {*} options 配置 number 为幻灯片宽度 | object 为配置项
* @returns
*/
export const slidesToImg = (slides = [], options) => {
let width, option, ispng = true
if (typeof options =='number'){width = options; option = {}}
else {const { width: w, isPng, ...opt } = options;width = w; ispng=isPng; option = opt}
const slidesStore = useSlidesStore()
!!width && slidesStore.setViewportSize(width) // 设置幻灯片宽度
return new Promise(async(resolve) => {
const instance = getCurrentInstance()
console.log('instance', instance)
const slidesDom = []
for(const slide of slides) {
const props = { class: 'c-thumbnail', slide, size: 120, ...option }
const node = h(ThumbnailSlide, props)
slidesDom.push(node)
}
// 渲染组件到body
const props = { class: 'c-thumbnails', style:{position:'absolute',top:0,left:'-200vw'}}
renderComponent('div', props, slidesDom)
let imgs = []
const toImag = ispng? toPng : toJpeg
for(const slide of slidesDom) {
const img = await toImag(slide.el)
imgs.push(img)
}
// console.log('ppt生成图片: ', imgs)
// console.log('图片已生成,正在卸载组件')
!!width && slidesStore.setViewportSize(1000) // 设置幻灯片宽度-恢复
render(null, document.body) // 卸载组件
resolve(imgs)
})
}
/**
* description: ppt 文件转换为图片
* @param {*} file file 为文件对象| arrayBuffer 为数组
* @param {*} options 配置 number 为幻灯片宽度 | object 为配置项
* @returns
*/
export const pptToImg = async(file, options) => {
const { slides } = await PPTXFileToJson(file)
return slidesToImg(slides, options)
}

View File

@ -83,6 +83,8 @@ import useUserStore from '@/store/modules/user' // 用户信息
import ChooseTextbook from '@/components/choose-textbook/index.vue' import ChooseTextbook from '@/components/choose-textbook/index.vue'
import KjListItem from '@/views/prepare/container/kj-list-item.vue' import KjListItem from '@/views/prepare/container/kj-list-item.vue'
import FileImage from '@/components/file-image/index.vue' import FileImage from '@/components/file-image/index.vue'
import progressDialog from '@/views/teachingDesign/container/progress-dialog.vue'
import ThumbnailSlide from '@/AixPPTist/src/views/components/ThumbnailSlide/index.vue'
import {creatAPT, getSmarttalkPage, getModelInfo} from '@/api/file' import {creatAPT, getSmarttalkPage, getModelInfo} from '@/api/file'
import {ArrowDown, Flag, Position} from '@element-plus/icons-vue' import {ArrowDown, Flag, Position} from '@element-plus/icons-vue'
import {asyncLocalFile, parseCataByNode} from "@/utils/talkFile"; import {asyncLocalFile, parseCataByNode} from "@/utils/talkFile";
@ -94,11 +96,11 @@ import {createWindow, toLinkLeftWeb} from "@/utils/tool";
import {ElMessage} from "element-plus"; import {ElMessage} from "element-plus";
import {PPTXFileToJson} from "@/AixPPTist/src/hooks/useImport"; import {PPTXFileToJson} from "@/AixPPTist/src/hooks/useImport";
import * as API_entpcoursefile from "@/api/education/entpcoursefile"; import * as API_entpcoursefile from "@/api/education/entpcoursefile";
import progressDialog from '@/views/teachingDesign/container/progress-dialog.vue'
import msgUtils from "@/plugins/modal"; import msgUtils from "@/plugins/modal";
import * as commUtils from "@/utils/comm"; import * as commUtils from "@/utils/comm";
import * as Api_server from "@/api/apiService"; // import * as Api_server from "@/api/apiService"; //
import useClassTaskStore from '@/store/modules/classTask' import useClassTaskStore from '@/store/modules/classTask'
import { slidesToImg } from '@/utils/ppt' // ppt
const router = useRouter() const router = useRouter()
const userStore = useUserStore().user // const userStore = useUserStore().user //
@ -278,12 +280,14 @@ const handleFileChange = ()=> {
createAIPPTByFile(file) createAIPPTByFile(file)
} }
} }
// pptPPT线
const createAIPPTByFile = async (file)=> { const createAIPPTByFile = async (file)=> {
pgDialog.value.visible = true // pgDialog.value.visible = true
pgDialog.value.pg.percentage = 0 // pgDialog.value.pg.percentage = 0
const resPptJson = await PPTXFileToJson(file) const resPptJson = await PPTXFileToJson(file)
const { def, slides, ...content } = resPptJson const { def, slides, ...content } = resPptJson
//
const thumbnails = await slidesToImg(slides, content.width)
// || 线 // || 线
let completed = 0 let completed = 0
const total = slides.length const total = slides.length
@ -335,6 +339,7 @@ const createAIPPTByFile = async (file)=> {
entpcourseid: resCourse.id, entpcourseid: resCourse.id,
title: '', title: '',
filetype: 'slide', filetype: 'slide',
thumbnails, // -
slides: resSlides, slides: resSlides,
edituserid: userStore.userId edituserid: userStore.userId
} }

View File

@ -205,13 +205,12 @@ import TreeLog from '@/views/prepare/components/treeLog.vue'
import classStart from './container/class-start.vue' // import classStart from './container/class-start.vue' //
import MsgEnum from '@/plugins/imChat/msgEnum' // im import MsgEnum from '@/plugins/imChat/msgEnum' // im
import * as commUtils from "@/utils/comm"; import * as commUtils from "@/utils/comm";
import * as Api_server from "@/api/apiService";
import msgUtils from "@/plugins/modal"; import msgUtils from "@/plugins/modal";
import * as Api_server from "@/api/apiService";
import * as API_entpcoursefile from "@/api/education/entpcoursefile"; import * as API_entpcoursefile from "@/api/education/entpcoursefile";
import { slidesToImg } from '@/utils/ppt' // ppt
import ChatWs from '@/plugins/socket' // socket import ChatWs from '@/plugins/socket' // socket
if (!ChatWs.ws) ChatWs.init() if (!ChatWs.ws) ChatWs.init()
// import Chat from '@/utils/chat' // im
// if (!Chat.imChat) Chat.init()
const toolStore = useToolState() const toolStore = useToolState()
const fs = require('fs') const fs = require('fs')
@ -573,8 +572,9 @@ export default {
},500) },500)
}) })
}, },
openFilePicker(){ async openFilePicker(){
this.$refs.fileInput.click(); this.$refs.fileInput.click();
// const files = await commUtils.getFiles()
}, },
handleFileChange(){ handleFileChange(){
const file = event.target.files[0]; const file = event.target.files[0];
@ -634,6 +634,8 @@ export default {
this.pgDialog.pg.percentage = 0 this.pgDialog.pg.percentage = 0
const resPptJson = await PPTXFileToJson(file) const resPptJson = await PPTXFileToJson(file)
const { def, slides, ...content } = resPptJson const { def, slides, ...content } = resPptJson
//
const thumbnails = await slidesToImg(slides, content.width)
// || 线 // || 线
let completed = 0 let completed = 0
const total = slides.length const total = slides.length
@ -686,6 +688,7 @@ export default {
title: '', title: '',
filetype: 'slide', filetype: 'slide',
slides: resSlides, slides: resSlides,
thumbnails, //
edituserid: this.userStore.userId edituserid: this.userStore.userId
} }
const res_3 = await API_entpcoursefile.batchAddNew(params) const res_3 = await API_entpcoursefile.batchAddNew(params)

View File

@ -94,6 +94,7 @@ import * as API_smarttalk from '@/api/file' // 文件相关api
import msgUtils from '@/plugins/modal' import msgUtils from '@/plugins/modal'
import { getEntpcoursefile } from '@/api/education/entpcoursefile' import { getEntpcoursefile } from '@/api/education/entpcoursefile'
import { createWindow } from '@/utils/tool' // import { createWindow } from '@/utils/tool' //
import { slidesToImg } from '@/utils/ppt' // ppt
const userStore = useUserStore() const userStore = useUserStore()
const pptDialog = ref(false) const pptDialog = ref(false)
@ -323,6 +324,7 @@ const prompt = ref('')
const addAiPPT = async (res) => { 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 let node = courseObj.node
pptDialog.value = false; pptDialog.value = false;
if (!node) return msgUtils.msgWarning('请选择章节?') if (!node) return msgUtils.msgWarning('请选择章节?')
@ -341,6 +343,8 @@ const addAiPPT = async (res) => {
.then(async buffer => { .then(async buffer => {
const resPptJson = await PPTXFileToJson(buffer) const resPptJson = await PPTXFileToJson(buffer)
const { def, slides, ...content } = resPptJson const { def, slides, ...content } = resPptJson
//
const thumbnails = await slidesToImg(slides, content.width)
// || 线 // || 线
let completed = 0 let completed = 0
const total = slides.length const total = slides.length
@ -360,7 +364,7 @@ const addAiPPT = async (res) => {
const smarttalk = await HTTP_SERVER_API('addSmarttalk', { fileId: parentid }) const smarttalk = await HTTP_SERVER_API('addSmarttalk', { fileId: parentid })
if (slides.length > 0) { if (slides.length > 0) {
const resSlides = slides.map(({ id, ...slide }) => JSON.stringify(slide)) const resSlides = slides.map(({ id, ...slide }) => JSON.stringify(slide))
const params = { parentid, filetype: 'slide', title: '', slides: resSlides } const params = { parentid, filetype: 'slide', title: '', thumbnails, slides: resSlides }
const res_3 = await HTTP_SERVER_API('batchAddNew', params) const res_3 = await HTTP_SERVER_API('batchAddNew', params)
if (res_3 && res_3.code == 200) { if (res_3 && res_3.code == 200) {
msgUtils.msgSuccess('生成PPT课件成功') msgUtils.msgSuccess('生成PPT课件成功')