diff --git a/.env.development b/.env.development index b3a7114..422cd04 100644 --- a/.env.development +++ b/.env.development @@ -17,8 +17,8 @@ VITE_APP_RES_FILE_PATH = 'https://file.ysaix.com:7868/src/assets/textbook/booktx VITE_APP_BUILD_BASE_PATH = 'https://file.ysaix.com:7868/' # websocket 地址 -# VITE_APP_WS_URL = 'wss://file.ysaix.com:7868' -VITE_APP_WS_URL = 'ws://192.168.2.16:7865' +VITE_APP_WS_URL = 'wss://file.ysaix.com:7868' +# VITE_APP_WS_URL = 'ws://192.168.2.16:7865' # 是否显示开发工具 VITE_SHOW_DEV_TOOLS = 'true' diff --git a/src/renderer/src/AixPPTist/src/App.vue b/src/renderer/src/AixPPTist/src/App.vue index 77b800d..7cb504f 100644 --- a/src/renderer/src/AixPPTist/src/App.vue +++ b/src/renderer/src/AixPPTist/src/App.vue @@ -28,7 +28,6 @@ import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关ap import { PPTApi } from './api' import { sessionStore } from '@/utils/store' // electron-store 状态管理 import './api/watcher' // 监听 -import './api/classcourse' // 课程相关 const loading = ref(true) const _isPC = isPC() diff --git a/src/renderer/src/AixPPTist/src/api/classcourse.ts b/src/renderer/src/AixPPTist/src/api/classcourse.ts index ca162e1..e78a19d 100644 --- a/src/renderer/src/AixPPTist/src/api/classcourse.ts +++ b/src/renderer/src/AixPPTist/src/api/classcourse.ts @@ -2,21 +2,51 @@ * @author zdg * @description 上课相关内容 */ -import type { Classcourse } from './types' +// import type { Classcourse } from './types' import { sessionStore } from '@/utils/store' // electron-store 状态管理 import * as useStore from '../store' // pptist-状态管理 -import { ChatWs } from '@/plugins/socket' // 聊天socket +import ChatWs from '@/plugins/socket' // 聊天socket +import msgUtils from '@/plugins/modal' // 消息工具 + const screenStore = useStore.useScreenStore() // 全屏-状态管理 const classcourseStore = useStore.useClasscourseStore() // 课堂信息-状态管理 -const classcourse: Classcourse = sessionStore.get('curr.classcourse') // 课堂信息 +const classcourse = sessionStore.get('curr.classcourse') // 课堂信息 -// 如果课堂信息有值,则连接socket -if (!!classcourse) { - // 连接socket - const ws = new ChatWs() - console.log('ws- ',ws) - // ChatWs.connect(classcourse.id) - classcourseStore.setClasscourse(classcourse) +export class Classcourse { + msgObj:ElMessageBox = null // 提示消息对象 + + constructor() { + this.load() + } + /** + * @description 加载 + */ + load() { + // 打开全屏 + screenStore.setScreening(!!classcourse) + // 如果课堂信息有值,则连接socket + if (!!classcourse) { + // 连接socket + if (!ChatWs.ws) ChatWs.init() + ChatWs.id = classcourse.timgroupid // 群组id + console.log('ws- ', classcourse) + classcourseStore.setClasscourse(classcourse) + // 待上课提示 + if (!classcourse.status) { + this.msgObj = { + type: 'success', + title: '系统提示', + message: '公屏课堂已准备完毕,请等待老师开启课堂!', + center: true, + showClose: false, + showCancelButton: false, + showConfirmButton: false, + beforeClose: () => {} + } + msgUtils.ElMessageBox(this.msgObj) + } + } + } } -// 打开全屏 -screenStore.setScreening(!!classcourse) \ No newline at end of file + +export default new Classcourse() \ No newline at end of file diff --git a/src/renderer/src/AixPPTist/src/api/index.ts b/src/renderer/src/AixPPTist/src/api/index.ts index 43bec28..057dcf3 100644 --- a/src/renderer/src/AixPPTist/src/api/index.ts +++ b/src/renderer/src/AixPPTist/src/api/index.ts @@ -3,22 +3,22 @@ * @author zdg * @date 2024-11-26 */ -import { toRaw } from 'vue' +import { toRaw, nextTick } from 'vue' import type { Result } from './types' // 接口类型 import msgUtils from '@/plugins/modal' // 消息工具 import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api import * as API_smarttalk from '@/api/file' // 相关api +import * as Api_server from '@/api/apiService' // 相关api +import * as CreateHomework from '@/views/tool/createHomework' // 统计相关 import * as useStore from '../store' // pptist-状态管理 import { sessionStore } from '@/utils/store' // electron-store 状态管理 import useUserStore from '@/store/modules/user' // 外部-用户信息 -import * as Api_server from '@/api/apiService' // 相关api -import * as commUtils from '@/utils/comm.js' +import { toPng, toJpeg } from 'html-to-image' // 引入html-to-image库 +// import * as commUtils from '@/utils/comm.js' +import { createWindow } from '@/utils/tool' +import { useToolState } from '@/store/modules/tool' const slidesStore = useStore.useSlidesStore() const userStore = useUserStore() - -import { getClassWorkList,getStudentClassWorkData } from '@/views/tool/createHomework' -import {createWindow} from '@/utils/tool' -import { useToolState } from '@/store/modules/tool' const toolStore = useToolState() /** 工具类 */ export class Utils { @@ -60,7 +60,6 @@ export class PPTApi { return new Promise(async (resolve, reject) => { const params: object = { parentid, orderByColumn: 'fileidx', isAsc: 'asc', pageSize: 9999 } const res: Result = await API_entpcoursefile.listEntpcoursefileNew(params) - console.log(res.rows,'res.rows'); if (res.code === 200) { const slides = (res.rows || []).map(o => { if (!!o.datacontent) { @@ -74,7 +73,8 @@ export class PPTApi { // 活动列表处理 const workList = (res.rows || []).map(o => o.activityContent) const workItem = [...res.rows] - slidesStore.updateSlideIndex(0) // 下标0 为第一页 + // 加入活动后刷新ppt数据内容,不跟换为第一页 + // slidesStore.updateSlideIndex(0) // 下标0 为第一页 slidesStore.setSlides(slides) // 写入数据 // 写入作业列表数据 slidesStore.setWorkList(workList) @@ -147,6 +147,8 @@ export class PPTApi { // 更新幻灯片 static updateSlide(data: object): Promise { return new Promise(async (resolve, reject) => { + const thumUrl = await this.getSlideThumUrl() + data.base64Code = thumUrl // 更新缩略图 const res: Result = await API_entpcoursefile.updateEntpcoursefileNew(data) console.log(data,'data'); console.log(res,'dresata'); @@ -191,30 +193,40 @@ export class PPTApi { else msgUtils.msgError(res.msg || '更新失败');return false }) } + // thumbnail-slide thumbnail + static getSlideThumUrl(): Promise { + return nextTick().then(async() => { + const slideIndex = slidesStore.slideIndex + const elements = document.querySelectorAll('.thumbnail-slide') + if (elements.length && slideIndex >= 0) { + const element = elements[slideIndex] + return await toPng(element) + } + return null + }) + } // 图片|音频|视频 转换为在线地址 - static toRousrceUrl =async (o:any) => { - const formData = new FormData() - formData.append('file', o) - const res = await Api_server.Other.uploadFile(formData) - if (res && res.code == 200){ - const url = res?.url - url &&(o.src = url) - return url - } - -} + static toRousrceUrl =async (file: File|any) => { + 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) + return url + } + } } export class Homework{ // 作业弹窗 static async showHomework(id: any) { - let result = await getClassWorkList(id) -   result = await getStudentClassWorkData() -   localStorage.setItem('teachClassWorkItem', JSON.stringify(result[0])); -   toolStore.isTaskWin=true; // 设置打开批改窗口 - //   emit('closeActive') -   createWindow('open-taskwin',{url:'/teachClassTask'}); + let result = await CreateHomework.getClassWorkList(id) + result = await CreateHomework.getStudentClassWorkData() + localStorage.setItem('teachClassWorkItem', JSON.stringify(result[0])); + toolStore.isTaskWin=true; // 设置打开批改窗口 + createWindow('open-taskwin',{url:'/teachClassTask'}); } } export default PPTApi \ No newline at end of file diff --git a/src/renderer/src/AixPPTist/src/api/types.ts b/src/renderer/src/AixPPTist/src/api/types.ts index 8d4f81c..26881e9 100644 --- a/src/renderer/src/AixPPTist/src/api/types.ts +++ b/src/renderer/src/AixPPTist/src/api/types.ts @@ -19,6 +19,147 @@ export interface Classcourse { entpcoursefileid?: number|string, // 课程文件id classid?: number|string, // 班级id entpcourseid?: number|string, // 章节中间表id + timgroupid?: number|string, // ws 群组id plandate?: string, // 计划时间 opendate?: string, // 开课时间 -} \ No newline at end of file +} + +/** + * @description 消息枚举 + * @author zdg + * @date 2021-07-05 14:07:01 + */ + +export class MsgEnum { + /** + * @description: 消息类型 + * + * | 名称 | 含义 | 值(enum) | + * | ---- | ---- | ---- | + * | SYSTEM | 系统消息 | system | + * | TEACHER | 老师消息 | teacher | + * | STUDENT | 学生消息 | student | + * | NOTICE | 通知消息 | notice | + */ + static TYPES = { + /** @desc: 系统消息 */ + SYSTEM: 'system', + /** @desc: 老师消息 */ + TEACHER: 'teacher', + /** @desc: 学生消息 */ + STUDENT: 'student', + /** @desc: 通知消息 */ + NOTICE: 'notice' + } + /** + * @description: 消息头-类型 + * + * | 名称 | 含义 | 值(enum) | + * | ---- | ---- | ---- | + * | --- | 以下为旧定义-消息头 | --- | + * | MSG_closed | 结束课程(下课) | closed | + * | MSG_onlineStatus | 在线状态 | onlineStatus | + * | MSG_pushQuizOfClassWorkdata2Public | 老师端:把选中的学生习题作业,推到大屏 | pushQuizOfClassWorkdata2Public | + * | MSG_pushClassWorkdata2Public | 老师端:把选中的学生作业,推到大屏 | pushClassWorkdata2Public | + * | MSG_shareStudentPresentdata2All | 把某个学生的展示成果数据推给全班所有学生 | shareStudentPresentdata2All | + * | MSG_pushStudentPresentdata2Public | 老师端:课堂展示活动,把选中的学生展示数据,推到大屏 | pushStudentPresentdata2Public | + * | MSG_pushClassWorkPresentList2Public | 老师端:课堂展示活动,任务列表,推到大屏 | pushClassWorkPresentList2Public | + * | MSG_activePageType | 课标研读-分页切换 | activePageType | + * | MSG_slideFlapping | 幻灯片-切换 | slideFlapping | + * | MSG_anmationclick | 幻灯片-动画切换 | anmationclick | + * | MSG_classcourseopen | 群组创建成功 | classcourseopen | + * | MSG_classquizfeedback | 学生的测练结果反馈 | classquizfeedback | + * | MSG_classtaskfeedback | 老师端:接收到学生反馈消息-课堂测练中的其他任务 | classtaskfeedback | + * | MSG_studentfeedback | 老师端:学生反馈的消息,具体要看其中的feedbackkey,类别较繁杂 | studentfeedback | + * | MSG_studentfeedbackcancel | 老师端:学生反馈的消息取消,如取消学会了,取消困惑 | studentfeedbackcancel | + * | MSG_classshowdata | 学生提交的课堂展示数据-要在老师端显示,再由老师选择推送到公屏上 | classshowdata | + * | MSG_classWorkOfPresentDataUpdate | 学生在公屏上展示并完善后,保存后,老师端要更新 | classWorkOfPresentDataUpdate | + * | MSG_classlecturePagesrc | 课堂讲授活动,选择不同的内容 | classlecturePagesrc | + * | --- | 以下为新定义-消息头 | --- | + * | MSG_0001 | 点赞 | 0x0001 | + * | MSG_0002 | xx | 0x0002 | + * | MSG_0003 | xx | 0x0003 | + */ + static HEADS = { + // === 旧定义-消息头(兼容以前) === + /** @desc: 开课 */ + MSG_open : 'open', + /** @desc: 结束课程(下课) */ + MSG_closed : 'closed', + /** @desc: 在线状态 */ + MSG_onlineStatus : 'onlineStatus', + /** @desc: 老师端:把选中的学生习题作业,推到大屏 */ + MSG_pushQuizOfClassWorkdata2Public : 'pushQuizOfClassWorkdata2Public', + /** @desc: 老师端:把选中的学生作业,推到大屏 */ + MSG_pushClassWorkdata2Public : 'pushClassWorkdata2Public', + /** @desc: 把某个学生的展示成果数据推给全班所有学生 */ + MSG_shareStudentPresentdata2All : 'shareStudentPresentdata2All', + /** @desc: 老师端:课堂展示活动,把选中的学生展示数据,推到大屏 */ + MSG_pushStudentPresentdata2Public : 'pushStudentPresentdata2Public', + /** @desc: 老师端:课堂展示活动,任务列表,推到大屏 */ + MSG_pushClassWorkPresentList2Public : 'pushClassWorkPresentList2Public', + /** @desc: 课标研读-分页切换 */ + MSG_activePageType : 'activePageType', + /** @desc: 幻灯片-切换 */ + MSG_slideFlapping : 'slideFlapping', + /** @desc: 幻灯片-动画切换 */ + MSG_anmationclick : 'anmationclick', + /** @desc: 群组创建成功 */ + MSG_classcourseopen : 'classcourseopen', + /** @desc: 学生的测练结果反馈 */ + MSG_classquizfeedback : 'classquizfeedback', + /** @desc: 老师端:接收到学生反馈消息-课堂测练中的其他任务 */ + MSG_classtaskfeedback : 'classtaskfeedback', + /** @desc: 老师端:学生反馈的消息,具体要看其中的feedbackkey,类别较繁杂 */ + MSG_studentfeedback : 'studentfeedback', + /** @desc: 老师端:学生反馈的消息取消,如取消学会了,取消困惑 */ + MSG_studentfeedbackcancel : 'studentfeedbackcancel', + /** @desc: 学生提交的课堂展示数据-要在老师端显示,再由老师选择推送到公屏上 */ + MSG_classshowdata : 'classshowdata', + /** @desc: 学生在公屏上展示并完善后,保存后,老师端要更新 */ + MSG_classWorkOfPresentDataUpdate : 'classWorkOfPresentDataUpdate', + /** @desc: 课堂讲授活动,选择不同的内容 */ + MSG_classlecturePagesrc : 'classlecturePagesrc', + // === 新定义-消息头 === + /** @desc: 课程创建-待开课 */ + MSG_0000: 0x0000, + /** @desc: 点赞 */ + MSG_0001: 0x0001, + /** @desc: 疑惑 */ + MSG_0002: 0x0002, + MSG_0003: 0x0003, + MSG_0004: 0x0004, + MSG_0005: 0x0005, + MSG_0006: 0x0006, + MSG_0007: 0x0007, + MSG_0008: 0x0008, + MSG_0009: 0x0009, + MSG_0010: 0x000a, + MSG_0011: 0x000b, + MSG_0012: 0x000c, + MSG_0013: 0x000d, + MSG_0014: 0x000e, + MSG_0015: 0x000f, + /** @desc: 作业推送 */ + MSG_0016: 0x0010, + MSG_0017: 0x0011, + MSG_0018: 0x0012, + MSG_0019: 0x0013, + MSG_0020: 0x0014, + MSG_0021: 0x0015, + MSG_0022: 0x0016, + MSG_0023: 0x0017, + MSG_0024: 0x0018, + MSG_0025: 0x0019, + MSG_0026: 0x001a, + MSG_0027: 0x001b, + MSG_0028: 0x001c, + MSG_0029: 0x001d, + MSG_0030: 0x001e, + MSG_0031: 0x001f, + MSG_0032: 0x0020, + MSG_0033: 0x0021, + MSG_0034: 0x0022, + MSG_0035: 0x0023, + } +} diff --git a/src/renderer/src/AixPPTist/src/api/watcher.ts b/src/renderer/src/AixPPTist/src/api/watcher.ts index f1f21ba..ff14229 100644 --- a/src/renderer/src/AixPPTist/src/api/watcher.ts +++ b/src/renderer/src/AixPPTist/src/api/watcher.ts @@ -6,13 +6,17 @@ import { watch } from 'vue' import { PPTApi } from './index' import * as store from '../store' import { sessionStore } from '@/utils/store' // electron-store 状态管理 +import { MsgEnum } from './types' // 消息枚举 +import ChatWs from '@/plugins/socket' // 聊天socket +import Classcourse from './classcourse' // 课程相关 +import msgUtils from '@/plugins/modal' // 消息工具 const slidesStore = store.useSlidesStore() +const classcourseStore = store.useClasscourseStore() // 课堂信息-状态管理 const resource = sessionStore.get('curr.resource') // apt 资源 const smarttalk = sessionStore.get('curr.smarttalk') // 备课资源 /** * @description 监听器 */ - // 监听幻灯片内容变化 watch(() => slidesStore.slides, (newVal, oldVal) => { PPTApi.updateSlides(newVal, oldVal) // 更新幻灯片内容 @@ -24,6 +28,20 @@ watch(() => slidesStore.title, (newVal, oldVal) => { updatePPT({title: newVal}) }) +// 消息监听ws +console.log('监听器已开启', ChatWs) +if (ChatWs.ws) { + ChatWs.watch((msg, e) => { + try { + handleMessage(JSON.parse(msg)) + } catch (error) { + console.error('socket 解析异常 ', error, e) + handleMessage(msg) + } + }) +} + +// 更新ppt内容 const updatePPT = async (data) => { if (!resource) return data.id = resource.id @@ -36,4 +54,41 @@ const updatePPT = async (data) => { await PPTApi.updateSmarttalk(params) // 更新ppt内容 sessionStore.set('curr.smarttalk.fileShowName', params.fileShowName) } -} \ No newline at end of file +} + +// ws消息处理 +const handleMessage = (msg) => { + if (typeof msg === 'object'){ + const { head, content, ...other } = msg + switch (head) { + case MsgEnum.HEADS.MSG_open: // 开课 + // 课堂信息不一致 + if (Classcourse.id !== content.id) { + msgUtils.alertError('老师开课信息异常,请重新进入公屏!') + .then(() => { // 点击确定按钮,关闭窗口 + window.close() + }) + } else { // 正常更新数据 + classcourseStore.classcourse.status = 'open' + // 更新课堂信息-关闭警告框 + Classcourse?.msgObj?.onVanish() + } + break + case MsgEnum.HEADS.MSG_slideFlapping: // 幻灯片翻页 + const slideIndex = content.current + slidesStore.updateSlideIndex(slideIndex) // 更新幻灯片下标 + break + case MsgEnum.HEADS.MSG_closed: // 下课: + window.close() // 关闭窗口 + break + default: + break + } + } +} +// console.log('监听器已开启', Classcourse) +// setTimeout(() => { +// console.log('关闭弹窗') +// // Classcourse.msgObj?.close() +// Classcourse?.msgObj?.onVanish() +// }, 10 * 1000) \ No newline at end of file diff --git a/src/renderer/src/AixPPTist/src/hooks/useImport.ts b/src/renderer/src/AixPPTist/src/hooks/useImport.ts index d0e450f..b990686 100644 --- a/src/renderer/src/AixPPTist/src/hooks/useImport.ts +++ b/src/renderer/src/AixPPTist/src/hooks/useImport.ts @@ -33,6 +33,48 @@ const { theme } = storeToRefs(useSlidesStore()) const { addSlidesFromData } = useAddSlidesOrElements() const { isEmptySlide } = useSlideHandler() +const parseLineElement = (el: Shape) => { + let start: [number, number] = [0, 0] + let end: [number, number] = [0, 0] + + if (!el.isFlipV && !el.isFlipH) { // 右下 + start = [0, 0] + end = [el.width, el.height] + } + else if (el.isFlipV && el.isFlipH) { // 左上 + start = [el.width, el.height] + end = [0, 0] + } + else if (el.isFlipV && !el.isFlipH) { // 右上 + start = [0, el.height] + end = [el.width, 0] + } + else { // 左下 + start = [el.width, 0] + end = [0, el.height] + } + + const data: PPTLineElement = { + type: 'line', + id: nanoid(10), + width: el.borderWidth || 1, + left: el.left, + top: el.top, + start, + end, + style: el.borderType, + color: el.borderColor, + points: ['', /straightConnector/.test(el.shapType) ? 'arrow' : ''] + } + if (/bentConnector/.test(el.shapType)) { + data.broken2 = [ + Math.abs(start[0] - end[0]) / 2, + Math.abs(start[1] - end[1]) / 2, + ] + } + + return data +} export default () => { const exporting = ref(false) @@ -59,48 +101,7 @@ export default () => { reader.readAsText(file) } - const parseLineElement = (el: Shape) => { - let start: [number, number] = [0, 0] - let end: [number, number] = [0, 0] - - if (!el.isFlipV && !el.isFlipH) { // 右下 - start = [0, 0] - end = [el.width, el.height] - } - else if (el.isFlipV && el.isFlipH) { // 左上 - start = [el.width, el.height] - end = [0, 0] - } - else if (el.isFlipV && !el.isFlipH) { // 右上 - start = [0, el.height] - end = [el.width, 0] - } - else { // 左下 - start = [el.width, 0] - end = [0, el.height] - } - - const data: PPTLineElement = { - type: 'line', - id: nanoid(10), - width: el.borderWidth || 1, - left: el.left, - top: el.top, - start, - end, - style: el.borderType, - color: el.borderColor, - points: ['', /straightConnector/.test(el.shapType) ? 'arrow' : ''] - } - if (/bentConnector/.test(el.shapType)) { - data.broken2 = [ - Math.abs(start[0] - end[0]) / 2, - Math.abs(start[1] - end[1]) / 2, - ] - } - - return data - } + // 导入PPTX文件 const importPPTXFile = (files: FileList) => { @@ -493,6 +494,7 @@ export default () => { importPPTXFile, PPTXFileToJson, exporting, + parseLineElement } } @@ -664,6 +666,7 @@ export const PPTXFileToJson = (data: File|ArrayBuffer) => { } else if (el.type === 'shape') { if (el.shapType === 'line' || /Connector/.test(el.shapType)) { + // 从返回对象中解构出 xx 函数并调用 const lineElement = parseLineElement(el) slide.elements.push(lineElement) } diff --git a/src/renderer/src/AixPPTist/src/store/classcourse.ts b/src/renderer/src/AixPPTist/src/store/classcourse.ts index 7abd3fe..bd77fbd 100644 --- a/src/renderer/src/AixPPTist/src/store/classcourse.ts +++ b/src/renderer/src/AixPPTist/src/store/classcourse.ts @@ -2,7 +2,7 @@ import { defineStore } from 'pinia' import type { Classcourse } from '../api/types' export interface ClasscourseState { - classcourse: Classcourse + classcourse: Classcourse | any, // 课堂信息 } export const useClasscourseStore = defineStore('classcourse', { diff --git a/src/renderer/src/AixPPTist/src/views/Editor/Toolbar/ElementStylePanel/Active/index.vue b/src/renderer/src/AixPPTist/src/views/Editor/Toolbar/ElementStylePanel/Active/index.vue index c5f5c31..bc658cf 100644 --- a/src/renderer/src/AixPPTist/src/views/Editor/Toolbar/ElementStylePanel/Active/index.vue +++ b/src/renderer/src/AixPPTist/src/views/Editor/Toolbar/ElementStylePanel/Active/index.vue @@ -30,7 +30,7 @@ -
+
- - - + + +
+ +
@@ -142,7 +144,6 @@ const currentCourse = reactive({ worktype: '', }) -const dataList = ref([]) const dialogVisible = ref(false) const tasklist_loading = ref(false) @@ -152,11 +153,6 @@ const taskList = ref([]) // 活动引用的弹窗 const activeVisible = ref(false) -const params = reactive({ - parentid: 14766, - pageSize: 500, - orderby: 'fileidx' -}) const type = ref([ { @@ -179,6 +175,8 @@ const workList = ref([]) // 获取所选择的作业列表 const selectedWorkList = ref([]) +// 活动页面的loading框 +const loadingActive = ref(false) const paramData = ref<{ id: number, activityContent: string }>({} as { id: number, activityContent: string }) @@ -196,7 +194,6 @@ const formatClassWorkFile = async (postData: WorkItem[]): Promise => { } break; case '习题训练': { - console.log(item,'item'); // let workIds = item.quizlist!.map(items => items.id).join(','); // let ress = await listEntpcoursework({ ids: workIds }); // const arr = ress.rows.map((item:{id:number}) => { @@ -213,23 +210,20 @@ const formatClassWorkFile = async (postData: WorkItem[]): Promise => { case '常规作业': { // item.prevData = JSON.parse(item.workcodes); } - } - const arr = paramData.value.activityContent.split(',') - arr.push(item.id.toString()) - await PPTApi.updateSlide(paramData.value) - addWorkList(item) + } + workList.value.push(item) + loadingActive.value = false } await nextTick(); -} -// 添加的活动回显到页面上面去 -const addWorkList = (item: WorkItem) => { - workList.value.push(item) -} -// 删除作业 +}// 删除作业 const handleRemoveDemoActivityClassWork = (item: WorkItem) => { ElMessageBox.confirm('是否确认删除?') .then(() => { - workList.value.splice(workList.value.indexOf(item), 1); + workList.value = [] + const arr = paramData.value.activityContent.split(',') + const filterArr = arr.filter(itemId => itemId!== item.id.toString()) + paramData.value.activityContent = filterArr.join(',') + upDateData() }) .catch(() => { }); } @@ -269,13 +263,51 @@ const savePPtData = async () => { ElMessage.warning('请选择活动') return } + workList.value = [] const arr = selectedWorkList.value.map(item => item.id) // 应该是新加而不是覆盖 - paramData.value.activityContent = arr.join(',') - await PPTApi.updateSlide(paramData.value) + const existingIds = paramData.value.activityContent ? paramData.value.activityContent.split(',') : [] + paramData.value.activityContent = Array.from(new Set([...existingIds, ...arr])).join(',') + upDateData() activeVisible.value = false } +// 获取当前ppt页的数据 +const getCurrentPPtData = async () => { + workList.value = [] + objItem.value = workItem.value[slideIndex.value] + paramData.value.id = objItem.value.id + paramData.value.activityContent = objItem.value?.activityContent + if (objItem.value?.activityContent) { + loadingActive.value = true + const res = await homeworklist({ ids: objItem.value?.activityContent, pageSize: 100 }) + await formatClassWorkFile(res.rows) + } +} + +// 接收习题训练的值 +const getData = async (data: WorkItem) => { + workList.value = [] + if(paramData.value.activityContent){ + const arr = paramData.value.activityContent.split(',') + arr.push(data.id.toString()) + const unitArr = Array.from(new Set(arr)) + paramData.value.activityContent = unitArr.join(',') + }else{ + paramData.value.activityContent = data.id.toString() + } + upDateData() + dialogVisible.value = false +} +const upDateData = async () => { + await PPTApi.updateSlide(paramData.value) + loadingActive.value = true + const res = await homeworklist({ ids: paramData.value.activityContent, pageSize: 100 }) + await formatClassWorkFile(res.rows) + const resource = sessionStore.get('curr.resource') + await PPTApi.getSlideList(resource.id) +} + onMounted(() => { const curNode = sessionStore.get('subject.curNode') as CourseNode currentCourse.textbookId = curNode.rootid @@ -283,33 +315,12 @@ onMounted(() => { currentCourse.levelSecondId = curNode.id currentCourse.coursetitle = curNode.itemtitle currentCourse.node = curNode - listEntpcoursefile(params).then((res: { rows: WorkItem[] }) => { - dataList.value = [...res.rows] - }) objItem.value = workItem.value[slideIndex.value] getCurrentPPtData() }) watch(() => slideIndex.value, () => { getCurrentPPtData() }) -// 获取当前ppt页的数据 -const getCurrentPPtData = async () => { - workList.value = [] - objItem.value = workItem.value[slideIndex.value] - paramData.value.id = objItem.value.id - if (objItem.value?.activityContent) { - paramData.value.activityContent = objItem.value?.activityContent - const res = await homeworklist({ ids: objItem.value?.activityContent, pageSize: 100 }) - await formatClassWorkFile(res.rows) - } -} - -// 接收习题训练的值 -const getData = async (data: WorkItem) => { - console.log(data, 'data') - await formatClassWorkFile([data]) -} - \ No newline at end of file diff --git a/src/renderer/src/api/apiService.js b/src/renderer/src/api/apiService.js index 99deb41..b9ec985 100644 --- a/src/renderer/src/api/apiService.js +++ b/src/renderer/src/api/apiService.js @@ -56,4 +56,5 @@ export class Other { static baseUrl = "/common/upload" // 测试 static uploadFile = data => ApiService.publicHttp(this.baseUrl, data, 'post', null, 'file') + } \ No newline at end of file diff --git a/src/renderer/src/assets/iconfont/iconfont.css b/src/renderer/src/assets/iconfont/iconfont.css index ea364a2..48ced18 100644 --- a/src/renderer/src/assets/iconfont/iconfont.css +++ b/src/renderer/src/assets/iconfont/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 4723712 */ - src: url('iconfont.woff2?t=1732240267757') format('woff2'), - url('iconfont.woff?t=1732240267757') format('woff'), - url('iconfont.ttf?t=1732240267757') format('truetype'); + src: url('iconfont.woff2?t=1733880548695') format('woff2'), + url('iconfont.woff?t=1733880548695') format('woff'), + url('iconfont.ttf?t=1733880548695') format('truetype'); } .iconfont { @@ -13,6 +13,26 @@ -moz-osx-font-smoothing: grayscale; } +.icon-yuyin:before { + content: "\e648"; +} + +.icon-dianying:before { + content: "\e693"; +} + +.icon-jiqirenfushi:before { + content: "\e624"; +} + +.icon-xiangmuicon_maobishufa:before { + content: "\e651"; +} + +.icon-meishu-F:before { + content: "\e638"; +} + .icon-shangchuan:before { content: "\e61b"; } diff --git a/src/renderer/src/assets/iconfont/iconfont.js b/src/renderer/src/assets/iconfont/iconfont.js index a4efe1b..55c5cce 100644 --- a/src/renderer/src/assets/iconfont/iconfont.js +++ b/src/renderer/src/assets/iconfont/iconfont.js @@ -1 +1 @@ -window._iconfont_svg_string_4723712='',(l=>{var h=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var a,v,t,i,z,p=function(h,c){c.parentNode.insertBefore(h,c)};if(h&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(h){console&&console.log(h)}}a=function(){var h,c=document.createElement("div");c.innerHTML=l._iconfont_svg_string_4723712,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(h=document.body).firstChild?p(c,h.firstChild):h.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(a,0):(v=function(){document.removeEventListener("DOMContentLoaded",v,!1),a()},document.addEventListener("DOMContentLoaded",v,!1)):document.attachEvent&&(t=a,i=l.document,z=!1,o(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,M())})}function M(){z||(z=!0,t())}function o(){try{i.documentElement.doScroll("left")}catch(h){return void setTimeout(o,50)}M()}})(window); \ No newline at end of file +window._iconfont_svg_string_4723712='',(l=>{var h=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var a,v,t,i,z,p=function(h,c){c.parentNode.insertBefore(h,c)};if(h&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(h){console&&console.log(h)}}a=function(){var h,c=document.createElement("div");c.innerHTML=l._iconfont_svg_string_4723712,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(h=document.body).firstChild?p(c,h.firstChild):h.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(a,0):(v=function(){document.removeEventListener("DOMContentLoaded",v,!1),a()},document.addEventListener("DOMContentLoaded",v,!1)):document.attachEvent&&(t=a,i=l.document,z=!1,o(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,M())})}function M(){z||(z=!0,t())}function o(){try{i.documentElement.doScroll("left")}catch(h){return void setTimeout(o,50)}M()}})(window); \ No newline at end of file diff --git a/src/renderer/src/assets/iconfont/iconfont.json b/src/renderer/src/assets/iconfont/iconfont.json index fc6689f..7e335cb 100644 --- a/src/renderer/src/assets/iconfont/iconfont.json +++ b/src/renderer/src/assets/iconfont/iconfont.json @@ -5,6 +5,41 @@ "css_prefix_text": "icon-", "description": "", "glyphs": [ + { + "icon_id": "6338162", + "name": "语音生成", + "font_class": "yuyin", + "unicode": "e648", + "unicode_decimal": 58952 + }, + { + "icon_id": "6880941", + "name": "视频生成", + "font_class": "dianying", + "unicode": "e693", + "unicode_decimal": 59027 + }, + { + "icon_id": "11532042", + "name": "数字人生成", + "font_class": "jiqirenfushi", + "unicode": "e624", + "unicode_decimal": 58916 + }, + { + "icon_id": "13522843", + "name": "文生图片", + "font_class": "xiangmuicon_maobishufa", + "unicode": "e651", + "unicode_decimal": 58961 + }, + { + "icon_id": "37635062", + "name": "文生连环画", + "font_class": "meishu-F", + "unicode": "e638", + "unicode_decimal": 58936 + }, { "icon_id": "4942656", "name": "上传", diff --git a/src/renderer/src/assets/iconfont/iconfont.ttf b/src/renderer/src/assets/iconfont/iconfont.ttf index 04dd21f..51df6e8 100644 Binary files a/src/renderer/src/assets/iconfont/iconfont.ttf and b/src/renderer/src/assets/iconfont/iconfont.ttf differ diff --git a/src/renderer/src/assets/iconfont/iconfont.woff b/src/renderer/src/assets/iconfont/iconfont.woff index 0771012..e9b9ac1 100644 Binary files a/src/renderer/src/assets/iconfont/iconfont.woff and b/src/renderer/src/assets/iconfont/iconfont.woff differ diff --git a/src/renderer/src/assets/iconfont/iconfont.woff2 b/src/renderer/src/assets/iconfont/iconfont.woff2 index dd21fa9..ac355d5 100644 Binary files a/src/renderer/src/assets/iconfont/iconfont.woff2 and b/src/renderer/src/assets/iconfont/iconfont.woff2 differ diff --git a/src/renderer/src/components/file-image/index.vue b/src/renderer/src/components/file-image/index.vue index 86da3ca..0c1b1a5 100644 --- a/src/renderer/src/components/file-image/index.vue +++ b/src/renderer/src/components/file-image/index.vue @@ -36,6 +36,11 @@ const getFileTypeIcon = () => { rar: 'icon-rar', apt: 'icon-A', aippt: 'icon-a-ziyuan91', + aiyuyin: 'icon-yuyin', // 语音生成 + aivideo: 'icon-dianying', // 视频生成 + airobot: 'icon-jiqirenfushi', // 数字人生成 + aiimg: 'icon-xiangmuicon_maobishufa', // 文生图片 + aidraw: 'icon-meishu-F', // 文生连环画 } if (iconObj[name]) { return '#' + iconObj[name] diff --git a/src/renderer/src/components/template-study/container/adjust-dialog.vue b/src/renderer/src/components/template-study/container/adjust-dialog.vue index 5cf6251..65316c3 100644 --- a/src/renderer/src/components/template-study/container/adjust-dialog.vue +++ b/src/renderer/src/components/template-study/container/adjust-dialog.vue @@ -29,6 +29,20 @@
+
+ + + {{ curFile.fileName }} + + + + +
@@ -40,13 +54,15 @@ +const clickItem = (index, item) => { + activeIndex.value = index + Object.assign(curFile, item) + emitter.emit('changeCurFile', item) +} +const prevItem = reactive({}) +const onPrevItem = (item) => { + Object.assign(prevItem, item) + prevVisible.value = true +} + +onMounted(() => { + let data = sessionStore.get('subject.curNode') + Object.assign(curNode, data); + // 暂时写死"考试-" 目前只有考试分析才会弹出来 + let jsonKey = `考试-${curNode.edustage}-${curNode.edusubject}` + dataset_id.value = dataSetJson[jsonKey] + getList() +}) + + \ No newline at end of file diff --git a/src/renderer/src/components/template-study/container/left.vue b/src/renderer/src/components/template-study/container/left.vue index ae67aed..02db0d4 100644 --- a/src/renderer/src/components/template-study/container/left.vue +++ b/src/renderer/src/components/template-study/container/left.vue @@ -2,12 +2,13 @@
- {{ curNode.edustage }}{{ curNode.edusubject }}{{ type == 1 ? '课标研读' : '教材分析' }}
+
@@ -24,7 +25,7 @@ const props = defineProps(['curNode', 'type']) const showDialog = ref(false) const onClick = () => { - if (props.type == 1) return + if (props.type != 3) return showDialog.value = true } @@ -37,7 +38,9 @@ onMounted(async () => { if(props.type == 1){ fileurl = `${data.edustage}-${data.edusubject}-课标.txt` } + if(fileurl == '') return pdfUrl.value = import.meta.env.VITE_APP_RES_FILE_PATH + fileurl.replace('.txt', '.pdf') + }) diff --git a/src/renderer/src/components/template-study/container/right.vue b/src/renderer/src/components/template-study/container/right.vue index d62793a..8bc77e1 100644 --- a/src/renderer/src/components/template-study/container/right.vue +++ b/src/renderer/src/components/template-study/container/right.vue @@ -39,7 +39,7 @@ @@ -79,7 +79,7 @@ - + diff --git a/src/renderer/src/views/examination-analysis/container/dialog.vue b/src/renderer/src/views/examination-analysis/container/dialog.vue deleted file mode 100644 index 5e41740..0000000 --- a/src/renderer/src/views/examination-analysis/container/dialog.vue +++ /dev/null @@ -1,268 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/renderer/src/views/examination-analysis/container/edit-dialog.vue b/src/renderer/src/views/examination-analysis/container/edit-dialog.vue deleted file mode 100644 index 06548d6..0000000 --- a/src/renderer/src/views/examination-analysis/container/edit-dialog.vue +++ /dev/null @@ -1,78 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/renderer/src/views/examination-analysis/container/header.vue b/src/renderer/src/views/examination-analysis/container/header.vue deleted file mode 100644 index 880debf..0000000 --- a/src/renderer/src/views/examination-analysis/container/header.vue +++ /dev/null @@ -1,143 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/renderer/src/views/examination-analysis/container/keyword-dialog.vue b/src/renderer/src/views/examination-analysis/container/keyword-dialog.vue deleted file mode 100644 index 4821d6e..0000000 --- a/src/renderer/src/views/examination-analysis/container/keyword-dialog.vue +++ /dev/null @@ -1,161 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/renderer/src/views/examination-analysis/container/pdf.vue b/src/renderer/src/views/examination-analysis/container/pdf.vue deleted file mode 100644 index c871a8d..0000000 --- a/src/renderer/src/views/examination-analysis/container/pdf.vue +++ /dev/null @@ -1,32 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/renderer/src/views/examination-analysis/container/result.vue b/src/renderer/src/views/examination-analysis/container/result.vue deleted file mode 100644 index 304d70a..0000000 --- a/src/renderer/src/views/examination-analysis/container/result.vue +++ /dev/null @@ -1,513 +0,0 @@ - - - - - - diff --git a/src/renderer/src/views/examination-analysis/index.vue b/src/renderer/src/views/examination-analysis/index.vue index 7461e1c..ce74808 100644 --- a/src/renderer/src/views/examination-analysis/index.vue +++ b/src/renderer/src/views/examination-analysis/index.vue @@ -1,52 +1,11 @@ \ No newline at end of file diff --git a/src/renderer/src/views/model/index.vue b/src/renderer/src/views/model/index.vue index 44cd610..a1bfade 100644 --- a/src/renderer/src/views/model/index.vue +++ b/src/renderer/src/views/model/index.vue @@ -248,6 +248,138 @@ const onchange = (item) => { router.push(path) } } +// 获取资源列表 +const getResourceList = async () => { + const entpcourseidarray = courseObj?.entp?.id + if (!entpcourseidarray) return msgUtils.msgWarning('请选择章节?') + const params = { + pageSize: 100, parentid: 0, entpcourseidarray, + orderByColumn: 'timestamp', isAsc: 'desc', + } + const res = await HTTP_SERVER_API('getCourseFileList', params) + if (res?.code == 200) { + sourceOpt.data = res?.rows || [] + } else { + msgUtils.msgWarning('获取资源列表, 请重试') + } +} +// 统一HTTP处理 +const HTTP_SERVER_API = (type, params = {}) => { + switch (type) { + case 'addSmarttalk': { // 获取课程 + const def = { + fileId: '', // 文件id - Entpcoursefile 对应id + fileFlag: 'aippt', + fileShowName: courseObj.coursetitle + '.aippt', + textbookId: courseObj.textbookId, + levelFirstId: courseObj.levelFirstId, + levelSecondId: courseObj.levelSecondId, + 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': { // 添加课程文件 + 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 Promise.resolve(1) + return API_entpcoursefile.addEntpcoursefileReturnId({...def,...params}) + } + case 'getCourseList': { // 获取课程列表 + return API_entpcourse.listEntpcourse(params) + } + case 'getCourseFileList':{ // 获取课程文件列表 + return API_entpcoursefile.listEntpcoursefileNew(params) + } + } +} +// 事件回调 +const handleAll = async(type, row) =>{ + // console.log(type) + switch (type) { + case 'refresh': // 刷新 + getResourceList() + break; + case 'resource': // 资源库 + break; + case 'upload': // 上传 + break; + case 'add':{ // 添加PPT-list - 课程文件 + const enpt = courseObj.entp // 获取课程信息 + if (!enpt) { // 如果没有,就新增课程 + const resid = await HTTP_SERVER_API('addEntpcourse') + courseObj.entp.id = resid + } + // 生成ppt课件-父级 + const p_params = {parentContent: '{"width":1000,"ratio":0.5625}'} + const id = await HTTP_SERVER_API('addEntpcoursefile', p_params) + if (!!id??null) { // 生成第一个幻灯片 + const params = { + parentid: id, + title: '第一页', + filetype: 'slide', + datacontent: '{"elements":[],"background":{"type":"solid","color":"#fff"}}' // json内容 + } + // 生成ppt课件-子级(slide) + await HTTP_SERVER_API('addEntpcoursefile', params) + // 生成备课资源-Smarttalk + await HTTP_SERVER_API('addSmarttalk',{fileId: id}) + // 刷新资源列表 + await getResourceList() + } else { + msgUtils.msgWarning('添加失败!') + } + break; + } + case 'open': { // 打开资源-pptist + if (row.filetype != 'aippt') return msgUtils.msgWarning('暂不支持该类型文件操作!') + sessionStore.set('curr.resource', row) // 缓存当前资源信息 + createWindow('open-win', { + url: '/pptist', // 窗口关闭时,清除缓存 + close: () => { + sessionStore.set('curr.resource', null) // 清除缓存 + getResourceList() // 刷新资源列表 + } + }) const changeClass = async (type, row, other) => { switch(type) { case 'click': { // 点击-打开课件-aippt diff --git a/src/renderer/src/views/prepare/container/class-start.vue b/src/renderer/src/views/prepare/container/class-start.vue index 77eb615..4d06d5c 100644 --- a/src/renderer/src/views/prepare/container/class-start.vue +++ b/src/renderer/src/views/prepare/container/class-start.vue @@ -87,7 +87,8 @@ import { onMounted, reactive, ref, watchEffect, watch, nextTick, toRaw } from 'v import { Refresh } from '@element-plus/icons-vue' import { ElMessage, ElMessageBox } from 'element-plus' // ui框架: 消息提示 import vueQr from 'vue-qr/src/packages/vue-qr.vue' // 插件: 二维码 -import imChat from '@/views/tool/components/imChat.vue' // im-chat-子组件 +// import imChat from '@/views/tool/components/imChat.vue' // im-chat-子组件 +import ChatWs from '@/plugins/socket' // 聊天socket import MsgEnum from '@/plugins/imChat/msgEnum' // 消息头-相关定义(nuem) import * as commUtil from '@/utils/comm' // 工具类-通用 import { toLinkWeb, createWindow, getStaticUrl, sessionStore } from '@/utils/tool' // 工具类-主进程相关 @@ -151,9 +152,11 @@ const open = async (id, classObj) => { teacherForm.form.classcourseid = classObj.id } // 初始化im-chat - // nextTick(async() => { - // chat = await imChatRef.value?.initImChat() - // }) + nextTick(async() => { + // chat = await imChatRef.value?.initImChat() + // 连接socket + if (!ChatWs.ws) ChatWs.init() + }) } } // 关闭弹窗 @@ -267,10 +270,10 @@ const createClasscourse = async () => { setTimeout(() => { msgEl.close() msgEl = ElMessage.warning({message:'正在打开公屏,请稍后...',duration: 0}) - setTimeout(() => { + setTimeout(async() => { msgEl.close() - const classcourse = {...params, id: teacherForm.form.classcourseid} - openPublicScreen(classcourse) + const res = await Http_Classcourse.getClasscourse(teacherForm.form.classcourseid) + openPublicScreen(res.data) }, 2000); }, 1000); } @@ -303,9 +306,10 @@ const classTeachingStart = async () => { // 新版-pptList 打开公屏 if (myClassActive.value.filetype == 'aptist') { const msgEl = ElMessage.warning({message:'正在打开公屏,请稍后...',duration: 0}) - setTimeout(() => { + setTimeout(async () => { msgEl.close() - openPublicScreen({id}) + const res = await Http_Classcourse.getClasscourse(teacherForm.form.classcourseid) + openPublicScreen(res.data) }, 2000); }else { const url = `/teaching/classteaching?classcourseid=${id}&actor=classTeachingOnPublicScreen` @@ -350,6 +354,10 @@ const getQrUrl = async() => { // 打开公屏 const openPublicScreen = (classcourse) => { console.log('打开公屏', classcourse) + // 发送app端待开课消息 + const data = { id: classcourse.id } + ChatWs.sendMsg(MsgEnum.HEADS.MSG_0000, data, {}, ChatWs.TYPES.single, userStore.id) + // 缓存当前资源信息 const resource = toRaw(myClassActive.value) sessionStore.set('curr.resource', resource) // 缓存当前资源信息 sessionStore.set('curr.classcourse', classcourse) // 缓存当前当前上课 @@ -367,25 +375,25 @@ const openPublicScreen = (classcourse) => { // ================== 监听 ======================= // im-chat: 聊天事件 {type, data} -const chatChange = (type, data, ...args) => { - if (type == 'msg') { // im-chat 消息监听 - console.log('msg:===== ',data, args) - // const msgId = (args||[])[0].message_msg_id - const { msgKey:head, msgcontent:msg, senduserid:sendId, msgType } = data - switch(head) { - case MsgEnum.HEADS.MSG_classcourseopen:{ // 开课, 跳转公屏 - const { classcourseid:id } = teacherForm.form - const { classcourseid: imId, status } = data - if (imId == id && status == 'open') { - classTeachingStart() // 开始跳转 - } - break} - default: - console.log('未知消息:', data) - break - } - } -} +// const chatChange = (type, data, ...args) => { +// if (type == 'msg') { // im-chat 消息监听 +// console.log('msg:===== ',data, args) +// // const msgId = (args||[])[0].message_msg_id +// const { msgKey:head, msgcontent:msg, senduserid:sendId, msgType } = data +// switch(head) { +// case MsgEnum.HEADS.MSG_classcourseopen:{ // 开课, 跳转公屏 +// const { classcourseid:id } = teacherForm.form +// const { classcourseid: imId, status } = data +// if (imId == id && status == 'open') { +// classTeachingStart() // 开始跳转 +// } +// break} +// default: +// console.log('未知消息:', data) +// break +// } +// } +// } // 监听-班级id watch(() => classForm.form.classid, (val)=> { diff --git a/src/renderer/src/views/prepare/index.vue b/src/renderer/src/views/prepare/index.vue index a10cbe2..c025ddc 100644 --- a/src/renderer/src/views/prepare/index.vue +++ b/src/renderer/src/views/prepare/index.vue @@ -202,17 +202,14 @@ import ClassReserv from '@/views/classManage/classReserv.vue' import TreeLog from '@/views/prepare/components/treeLog.vue' import classStart from './container/class-start.vue' // 预备上课 import MsgEnum from '@/plugins/imChat/msgEnum' // im 消息枚举 -import Chat from '@/utils/chat' import * as commUtils from "@/utils/comm"; import * as Api_server from "@/api/apiService"; import msgUtils from "@/plugins/modal"; -import * as API_entpcoursefile from "@/api/education/entpcoursefile"; // im 登录初始化 -if (!Chat.imChat) Chat.init() +import * as API_entpcoursefile from "@/api/education/entpcoursefile"; +import ChatWs from '@/plugins/socket' // 聊天socket +if (!ChatWs.ws) ChatWs.init() // import Chat from '@/utils/chat' // im 登录初始化 // if (!Chat.imChat) Chat.init() -// import ChatWs from '@/plugins/socket' -// console.log('xxxx',ChatWs) -// ChatWs.watch((data,e) => console.log('ws', data, e)) const toolStore = useToolState() const fs = require('fs') @@ -344,6 +341,8 @@ export default { // } // }, methods: { + // 延时 + sleep(ms){return new Promise(resolve => setTimeout(resolve, ms))}, addAiPPT(item) { this.currentFileList.unshift(item.resData) KjListItem.methods.openFileWin(item.resData); @@ -446,6 +445,33 @@ export default { ElMessage.warning('该功能暂未开放!') break } + case 'wsApp': { // 发送app端待开课消息 + // console.log('wsApp', row) + const head = MsgEnum.HEADS.MSG_0000 + const data = { id: row.id } + const type = ChatWs.TYPES.single + const userId = this.userStore.userId + ElMessage.success('APP端待开课消息-发送成功!') + await this.sleep(1000) // 等待1s + // 打开公屏 + const msgEl = ElMessage.warning({message:'正在打开公屏,请稍后...',duration: 0}) + const res = await getEntpcoursefile(row.entpcoursefileid) + await this.sleep(2000) // 等待2s + ChatWs.sendMsg(head, data, null, type, userId) + msgEl.close() // 关闭提示 + const resource = res?.data||{} + const classcourse = row + sessionStore.set('curr.resource', resource) // 缓存当前资源信息 + sessionStore.set('curr.classcourse', classcourse) // 缓存当前当前上课 + createWindow('open-win', { + url: '/pptist', // 窗口关闭时,清除缓存 + close: () => { + sessionStore.set('curr.resource', null) // 清除缓存 + sessionStore.set('curr.classcourse', null) // 清除缓存 + } + }) + break + } default: break } @@ -524,7 +550,7 @@ export default { if (!!o.src) { // 如果有src就转换 const isBase64 = /^data:image\/(\w+);base64,/.test(o.src) const isBlobUrl = /^blob:/.test(o.src) - console.log('isBase64', o, isBase64) + // console.log('isBase64', o, isBase64) if (isBase64) { const bolb = commUtils.base64ToBlob(o.src) const fileName = Date.now() + '.png' @@ -539,11 +565,29 @@ export default { 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 this.toRousrceUrl(o.background.image) - if (o?.elements) o.elements.forEach(async o => {await this.toRousrceUrl(o)}) + // if (o?.elements) o.elements.forEach(async o => {await this.toRousrceUrl(o)}) + if(o?.elements){ + for (let element of o.elements) { + await this.toRousrceUrl(element); + } + } }, async createAIPPTByFile(file) { this.pgDialog.visible = true @@ -559,6 +603,8 @@ export default { // 设置进度条 this.pgDialog.pg.percentage = Math.floor(completed / total * 100) } + console.log('结束', slides) + return this.pgDialog.pg.percentage = 0 this.pgDialog.visible = false listEntpcourse({ @@ -595,6 +641,8 @@ export default { }).then(async (res) => { const resSlides = slides.map(({id, ...slide}) => JSON.stringify(slide)) + console.log(resSlides) + return let params = { parentid: slideid, entpid: resCourse.entpid, diff --git a/src/renderer/src/views/teachingDesign/container/right.vue b/src/renderer/src/views/teachingDesign/container/right.vue index 19f7fd2..24af068 100644 --- a/src/renderer/src/views/teachingDesign/container/right.vue +++ b/src/renderer/src/views/teachingDesign/container/right.vue @@ -330,11 +330,27 @@ const toRousrceUrl = async(o) => { 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) o.elements.forEach(async o => {await toRousrceUrl(o)}) + if(o?.elements){ + for (let element of o.elements) { + await this.toRousrceUrl(element); + } + } } // ======== zdg end ============