|
@@ -24,164 +24,6 @@ const generateCourseOptions = () => {
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 生成视频分析统计数据
|
|
|
|
|
-const generateVideoStats = (options) => {
|
|
|
|
|
- console.log('options-', options)
|
|
|
|
|
- const params = new URLSearchParams(options.url.split('?')[1])
|
|
|
|
|
- const courseId = params.get('courseId') || ''
|
|
|
|
|
- const timeRange = params.get('timeRange') || '30'
|
|
|
|
|
-
|
|
|
|
|
- // 根据时间范围调整数据规模
|
|
|
|
|
- const timeMultiplier =
|
|
|
|
|
- {
|
|
|
|
|
- 7: 0.3,
|
|
|
|
|
- 30: 1,
|
|
|
|
|
- 90: 2.5,
|
|
|
|
|
- 365: 8
|
|
|
|
|
- }[timeRange] || 1
|
|
|
|
|
- console.log('courseId', courseId)
|
|
|
|
|
- // 根据课程选择调整数据规模 - 全部课程数据更多
|
|
|
|
|
- const courseMultiplier = courseId === '' ? 3.5 : 1 // 全部课程是单个课程的3.5倍
|
|
|
|
|
- console.log('courseMultiplier', courseMultiplier)
|
|
|
|
|
- // 基础数据
|
|
|
|
|
- const baseViewers = Math.floor(800 * timeMultiplier * courseMultiplier)
|
|
|
|
|
- const baseCompleted = Math.floor(baseViewers * (0.65 + Math.random() * 0.2)) // 65%-85%完成率
|
|
|
|
|
- const completionRate = Math.round((baseCompleted / baseViewers) * 100)
|
|
|
|
|
-
|
|
|
|
|
- const baseDownloads = Math.floor(baseViewers * (0.25 + Math.random() * 0.15)) // 25%-40%下载率
|
|
|
|
|
- const downloadRate = Math.round((baseDownloads / baseViewers) * 100)
|
|
|
|
|
- const avgDownloads = Math.round((baseDownloads / baseViewers) * 100) / 100
|
|
|
|
|
-
|
|
|
|
|
- const baseExits = Math.floor(baseViewers * (0.15 + Math.random() * 0.1)) // 15%-25%跳出率
|
|
|
|
|
- const exitRate = Math.round((baseExits / baseViewers) * 100)
|
|
|
|
|
-
|
|
|
|
|
- const baseNotes = Math.floor(baseViewers * (0.6 + Math.random() * 0.4)) // 60%-100%笔记率
|
|
|
|
|
- const baseDiscussions = Math.floor(baseViewers * (0.3 + Math.random() * 0.2)) // 30%-50%讨论率
|
|
|
|
|
- const baseReplies = Math.floor(baseDiscussions * (2 + Math.random() * 2)) // 每个讨论2-4个回复
|
|
|
|
|
-
|
|
|
|
|
- return Mock.mock({
|
|
|
|
|
- code: 200,
|
|
|
|
|
- data: {
|
|
|
|
|
- totalViewers: baseViewers + Math.floor(Math.random() * 200),
|
|
|
|
|
- completedViewers: baseCompleted,
|
|
|
|
|
- completionRate: completionRate,
|
|
|
|
|
- totalDownloads: baseDownloads,
|
|
|
|
|
- downloadRate: downloadRate,
|
|
|
|
|
- avgDownloads: avgDownloads,
|
|
|
|
|
- totalExits: baseExits,
|
|
|
|
|
- exitRate: exitRate,
|
|
|
|
|
- avgExitTime: '@pick(["08:45", "12:34", "15:23", "18:56", "22:15", "06:30", "14:20", "19:45"])',
|
|
|
|
|
- totalNotes: baseNotes,
|
|
|
|
|
- totalDiscussions: baseDiscussions,
|
|
|
|
|
- totalReplies: baseReplies
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 生成学员详细数据
|
|
|
|
|
-const generateStudentData = (options) => {
|
|
|
|
|
- const params = new URLSearchParams(options.url.split('?')[1])
|
|
|
|
|
- const current = parseInt(params.get('current')) || 1
|
|
|
|
|
- const pageSize = parseInt(params.get('pageSize')) || 10
|
|
|
|
|
- const courseId = params.get('courseId') || ''
|
|
|
|
|
- const timeRange = params.get('timeRange') || '30'
|
|
|
|
|
-
|
|
|
|
|
- // 根据课程和时间范围调整总数
|
|
|
|
|
- const timeMultiplier = { 7: 0.3, 30: 1, 90: 2.5, 365: 8 }[timeRange] || 1
|
|
|
|
|
- const courseMultiplier = courseId === '' ? 3.5 : 1
|
|
|
|
|
- const baseTotal = Math.floor(120 * timeMultiplier * courseMultiplier)
|
|
|
|
|
-
|
|
|
|
|
- return Mock.mock({
|
|
|
|
|
- code: 200,
|
|
|
|
|
- [`data|${pageSize}`]: [
|
|
|
|
|
- {
|
|
|
|
|
- 'key|+1': (current - 1) * pageSize + 1,
|
|
|
|
|
- id: () => String((current - 1) * pageSize + Mock.Random.increment()).padStart(3, '0'),
|
|
|
|
|
- name: '@cname',
|
|
|
|
|
- totalTime: () => {
|
|
|
|
|
- const minutes = Math.floor(Math.random() * 180) + 30 // 30-210分钟
|
|
|
|
|
- const hours = Math.floor(minutes / 60)
|
|
|
|
|
- const mins = minutes % 60
|
|
|
|
|
- return hours > 0 ? `${hours}小时${mins}分钟` : `${mins}分钟`
|
|
|
|
|
- },
|
|
|
|
|
- 'progress|20-100': () => Math.floor(Math.random() * 80) + 20,
|
|
|
|
|
- 'viewCount|1-12': () => Math.floor(Math.random() * 12) + 1,
|
|
|
|
|
- 'exitPoints|1-5': () => {
|
|
|
|
|
- const count = Math.floor(Math.random() * 5) + 1
|
|
|
|
|
- const points = []
|
|
|
|
|
- for (let i = 0; i < count; i++) {
|
|
|
|
|
- const minutes = Math.floor(Math.random() * 45)
|
|
|
|
|
- const seconds = Math.floor(Math.random() * 60)
|
|
|
|
|
- points.push(`${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`)
|
|
|
|
|
- }
|
|
|
|
|
- return points
|
|
|
|
|
- },
|
|
|
|
|
- 'seekCount|2-25': () => Math.floor(Math.random() * 24) + 2,
|
|
|
|
|
- 'noteCount|0-15': () => Math.floor(Math.random() * 16),
|
|
|
|
|
- 'discussionCount|0-12': () => Math.floor(Math.random() * 13),
|
|
|
|
|
- 'replyCount|0-20': () => Math.floor(Math.random() * 21),
|
|
|
|
|
- lastAccess: '@datetime("yyyy-MM-dd HH:mm")'
|
|
|
|
|
- }
|
|
|
|
|
- ],
|
|
|
|
|
- total: baseTotal + Math.floor(Math.random() * 50),
|
|
|
|
|
- current,
|
|
|
|
|
- pageSize
|
|
|
|
|
- })
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 生成章节数据
|
|
|
|
|
-const generateChapterData = (options) => {
|
|
|
|
|
- const params = new URLSearchParams(options.url.split('?')[1])
|
|
|
|
|
- const courseId = params.get('courseId') || ''
|
|
|
|
|
- const timeRange = params.get('timeRange') || '30'
|
|
|
|
|
-
|
|
|
|
|
- // 根据课程和时间范围调整数据规模
|
|
|
|
|
- const timeMultiplier = { 7: 0.3, 30: 1, 90: 2.5, 365: 8 }[timeRange] || 1
|
|
|
|
|
- const courseMultiplier = courseId === '' ? 3.5 : 1
|
|
|
|
|
-
|
|
|
|
|
- const chapterCount = Math.floor(Math.random() * 4) + 5 // 5-8章
|
|
|
|
|
- const chapters = []
|
|
|
|
|
-
|
|
|
|
|
- for (let i = 1; i <= chapterCount; i++) {
|
|
|
|
|
- const baseViewers = Math.floor((800 - i * 50) * timeMultiplier * courseMultiplier) // 越后面章节观看人数越少
|
|
|
|
|
- const completionRate = Math.max(95 - i * 5 - Math.random() * 10, 60) // 越后面完成率越低
|
|
|
|
|
- const completed = Math.floor(baseViewers * (completionRate / 100))
|
|
|
|
|
-
|
|
|
|
|
- // 生成视频时长
|
|
|
|
|
- const totalMinutes = Math.floor(Math.random() * 30) + 15 // 15-45分钟
|
|
|
|
|
- const minutes = totalMinutes % 60
|
|
|
|
|
- const seconds = Math.floor(Math.random() * 60)
|
|
|
|
|
- const duration = `${minutes}:${seconds.toString().padStart(2, '0')}`
|
|
|
|
|
-
|
|
|
|
|
- // 平均观看时长应该小于等于视频时长
|
|
|
|
|
- const avgWatchMinutes = Math.floor(totalMinutes * (0.7 + Math.random() * 0.3))
|
|
|
|
|
- const avgWatchSeconds = Math.floor(Math.random() * 60)
|
|
|
|
|
- const avgWatchTime = `${avgWatchMinutes}:${avgWatchSeconds.toString().padStart(2, '0')}`
|
|
|
|
|
-
|
|
|
|
|
- chapters.push({
|
|
|
|
|
- key: i,
|
|
|
|
|
- chapter: `第${i}章:${
|
|
|
|
|
- ['课程介绍', '基础知识', '核心概念', '实战应用', '高级技巧', '项目实战', '总结回顾', '拓展学习'][i - 1] ||
|
|
|
|
|
- '课程内容'
|
|
|
|
|
- }`,
|
|
|
|
|
- duration: duration,
|
|
|
|
|
- viewers: baseViewers + Math.floor(Math.random() * 100),
|
|
|
|
|
- completed: completed,
|
|
|
|
|
- completionRate: Math.round(completionRate),
|
|
|
|
|
- avgWatchTime: avgWatchTime,
|
|
|
|
|
- exitRate: Math.min(Math.floor(5 + i * 3 + Math.random() * 10), 35), // 越后面跳出率越高
|
|
|
|
|
- downloads: Math.floor(baseViewers * (0.1 + Math.random() * 0.15)), // 10%-25%下载率
|
|
|
|
|
- notes: Math.floor(baseViewers * (0.4 + Math.random() * 0.3)), // 40%-70%笔记率
|
|
|
|
|
- discussions: Math.floor(baseViewers * (0.15 + Math.random() * 0.15)) // 15%-30%讨论率
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return {
|
|
|
|
|
- code: 200,
|
|
|
|
|
- data: chapters
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
// 生成图表数据
|
|
// 生成图表数据
|
|
|
const generateChartData = (options) => {
|
|
const generateChartData = (options) => {
|
|
|
const params = new URLSearchParams(options.url.split('?')[1])
|
|
const params = new URLSearchParams(options.url.split('?')[1])
|
|
@@ -257,9 +99,6 @@ const generateChartData = (options) => {
|
|
|
}
|
|
}
|
|
|
// Mock 接口定义
|
|
// Mock 接口定义
|
|
|
Mock.mock(/\/api\/webapp\/video-analysis\/course-options/, 'get', generateCourseOptions)
|
|
Mock.mock(/\/api\/webapp\/video-analysis\/course-options/, 'get', generateCourseOptions)
|
|
|
-Mock.mock(/\/api\/webapp\/video-analysis\/stats/, 'get', generateVideoStats)
|
|
|
|
|
-Mock.mock(/\/api\/webapp\/video-analysis\/students/, 'get', generateStudentData)
|
|
|
|
|
-Mock.mock(/\/api\/webapp\/video-analysis\/chapters/, 'get', generateChapterData)
|
|
|
|
|
Mock.mock(/\/api\/webapp\/video-analysis\/charts/, 'get', generateChartData)
|
|
Mock.mock(/\/api\/webapp\/video-analysis\/charts/, 'get', generateChartData)
|
|
|
|
|
|
|
|
// 导出的API函数
|
|
// 导出的API函数
|
|
@@ -268,21 +107,31 @@ export const videoAnalysisApi = {
|
|
|
getCourseOptions() {
|
|
getCourseOptions() {
|
|
|
return request('/video-analysis/course-options', '', 'get')
|
|
return request('/video-analysis/course-options', '', 'get')
|
|
|
},
|
|
},
|
|
|
-
|
|
|
|
|
- // 获取视频分析统计数据
|
|
|
|
|
- getVideoStats(params = {}) {
|
|
|
|
|
- console.log('params=', params)
|
|
|
|
|
- return request('/video-analysis/stats', params, 'get')
|
|
|
|
|
|
|
+ // 视频分析-观看人数统计
|
|
|
|
|
+ watchUserCountProgress(params = {}) {
|
|
|
|
|
+ return request('/disk/videoanalysis/watchUserCountProgress', params, 'get')
|
|
|
|
|
+ },
|
|
|
|
|
+ // 视频分析-讲义下载次数
|
|
|
|
|
+ teachMaterialsDownloadCount(params = {}) {
|
|
|
|
|
+ return request('/disk/videoanalysis/teachMaterialsDownloadCount', params, 'get')
|
|
|
|
|
+ },
|
|
|
|
|
+ // 视频分析-跳出时间分析
|
|
|
|
|
+ jumpTimeAnalyse(params = {}) {
|
|
|
|
|
+ return request('/disk/videoanalysis/jumpTimeAnalyse', params, 'get')
|
|
|
|
|
+ },
|
|
|
|
|
+ // 视频分析-互动统计分析
|
|
|
|
|
+ interactionDataAnalyse(params = {}) {
|
|
|
|
|
+ return request('/disk/videoanalysis/interactionDataAnalyse', params, 'get')
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
// 获取学员详细数据
|
|
// 获取学员详细数据
|
|
|
- getStudentData(params = {}) {
|
|
|
|
|
- return request('/video-analysis/students', params, 'get')
|
|
|
|
|
|
|
+ studyBehaviorDetailData(params = {}) {
|
|
|
|
|
+ return request('/disk/videoanalysis/studyBehaviorDetailData', params, 'get')
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
// 获取章节数据
|
|
// 获取章节数据
|
|
|
- getChapterData(params = {}) {
|
|
|
|
|
- return request('/video-analysis/chapters', params, 'get')
|
|
|
|
|
|
|
+ videoDetailDataAnalysis(params = {}) {
|
|
|
|
|
+ return request('/disk/videoanalysis/videoDetailDataAnalysis', params, 'get')
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
// 获取图表数据
|
|
// 获取图表数据
|