diff --git a/src/renderer/src/AixPPTist/src/api/chat.ts b/src/renderer/src/AixPPTist/src/api/chat.ts index 6cf016e..70574e2 100644 --- a/src/renderer/src/AixPPTist/src/api/chat.ts +++ b/src/renderer/src/AixPPTist/src/api/chat.ts @@ -4,21 +4,51 @@ import ChatWs from '@/plugins/socket' // 聊天socket import { sessionStore } from '@/utils/store' // electron-store 状态管理 +import { useClasscourseStore } from '../store' import * as API_classcourse from '@/api/teaching/classcourse' // 后端api +import { MsgEnum } from './types' +// import msgUtils from '@/plugins/modal' // 消息工具 export default () => { const classcourse = sessionStore.get('curr.classcourse') // 课堂信息 + const courseId = classcourse?.id // 课堂id const timgroupid = classcourse?.timgroupid // 群组id + const classcourseStore = useClasscourseStore() // 课堂信息-状态管理 if (!ChatWs.ws) ChatWs.init() + // 开课消息 + const startCourse = async() => { + // await API_classcourse.updateClasscourse({ id: classcourse.id, status: 'open' }) + ChatWs.sendMsg('open', {id: courseId}) + return Promise.resolve() + } // 下课消息 const exitCourse = async() => { if(!timgroupid) throw new Error('未获取到群组ID') - await API_classcourse.updateClasscourse({ id: classcourse.id, status: 'closed' }) + await API_classcourse.updateClasscourse({ id: courseId, status: 'closed' }) return ChatWs.closedCourse(timgroupid) } + // 翻页消息 + const slideFlapping = (msg:object) => { + return new Promise(async (resolve, reject) => { + const isWs = !!ChatWs.ws && ChatWs.ws.readyState === 1 // 是否有socket连接 + if(!timgroupid) return reject('未获取到群组ID') + else if(!isWs) return reject('信异常,请重试!') + const {current: paging, animationSteps: cartoonTimes} = msg || {} + const head = MsgEnum.HEADS.MSG_slideFlapping + ChatWs.sendMsg(head, msg) // 发送消息 + API_classcourse.setPaging({ id: courseId, paging, cartoonTimes}) + // 更新本地缓存 + sessionStore.set('curr.classcourse.paging', paging) + sessionStore.set('curr.classcourse.cartoonTimes', cartoonTimes) + classcourseStore.classcourse.paging = paging + classcourseStore.classcourse.cartoonTimes = cartoonTimes + return resolve(true) + }) + } return { - exitCourse, - classcourse, groupid: timgroupid, + classcourse, + exitCourse, + slideFlapping, } } \ No newline at end of file diff --git a/src/renderer/src/AixPPTist/src/api/classcourse.ts b/src/renderer/src/AixPPTist/src/api/classcourse.ts index 21e7296..c92a6b6 100644 --- a/src/renderer/src/AixPPTist/src/api/classcourse.ts +++ b/src/renderer/src/AixPPTist/src/api/classcourse.ts @@ -14,6 +14,7 @@ const slidesStore = useStore.useSlidesStore() // 幻灯片-状态管理 const screenStore = useStore.useScreenStore() // 全屏-状态管理 const classcourseStore = useStore.useClasscourseStore() // 课堂信息-状态管理 const classcourse = sessionStore.get('curr.classcourse') // 课堂信息 +const isPublic = sessionStore.get('curr.isPublic') // 是否公屏开课 export class Classcourse { msgObj:ElMessageBox = null // 提示消息对象 @@ -36,8 +37,13 @@ export class Classcourse { // 如果课堂信息有值,则连接socket if (isCourse) { // 连接socket - if (!ChatWs.ws) ChatWs.init() ChatWs.id = classcourse.timgroupid // 群组id + if (!ChatWs.ws) { + ChatWs.init().then(_ => { + isPublic && ChatWs.sendMsg('open', {id: classcourse.id}) + // isPublic && console.log('socket-开课消息-已发送') + }) + } this.classcourse = classcourse // 课堂信息 this.id = classcourse.id // 课堂id // 如果课堂信息有paging,则更新当前页码 @@ -46,7 +52,7 @@ export class Classcourse { // 如果课堂信息有paging,则更新动画播放状态 const isAnim = !!cartoonTimes || cartoonTimes === 0 if (isPaging) slidesStore.updateSlideIndex(paging) - if (isAnim) slidesStore.updateAnimationIndex(cartoonTimes+1) + if (isAnim) slidesStore.updateAnimationIndex(cartoonTimes) // 课堂信息-状态管理 classcourseStore.setClasscourse(classcourse) // 待上课提示 diff --git a/src/renderer/src/AixPPTist/src/api/types.ts b/src/renderer/src/AixPPTist/src/api/types.ts index e762748..3678ed4 100644 --- a/src/renderer/src/AixPPTist/src/api/types.ts +++ b/src/renderer/src/AixPPTist/src/api/types.ts @@ -124,6 +124,8 @@ export class MsgEnum { MSG_classlecturePagesrc : 'classlecturePagesrc', /** @desc: 课堂作业|活动 */ MSG_homework : 'HOMEWORK', + /** @desc: 公屏 - 课堂作业|活动 */ + MSG_pushSreen_work : 'pushSreen_work', /** @desc: 点赞 */ MSG_dz : 'dz', /** @desc: 疑惑 */ diff --git a/src/renderer/src/AixPPTist/src/api/watcher.ts b/src/renderer/src/AixPPTist/src/api/watcher.ts index 220ed53..34c3294 100644 --- a/src/renderer/src/AixPPTist/src/api/watcher.ts +++ b/src/renderer/src/AixPPTist/src/api/watcher.ts @@ -23,7 +23,7 @@ export default () => { const classcourseStore = store.useClasscourseStore() // 课堂信息-状态管理 const resource = sessionStore.get('curr.resource') // apt 资源 const smarttalk = sessionStore.get('curr.smarttalk') // 备课资源 - const { execNext, turnPrevSlide } = useExecPlay() + const { execNext, turnPrevSlide } = useExecPlay(false) // 不加载钩子 // 监听幻灯片内容变化 watch(() => slidesStore.slides, (newVal, oldVal) => { PPTApi.updateSlides(newVal, oldVal) // 更新幻灯片内容 @@ -98,14 +98,21 @@ export default () => { break case MsgEnum.HEADS.MSG_slideFlapping: // 幻灯片翻页 const slideIndex = content?.current || 0 - const type = content?.animation + const type = content?.animation // 上下动作 + const steps = content?.animationSteps // 动画步骤 if (type === 'Nextsteps') execNext(true) // 下一步-异步动画 else if (type === 'Previoustep') turnPrevSlide() // 上一步清空-动画 else slidesStore.updateSlideIndex(slideIndex) // 更新幻灯片下标 + // 更新本地缓存 + sessionStore.set('curr.classcourse.paging', slideIndex) + sessionStore.set('curr.classcourse.cartoonTimes', steps) + classcourseStore.classcourse.paging = slideIndex + classcourseStore.classcourse.cartoonTimes = steps break - case MsgEnum.HEADS.MSG_homework: // 作业|活动-布置 - if (!content.classWorkId) return - Homework.showHomework(content.classWorkId) + // case MsgEnum.HEADS.MSG_homework: // 作业|活动-布置 不处理 + case MsgEnum.HEADS.MSG_pushSreen_work: // 打开-作业|活动 + if (!content.id) return + Homework.showHomework(content.id) break case MsgEnum.HEADS.MSG_closed: // 下课: close() diff --git a/src/renderer/src/AixPPTist/src/views/Screen/hooks/useExecPlay.ts b/src/renderer/src/AixPPTist/src/views/Screen/hooks/useExecPlay.ts index 860e37a..cb59465 100644 --- a/src/renderer/src/AixPPTist/src/views/Screen/hooks/useExecPlay.ts +++ b/src/renderer/src/AixPPTist/src/views/Screen/hooks/useExecPlay.ts @@ -6,8 +6,13 @@ import { KEYS } from '../../../configs/hotkey' import { ANIMATION_CLASS_PREFIX } from '../../../configs/animation' import message from '../../../utils/message' import emitter from '@/utils/mitt'; +import Chat from '../../../api/chat' // 聊天封装 +// import ChatWs from '@/plugins/socket' // 聊天socket +// import { MsgEnum } from '../../../api/types' // 消息枚举 -export default () => { +export default (isLoader?: boolean = true) => { + // isLoader 是否执行 onMounted, onUnmounted + const chatApi = Chat() const slidesStore = useSlidesStore() const classcourseStore = useClasscourseStore() // 课堂信息-状态管理 const { slides, slideIndex, formatedAnimations, animationIndex } = storeToRefs(slidesStore) @@ -71,14 +76,15 @@ export default () => { elRef.addEventListener('animationend', handleAnimationEnd, { once: true }) } } - - onMounted(() => { - const firstAnimations = formatedAnimations.value[0] - if (firstAnimations && firstAnimations.animations.length) { - const autoExecFirstAnimations = firstAnimations.animations.every(item => item.trigger === 'auto' || item.trigger === 'meantime') - if (autoExecFirstAnimations) runAnimation() - } - }) + if (isLoader) { // 加载相关钩子 + onMounted(() => { + const firstAnimations = formatedAnimations.value[0] + if (firstAnimations && firstAnimations.animations.length) { + const autoExecFirstAnimations = firstAnimations.animations.every(item => item.trigger === 'auto' || item.trigger === 'meantime') + if (autoExecFirstAnimations) runAnimation() + } + }) + } // 撤销元素动画,除了将索引前移外,还需要清除动画状态 const revokeAnimation = () => { @@ -142,7 +148,6 @@ export default () => { inAnimation.value = false } const execNext = (isAsync: boolean) => { - console.log('execNext', isAsync) if (formatedAnimations.value.length && animationIndex.value < formatedAnimations.value.length) { runAnimation(isAsync) } @@ -178,8 +183,7 @@ export default () => { // 鼠标滚动翻页 const mousewheelListener = (e: WheelEvent) => { // console.log('mousewheel', e) - // 课堂信息存在时,不允许翻页 - if (!!classcourseStore.classcourse) e.preventDefault() + e.preventDefault() // 阻止默认事件 mousewheelListenerThrottle(e) } const mousewheelListenerThrottle = throttle(function(e: WheelEvent) { @@ -210,12 +214,17 @@ export default () => { } } // 向上翻页/向下翻页 - const turning = (e, type) => { + const turning = async (e, type) => { e.preventDefault() // 阻止默认事件 - // 课堂信息存在时,不允许翻页 - if (!!classcourseStore.classcourse) return if (type === 'prev') execPrev() else if (type === 'next') execNext() + if (classcourseStore.classcourse) { // 上课中 + const current = slideIndex.value + const animationSteps = animationIndex.value + const animation = type == 'next'?'Nextsteps':'Previoustep' + const msg = { current, animation, animationSteps} + chatApi.slideFlapping(msg) + } } // 快捷键翻页 const keydownListener = (e: KeyboardEvent) => { @@ -230,10 +239,11 @@ export default () => { key === KEYS.PAGEDOWN ) turning(e, 'next') } - - onMounted(() => {document.addEventListener('keydown', keydownListener)}) - onUnmounted(() => {document.removeEventListener('keydown', keydownListener)}) - + if (isLoader) { // 加载相关钩子 + onMounted(() => {document.addEventListener('keydown', keydownListener)}) + onUnmounted(() => {document.removeEventListener('keydown', keydownListener)}) + } + // 切换到上一张/上一张幻灯片(无视元素的入场动画) const turnPrevSlide = () => { slidesStore.updateSlideIndex(slideIndex.value - 1) diff --git a/src/renderer/src/api/teaching/classcourse.js b/src/renderer/src/api/teaching/classcourse.js index d312f85..d227b2d 100644 --- a/src/renderer/src/api/teaching/classcourse.js +++ b/src/renderer/src/api/teaching/classcourse.js @@ -95,3 +95,11 @@ export function getCourseTeachingMsg(id) { }) } +export function setPaging(data) { + return request({ + url: '/education/classcourse/record/paging', + method: 'post', + data + }) +} + diff --git a/src/renderer/src/components/choose-textbook/experimentBook.vue b/src/renderer/src/components/choose-textbook/experimentBook.vue new file mode 100644 index 0000000..cf14940 --- /dev/null +++ b/src/renderer/src/components/choose-textbook/experimentBook.vue @@ -0,0 +1,333 @@ + + + + + {{ curBook.data.itemtitle }} + + + + + + {{ node.label }} + + + + + + + + + + 切换教材 + + + + + + + + + + + + {{ item.itemtitle }} + + + + + + + + + + \ No newline at end of file diff --git a/src/renderer/src/directive/scroll.js b/src/renderer/src/directive/scroll.js new file mode 100644 index 0000000..83821e3 --- /dev/null +++ b/src/renderer/src/directive/scroll.js @@ -0,0 +1,26 @@ +/** + * 无限滚动 + */ +import { nextTick } from 'vue' +const mountedHook = async (el, binding) => { + console.log(el, binding) + const value = binding.value + if (typeof value !== 'function') return console.error('v-scroll must be a function') + await nextTick() +} +export default { + // Hooks for Vue3 + mounted(el, binding) { + mountedHook(el, binding) + }, + // Hooks for Vue2 + inserted(el, binding) { + mountedHook(el, binding) + }, + + update(el, binding){ + }, + updated(el, binding){ + + }, +} \ No newline at end of file diff --git a/src/renderer/src/layout/components/Aside.vue b/src/renderer/src/layout/components/Aside.vue index 7a421c6..3c6456f 100644 --- a/src/renderer/src/layout/components/Aside.vue +++ b/src/renderer/src/layout/components/Aside.vue @@ -182,9 +182,9 @@ watch( const logout = () => { - const hasClass = sessionStore.has('activeClass.id') - const hasTool = sessionStore.get('isToolWin') - if (hasClass || hasTool) return ElMessage.warning('当前正在上课,请先结束上课') + + if(!!sessionstore.get('curr.classcourse'))return ElMessage.warning('当前正在上课,请先结束上课') + ElMessageBox.confirm('确认退出系统吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', @@ -212,7 +212,7 @@ onMounted(() => { \ No newline at end of file diff --git a/src/renderer/src/views/resource/container/exper-list.vue b/src/renderer/src/views/resource/container/exper-list.vue new file mode 100644 index 0000000..96ce1ca --- /dev/null +++ b/src/renderer/src/views/resource/container/exper-list.vue @@ -0,0 +1,214 @@ + + + + + + + + + + {{ item.title }} + + + + + + + + + + + + + + + + + {{ item.fileShowName }} + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/renderer/src/views/resource/container/resoure-search.vue b/src/renderer/src/views/resource/container/resoure-search.vue index 97c3bf2..a74fbbb 100644 --- a/src/renderer/src/views/resource/container/resoure-search.vue +++ b/src/renderer/src/views/resource/container/resoure-search.vue @@ -3,19 +3,21 @@ {{ item.label + :type="sourceStore.activeIndex == item.id ? 'primary' : ''" + @click="sourceStore.changeType(item)" :disabled="item.disabled">{{ item.label }} - - - - - - + + + + + + + + @@ -34,22 +36,24 @@ - - - + + + + - - - - - {{ - item.label }} + + + + + {{ + item.label }} + @@ -63,7 +67,10 @@ import {watch,ref,onMounted} from 'vue' import useResoureStore from '../store' import {coursewareTypeList} from '@/utils/resourceDict' +// 是否是第三方资源 const isThird = ref(false) +//判断是不是进入实验室 +const isExper = ref(false) const sourceStore = useResoureStore() // 防抖函数 const debounce = (fn, t) => { @@ -85,6 +92,9 @@ onMounted(() => { watch(() => sourceStore.query.fileSource,() => { sourceStore.query.fileSource === '第三方'?isThird.value = true:isThird.value = false }) +watch(() => sourceStore.query.orderByColumn,() => { + sourceStore.query.orderByColumn === 'uploadTime'?isExper.value = true:isExper.value = false +})