299 lines
8.7 KiB
JavaScript
299 lines
8.7 KiB
JavaScript
/**
|
||
* 学习监控工具类
|
||
* 实现屏幕截图和上传功能
|
||
*/
|
||
import request from './request.js'
|
||
|
||
class Monitor {
|
||
constructor() {
|
||
this.screenshotTimer = null
|
||
this.uploadQueue = [] // 上传队列
|
||
this.isUploading = false
|
||
this.interval = 30000 // 默认30秒
|
||
this.currentCourseId = null
|
||
this.isEnabled = false
|
||
}
|
||
|
||
/**
|
||
* 启动监控
|
||
* @param {Number} courseId 课程ID(可选)
|
||
* @param {Number} interval 截图间隔(毫秒,默认30秒)
|
||
*/
|
||
start(courseId = null, interval = 30000) {
|
||
if (this.isEnabled) {
|
||
console.log('监控已启动')
|
||
return
|
||
}
|
||
|
||
this.currentCourseId = courseId
|
||
this.interval = interval
|
||
this.isEnabled = true
|
||
|
||
console.log('启动学习监控,间隔:', interval / 1000, '秒')
|
||
|
||
// 立即执行一次
|
||
this.captureAndUpload()
|
||
|
||
// 定时执行
|
||
this.screenshotTimer = setInterval(() => {
|
||
if (this.isEnabled) {
|
||
this.captureAndUpload()
|
||
}
|
||
}, this.interval)
|
||
}
|
||
|
||
/**
|
||
* 停止监控
|
||
*/
|
||
stop() {
|
||
this.isEnabled = false
|
||
if (this.screenshotTimer) {
|
||
clearInterval(this.screenshotTimer)
|
||
this.screenshotTimer = null
|
||
}
|
||
console.log('学习监控已停止')
|
||
}
|
||
|
||
/**
|
||
* 设置当前课程ID
|
||
*/
|
||
setCourseId(courseId) {
|
||
this.currentCourseId = courseId
|
||
}
|
||
|
||
/**
|
||
* 截图并上传
|
||
*/
|
||
async captureAndUpload() {
|
||
try {
|
||
// 检查网络状态
|
||
const networkType = await this.getNetworkType()
|
||
if (networkType === 'none') {
|
||
console.log('网络未连接,跳过截图上传')
|
||
return
|
||
}
|
||
|
||
// 截图
|
||
const screenshotPath = await this.captureScreenshot()
|
||
if (!screenshotPath) {
|
||
console.log('截图失败')
|
||
return
|
||
}
|
||
|
||
// 添加到上传队列
|
||
this.uploadQueue.push({
|
||
filePath: screenshotPath,
|
||
courseId: this.currentCourseId,
|
||
timestamp: Date.now()
|
||
})
|
||
|
||
// 处理上传队列
|
||
this.processUploadQueue()
|
||
} catch (error) {
|
||
console.error('截图上传失败:', error)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 截图
|
||
*/
|
||
captureScreenshot() {
|
||
return new Promise((resolve, reject) => {
|
||
// #ifdef APP-PLUS
|
||
// App环境:使用plus API
|
||
this.captureScreenshotApp(resolve, reject)
|
||
// #endif
|
||
|
||
// #ifdef H5
|
||
// H5环境:使用html2canvas或简化方案
|
||
this.captureScreenshotH5(resolve, reject)
|
||
// #endif
|
||
|
||
// #ifndef H5 || APP-PLUS
|
||
// 其他平台:暂不支持或使用降级方案
|
||
console.warn('当前平台不支持截图功能,跳过截图')
|
||
reject(new Error('当前平台不支持截图功能'))
|
||
// #endif
|
||
})
|
||
}
|
||
|
||
/**
|
||
* App环境截图(推荐)
|
||
*/
|
||
captureScreenshotApp(resolve, reject) {
|
||
// #ifdef APP-PLUS
|
||
try {
|
||
// 使用plus.screen.capture截图
|
||
if (typeof plus !== 'undefined' && plus.screen) {
|
||
plus.screen.capture((bitmap) => {
|
||
// 保存为临时文件
|
||
const timestamp = Date.now()
|
||
const filePath = `_doc/screenshot_${timestamp}.png`
|
||
|
||
bitmap.save(filePath, (success) => {
|
||
resolve(filePath)
|
||
}, (error) => {
|
||
console.error('保存截图失败:', error)
|
||
reject(error)
|
||
})
|
||
}, (error) => {
|
||
console.error('截图失败:', error)
|
||
reject(error)
|
||
})
|
||
} else {
|
||
// 降级方案:提示用户
|
||
console.warn('plus.screen不可用,跳过截图')
|
||
reject(new Error('截图功能不可用'))
|
||
}
|
||
} catch (error) {
|
||
console.error('截图异常:', error)
|
||
reject(error)
|
||
}
|
||
// #endif
|
||
|
||
// #ifndef APP-PLUS
|
||
reject(new Error('App环境不可用'))
|
||
// #endif
|
||
}
|
||
|
||
/**
|
||
* H5环境截图(简化方案)
|
||
* 注意:H5环境截图功能有限,主要用于开发测试
|
||
*/
|
||
captureScreenshotH5(resolve, reject) {
|
||
// #ifdef H5
|
||
try {
|
||
// H5环境可以使用html2canvas库,但这里使用简单的canvas方式
|
||
// 注意:H5环境截图功能有限,建议在App环境中使用
|
||
const canvas = document.createElement('canvas')
|
||
const ctx = canvas.getContext('2d')
|
||
canvas.width = window.innerWidth
|
||
canvas.height = window.innerHeight
|
||
|
||
// 绘制当前页面内容(简化版,实际需要更复杂的实现)
|
||
ctx.fillStyle = '#fff'
|
||
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||
ctx.fillStyle = '#000'
|
||
ctx.font = '16px Arial'
|
||
ctx.fillText('学习监控截图', 20, 40)
|
||
ctx.fillText(new Date().toLocaleString(), 20, 60)
|
||
ctx.fillText('H5环境截图功能有限', 20, 80)
|
||
|
||
// 转换为图片
|
||
canvas.toBlob((blob) => {
|
||
if (typeof URL !== 'undefined' && URL.createObjectURL) {
|
||
const filePath = URL.createObjectURL(blob)
|
||
resolve(filePath)
|
||
} else {
|
||
// Fallback: convert blob to data URL
|
||
const reader = new FileReader()
|
||
reader.onloadend = () => {
|
||
resolve(reader.result)
|
||
}
|
||
reader.onerror = (error) => {
|
||
reject(error)
|
||
}
|
||
reader.readAsDataURL(blob)
|
||
}
|
||
}, 'image/png', 0.8)
|
||
} catch (error) {
|
||
console.error('H5截图失败:', error)
|
||
reject(error)
|
||
}
|
||
// #endif
|
||
|
||
// #ifndef H5
|
||
reject(new Error('H5环境不可用'))
|
||
// #endif
|
||
}
|
||
|
||
/**
|
||
* 获取网络状态
|
||
*/
|
||
getNetworkType() {
|
||
return new Promise((resolve) => {
|
||
uni.getNetworkType({
|
||
success: (res) => {
|
||
resolve(res.networkType)
|
||
},
|
||
fail: () => {
|
||
resolve('unknown')
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 处理上传队列
|
||
*/
|
||
async processUploadQueue() {
|
||
if (this.isUploading || this.uploadQueue.length === 0) {
|
||
return
|
||
}
|
||
|
||
this.isUploading = true
|
||
|
||
while (this.uploadQueue.length > 0) {
|
||
const item = this.uploadQueue.shift()
|
||
try {
|
||
await this.uploadScreenshot(item.filePath, item.courseId)
|
||
console.log('截图上传成功')
|
||
} catch (error) {
|
||
console.error('截图上传失败:', error)
|
||
// 上传失败,重新加入队列(最多重试3次)
|
||
if (item.retryCount === undefined) {
|
||
item.retryCount = 0
|
||
}
|
||
if (item.retryCount < 3) {
|
||
item.retryCount++
|
||
this.uploadQueue.push(item)
|
||
}
|
||
}
|
||
}
|
||
|
||
this.isUploading = false
|
||
}
|
||
|
||
/**
|
||
* 上传截图
|
||
*/
|
||
async uploadScreenshot(filePath, courseId = null) {
|
||
const formData = {}
|
||
if (courseId) {
|
||
formData.courseId = courseId
|
||
}
|
||
|
||
return await request.upload('/study/monitor/screenshot', filePath, formData, {
|
||
name: 'file'
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 手动截图上传(用于测试)
|
||
*/
|
||
async manualCapture() {
|
||
try {
|
||
const screenshotPath = await this.captureScreenshot()
|
||
if (screenshotPath) {
|
||
await this.uploadScreenshot(screenshotPath, this.currentCourseId)
|
||
uni.showToast({
|
||
title: '截图上传成功',
|
||
icon: 'success'
|
||
})
|
||
}
|
||
} catch (error) {
|
||
uni.showToast({
|
||
title: '截图失败',
|
||
icon: 'none'
|
||
})
|
||
console.error('手动截图失败:', error)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 创建单例
|
||
const monitor = new Monitor()
|
||
|
||
export default monitor
|
||
|