|
@@ -100,6 +100,16 @@
|
|
|
<span class="btn-text">{{ isSubmitting ? '提交中...' : '作业提交' }}</span>
|
|
|
</div>
|
|
|
|
|
|
+ <!-- 刷新iframe按钮 -->
|
|
|
+ <div class="refresh-page-btn"
|
|
|
+ v-if="currentSlideHasIframe"
|
|
|
+ :style="{ right: getRefreshButtonRight() + 'px' }"
|
|
|
+ @click="handleRefreshPage"
|
|
|
+ v-tooltip="'刷新iframe内容'">
|
|
|
+ <Refresh class="tool-btn" />
|
|
|
+ <span class="btn-text">刷新</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
<!-- 功能组件 -->
|
|
|
<SlideThumbnails v-if="slideThumbnailModelVisible" :turnSlideToIndex="goToSlide"
|
|
|
@close="slideThumbnailModelVisible = false" />
|
|
@@ -239,6 +249,7 @@ import DialoguePanel from './components/DialoguePanel.vue'
|
|
|
import ChoiceStatistics from './components/ChoiceStatistics.vue'
|
|
|
import * as Y from 'yjs'
|
|
|
import { WebsocketProvider } from 'y-websocket'
|
|
|
+import { Refresh } from '@icon-park/vue-next'
|
|
|
|
|
|
|
|
|
// 导入图片资源
|
|
@@ -325,9 +336,7 @@ const slidePanelCollapsed = ref(false)
|
|
|
// 右侧面板当前显示的内容:'homework' | 'dialogue' | 'choice'
|
|
|
const rightPanelMode = ref<'homework' | 'dialogue' | 'choice'>('homework')
|
|
|
|
|
|
-// 定时器相关
|
|
|
-const workTimer = ref<number | null>(null)
|
|
|
-const workUpdateInterval = 5000 // 5秒更新一次
|
|
|
+// 移除定时器相关代码,改用socket监听
|
|
|
|
|
|
const courseDetail = ref<any>({})
|
|
|
const studentArray = ref<any>([])
|
|
@@ -397,26 +406,7 @@ const autoSwitchToAvailablePanel = () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 启动作业更新定时器
|
|
|
-const startWorkTimer = () => {
|
|
|
- if (workTimer.value) {
|
|
|
- clearInterval(workTimer.value)
|
|
|
- }
|
|
|
- workTimer.value = setInterval(() => {
|
|
|
- console.log('定时器触发,检查作业更新')
|
|
|
- getWork(true) // 传入 true 表示是更新模式
|
|
|
- }, workUpdateInterval)
|
|
|
- console.log('作业更新定时器已启动,间隔:', workUpdateInterval, 'ms')
|
|
|
-}
|
|
|
-
|
|
|
-// 停止作业更新定时器
|
|
|
-const stopWorkTimer = () => {
|
|
|
- if (workTimer.value) {
|
|
|
- clearInterval(workTimer.value)
|
|
|
- workTimer.value = null
|
|
|
- console.log('作业更新定时器已停止')
|
|
|
- }
|
|
|
-}
|
|
|
+// 移除定时器相关函数,改用socket监听
|
|
|
|
|
|
// 收缩/展开后重新计算中间画布尺寸(在 DOM 更新并完成过渡后)
|
|
|
watch([() => workPanelCollapsed.value, () => slidePanelCollapsed.value], async () => {
|
|
@@ -586,7 +576,9 @@ const nextSlide = () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 监听slideIndex变化,调用getWork并管理定时器
|
|
|
+
|
|
|
+
|
|
|
+// 监听slideIndex变化,调用getWork
|
|
|
watch(() => slideIndex.value, (newIndex, oldIndex) => {
|
|
|
console.log('slideIndex变化,调用getWork', { newIndex, oldIndex })
|
|
|
if (newIndex !== oldIndex && typeof newIndex === 'number') {
|
|
@@ -594,15 +586,11 @@ watch(() => slideIndex.value, (newIndex, oldIndex) => {
|
|
|
const hasIframe = currentSlideHasIframe.value
|
|
|
|
|
|
if (hasIframe) {
|
|
|
- console.log('当前页面有iframe,启动作业更新定时器')
|
|
|
- startWorkTimer()
|
|
|
+ console.log('当前页面有iframe,获取作业数据')
|
|
|
console.log('触发getWork,当前幻灯片索引:', newIndex)
|
|
|
getWork()
|
|
|
}
|
|
|
- else {
|
|
|
- console.log('当前页面无iframe,停止作业更新定时器')
|
|
|
- stopWorkTimer()
|
|
|
- }
|
|
|
+
|
|
|
if (props.type == '1' && isFollowModeActive.value && isCreator.value) {
|
|
|
api.updateCourseFollowC(newIndex, props.courseid as string)
|
|
|
sendMessage({slideIndex: newIndex, courseid: props.courseid, type: 'slideIndex'})
|
|
@@ -766,7 +754,7 @@ const processIframeLinks = async () => {
|
|
|
return element
|
|
|
}
|
|
|
}
|
|
|
- else if (toolType == 45) {
|
|
|
+ else if (toolType == 73) {
|
|
|
hasIframe = true
|
|
|
|
|
|
// 先尝试获取iframe的contentWindow,如果获取不到再使用HTML方式
|
|
@@ -873,10 +861,9 @@ const importJSON = (jsonData: any) => {
|
|
|
// 延迟500ms后重新显示组件,确保重新渲染完成
|
|
|
setTimeout(() => {
|
|
|
showSlideList.value = true
|
|
|
- // 只有当当前页面存在iframe时才启动作业更新定时器
|
|
|
+ // 只有当当前页面存在iframe时才获取作业数据
|
|
|
if (currentSlideHasIframe.value && props.type == '1') {
|
|
|
getWork()
|
|
|
- startWorkTimer()
|
|
|
}
|
|
|
selectCourseSLook()
|
|
|
console.log('组件重新渲染完成')
|
|
@@ -1058,6 +1045,14 @@ const handleHomeworkSubmit = async () => {
|
|
|
console.log('submitWork同步执行完成')
|
|
|
message.success('作业提交成功')
|
|
|
hasSubmitWork = true
|
|
|
+
|
|
|
+ // 发送作业提交成功的socket消息
|
|
|
+ sendMessage({
|
|
|
+ type: 'homework_submitted',
|
|
|
+ courseid: props.courseid,
|
|
|
+ slideIndex: slideIndex.value,
|
|
|
+ userid: props.userid
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
break
|
|
@@ -1082,6 +1077,14 @@ const handleHomeworkSubmit = async () => {
|
|
|
await submitWork(iframeSlideIndex, '72', Cow, '20')
|
|
|
message.success('作业提交成功')
|
|
|
hasSubmitWork = true
|
|
|
+
|
|
|
+ // 发送作业提交成功的socket消息
|
|
|
+ sendMessage({
|
|
|
+ type: 'homework_submitted',
|
|
|
+ courseid: props.courseid,
|
|
|
+ slideIndex: slideIndex.value,
|
|
|
+ userid: props.userid
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
@@ -1153,6 +1156,14 @@ const handleHomeworkSubmit = async () => {
|
|
|
await submitWork(slideIndex.value, '73', imageUrl, '1') // 73表示截图工具,21表示图片类型
|
|
|
message.success('页面截图提交成功')
|
|
|
hasSubmitWork = true
|
|
|
+
|
|
|
+ // 发送作业提交成功的socket消息
|
|
|
+ sendMessage({
|
|
|
+ type: 'homework_submitted',
|
|
|
+ courseid: props.courseid,
|
|
|
+ slideIndex: slideIndex.value,
|
|
|
+ userid: props.userid
|
|
|
+ })
|
|
|
}
|
|
|
catch (error) {
|
|
|
console.error('截图提交失败:', error)
|
|
@@ -1176,6 +1187,66 @@ const handleHomeworkSubmit = async () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// 刷新iframe功能
|
|
|
+const handleRefreshPage = () => {
|
|
|
+ console.log('刷新iframe按钮被点击')
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 获取当前幻灯片中的所有iframe元素
|
|
|
+ const iframes = document.querySelectorAll('.viewer-canvas .screen-slide')[slideIndex.value].querySelectorAll('iframe')
|
|
|
+ console.log('找到iframe元素数量:', iframes.length)
|
|
|
+
|
|
|
+ if (iframes.length === 0) {
|
|
|
+ message.warning('当前页面没有找到iframe元素')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ let refreshedCount = 0
|
|
|
+
|
|
|
+ // 遍历所有iframe并刷新
|
|
|
+ for (let i = 0; i < iframes.length; i++) {
|
|
|
+ const iframe = iframes[i] as HTMLIFrameElement
|
|
|
+ const currentSrc = iframe.src
|
|
|
+
|
|
|
+ if (currentSrc) {
|
|
|
+ console.log(`刷新iframe ${i + 1}:`, currentSrc)
|
|
|
+
|
|
|
+ // 保存当前src
|
|
|
+ const originalSrc = currentSrc
|
|
|
+
|
|
|
+ // 清空src触发刷新
|
|
|
+ iframe.src = ''
|
|
|
+
|
|
|
+ // 短暂延迟后恢复src,确保刷新生效
|
|
|
+ setTimeout(() => {
|
|
|
+ iframe.src = originalSrc
|
|
|
+ console.log(`iframe ${i + 1} 刷新完成`)
|
|
|
+ }, 100)
|
|
|
+
|
|
|
+ refreshedCount++
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (refreshedCount > 0) {
|
|
|
+ message.success(`刷新完成`)
|
|
|
+
|
|
|
+ // 如果当前页面有iframe,重新获取作业数据
|
|
|
+ if (currentSlideHasIframe.value && props.type == '1') {
|
|
|
+ setTimeout(() => {
|
|
|
+ getWork()
|
|
|
+ }, 500) // 延迟500ms等待iframe加载完成
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ message.info('没有找到可刷新的iframe')
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
+ console.error('刷新iframe时出错:', error)
|
|
|
+ message.error('刷新iframe失败')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// 获取作业提交按钮的右侧位置
|
|
|
const getHomeworkButtonRight = () => {
|
|
|
if (isFullscreen.value) {
|
|
@@ -1188,6 +1259,18 @@ const getHomeworkButtonRight = () => {
|
|
|
return 30 // type=2时按钮在右侧30px
|
|
|
}
|
|
|
|
|
|
+// 获取刷新按钮的右侧位置
|
|
|
+const getRefreshButtonRight = () => {
|
|
|
+ if (isFullscreen.value) {
|
|
|
+ return 160 // 全屏时按钮在右侧150px
|
|
|
+ }
|
|
|
+ if (props.type === '1') {
|
|
|
+ // 展开作业区:按钮更靠左;收起时:按钮更靠右侧
|
|
|
+ return workPanelCollapsed.value ? 190 : 560
|
|
|
+ }
|
|
|
+ return 160 // type=2时按钮在右侧150px
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
// 键盘快捷键
|
|
|
const handleKeydown = (e: KeyboardEvent) => {
|
|
@@ -1290,11 +1373,18 @@ const getCourseDetail = async () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+const getWorkLoading = ref<any>(false)
|
|
|
+
|
|
|
+
|
|
|
const getWork = async (isUpdate = false) => {
|
|
|
try {
|
|
|
+ if (getWorkLoading.value) {
|
|
|
+ return
|
|
|
+ }
|
|
|
if (!isUpdate) {
|
|
|
workLoading.value = true
|
|
|
}
|
|
|
+ getWorkLoading.value = true
|
|
|
console.log('getWork 开始执行,参数:', {
|
|
|
courseid: props.courseid,
|
|
|
slideIndex: slideIndex.value,
|
|
@@ -1335,17 +1425,20 @@ const getWork = async (isUpdate = false) => {
|
|
|
}
|
|
|
|
|
|
console.log('getWork 执行成功,结果:', workArray.value)
|
|
|
+ getWorkLoading.value = false
|
|
|
}
|
|
|
catch (error) {
|
|
|
console.error('getWork 执行失败:', error)
|
|
|
if (!isUpdate) {
|
|
|
message.error('获取作业信息失败')
|
|
|
}
|
|
|
+ getWorkLoading.value = false
|
|
|
}
|
|
|
finally {
|
|
|
if (!isUpdate) {
|
|
|
workLoading.value = false
|
|
|
}
|
|
|
+ getWorkLoading.value = false
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1489,12 +1582,28 @@ const sendMessage = (obj: any) => {
|
|
|
*/
|
|
|
const getMessages = (msgObj: any) => {
|
|
|
console.log('message', msgObj)
|
|
|
+
|
|
|
+ // 处理幻灯片切换消息
|
|
|
if (props.type == '2' && msgObj.slideIndex && msgObj.type === 'slideIndex') {
|
|
|
goToSlide(msgObj.slideIndex)
|
|
|
}
|
|
|
+
|
|
|
+ // 处理跟随模式状态变化
|
|
|
if (props.type == '2' && msgObj.type === 'sopen') {
|
|
|
selectCourseSLook()
|
|
|
}
|
|
|
+
|
|
|
+ // 处理作业提交消息 - 当有人提交作业时,重新获取作业数据
|
|
|
+ if (props.type == '1' && msgObj.type === 'homework_submitted' && msgObj.courseid === props.courseid) {
|
|
|
+ console.log('收到作业提交消息,重新获取作业数据')
|
|
|
+ // 延迟一点时间,确保后端数据已更新
|
|
|
+ setTimeout(() => {
|
|
|
+ if (currentSlideHasIframe.value) {
|
|
|
+ getWork(true) // 传入true表示是更新模式
|
|
|
+ }
|
|
|
+ }, 1000)
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
|
|
@@ -1594,8 +1703,7 @@ onUnmounted(() => {
|
|
|
// 移除视口尺寸更新事件监听器
|
|
|
window.removeEventListener('viewportSizeUpdated', handleViewportSizeUpdated)
|
|
|
|
|
|
- // 停止作业更新定时器
|
|
|
- stopWorkTimer()
|
|
|
+ // 移除定时器清理代码,已改用socket监听
|
|
|
|
|
|
// 清理window上的引用
|
|
|
if ((window as any).PPTistStudent) {
|
|
@@ -1671,7 +1779,7 @@ onUnmounted(() => {
|
|
|
overflow: auto;
|
|
|
}
|
|
|
.layout-content-right.collapsed {
|
|
|
- width: 48px;
|
|
|
+ width: 52px;
|
|
|
}
|
|
|
.right-panel-header {
|
|
|
display: flex;
|
|
@@ -2279,6 +2387,53 @@ onUnmounted(() => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/* 刷新网页按钮样式 */
|
|
|
+.refresh-page-btn {
|
|
|
+ position: fixed;
|
|
|
+ bottom: 30px;
|
|
|
+ z-index: 100;
|
|
|
+ background: linear-gradient(135deg, #52c41a 0%, #389e0d 100%);
|
|
|
+ color: white;
|
|
|
+ padding: 12px 20px;
|
|
|
+ border-radius: 25px;
|
|
|
+ cursor: pointer;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
|
|
+ }
|
|
|
+
|
|
|
+ &:active {
|
|
|
+ transform: translateY(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ .btn-text {
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ .tool-btn {
|
|
|
+ background: transparent;
|
|
|
+ color: white;
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ font-size: 16px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: transparent;
|
|
|
+ transform: none;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/* Loading状态样式 */
|
|
|
.loading-overlay {
|
|
|
position: absolute;
|