analysisTeachingActivities.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. // 教学活动分析相关接口
  2. import { moduleRequest } from '@/utils/request'
  3. import Mock from 'mockjs'
  4. const request = moduleRequest(`/api/webapp/`)
  5. // Mock 数据配置
  6. Mock.setup({
  7. timeout: '200-600'
  8. })
  9. // 课程选项数据
  10. const courseOptions = [
  11. { value: '', label: '全部课程' },
  12. { value: 'course1', label: 'JavaScript基础教程' },
  13. { value: 'course2', label: 'Python数据分析' },
  14. { value: 'course3', label: 'React前端开发' },
  15. { value: 'course4', label: '机器学习入门' },
  16. { value: 'course5', label: 'Vue.js实战开发' },
  17. { value: 'course6', label: 'Node.js后端开发' }
  18. ]
  19. // 生成日期数据
  20. const generateDateData = (days) => {
  21. const dates = []
  22. const today = new Date()
  23. for (let i = days - 1; i >= 0; i--) {
  24. const date = new Date(today)
  25. date.setDate(date.getDate() - i)
  26. dates.push(date.toLocaleDateString('zh-CN', { month: '2-digit', day: '2-digit' }))
  27. }
  28. return dates
  29. }
  30. // 生成访问数据
  31. const generateVisitData = (days, courseId = '') => {
  32. const data = []
  33. // 全部课程的访问量应该比单个课程高
  34. const courseMultiplier = courseId ? 0.15 + Math.random() * 0.25 : 1.0
  35. for (let i = 0; i < days; i++) {
  36. const dayOfWeek = (new Date().getDay() - days + i + 7) % 7
  37. const isWeekend = dayOfWeek === 0 || dayOfWeek === 6
  38. // 全部课程基础访问量更高
  39. const baseValue = isWeekend ? 450 : 680
  40. const randomFactor = 0.7 + Math.random() * 0.6
  41. const finalValue = Math.round(baseValue * randomFactor * courseMultiplier)
  42. data.push(finalValue)
  43. }
  44. return data
  45. }
  46. // 讨论数据 - 这是全部课程的汇总讨论数据
  47. const discussionData = [
  48. {
  49. id: 1,
  50. title: 'JavaScript闭包概念理解',
  51. author: '张三',
  52. createTime: '2024-01-15 10:30',
  53. replyCount: 25,
  54. lastReplyTime: '2024-01-16 14:20'
  55. },
  56. {
  57. id: 2,
  58. title: 'Python数据分析库选择',
  59. author: '李四',
  60. createTime: '2024-01-14 16:45',
  61. replyCount: 18,
  62. lastReplyTime: '2024-01-15 09:15'
  63. },
  64. {
  65. id: 3,
  66. title: 'React组件生命周期',
  67. author: '王五',
  68. createTime: '2024-01-13 11:20',
  69. replyCount: 32,
  70. lastReplyTime: '2024-01-14 17:30'
  71. },
  72. {
  73. id: 4,
  74. title: '机器学习算法选择',
  75. author: '赵六',
  76. createTime: '2024-01-12 14:15',
  77. replyCount: 16,
  78. lastReplyTime: '2024-01-13 10:45'
  79. },
  80. {
  81. id: 5,
  82. title: '前端性能优化技巧',
  83. author: '钱七',
  84. createTime: '2024-01-11 09:30',
  85. replyCount: 45,
  86. lastReplyTime: '2024-01-15 16:20'
  87. },
  88. {
  89. id: 6,
  90. title: 'Vue3 Composition API使用心得',
  91. author: '孙八',
  92. createTime: '2024-01-10 15:20',
  93. replyCount: 38,
  94. lastReplyTime: '2024-01-14 11:30'
  95. },
  96. {
  97. id: 7,
  98. title: 'Node.js异步编程最佳实践',
  99. author: '周九',
  100. createTime: '2024-01-09 13:45',
  101. replyCount: 29,
  102. lastReplyTime: '2024-01-13 16:15'
  103. },
  104. {
  105. id: 8,
  106. title: '数据库索引优化策略',
  107. author: '陈十',
  108. createTime: '2024-01-08 11:20',
  109. replyCount: 22,
  110. lastReplyTime: '2024-01-12 15:40'
  111. },
  112. {
  113. id: 9,
  114. title: 'Web安全防护实践',
  115. author: '刘十一',
  116. createTime: '2024-01-07 14:30',
  117. replyCount: 19,
  118. lastReplyTime: '2024-01-11 09:25'
  119. },
  120. {
  121. id: 10,
  122. title: '移动端适配解决方案',
  123. author: '杨十二',
  124. createTime: '2024-01-06 16:15',
  125. replyCount: 27,
  126. lastReplyTime: '2024-01-10 13:50'
  127. },
  128. {
  129. id: 11,
  130. title: 'TypeScript类型系统深入',
  131. author: '黄十三',
  132. createTime: '2024-01-05 10:45',
  133. replyCount: 34,
  134. lastReplyTime: '2024-01-09 17:20'
  135. },
  136. {
  137. id: 12,
  138. title: 'Docker容器化部署',
  139. author: '吴十四',
  140. createTime: '2024-01-04 13:20',
  141. replyCount: 21,
  142. lastReplyTime: '2024-01-08 11:15'
  143. },
  144. {
  145. id: 13,
  146. title: 'GraphQL API设计',
  147. author: '郑十五',
  148. createTime: '2024-01-03 15:40',
  149. replyCount: 15,
  150. lastReplyTime: '2024-01-07 14:30'
  151. },
  152. {
  153. id: 14,
  154. title: '微服务架构实践',
  155. author: '王十六',
  156. createTime: '2024-01-02 09:15',
  157. replyCount: 28,
  158. lastReplyTime: '2024-01-06 16:45'
  159. },
  160. {
  161. id: 15,
  162. title: 'Redis缓存策略',
  163. author: '李十七',
  164. createTime: '2024-01-01 11:30',
  165. replyCount: 23,
  166. lastReplyTime: '2024-01-05 12:20'
  167. }
  168. ]
  169. // 文档数据 - 这是全部课程的汇总数据,数量较大
  170. const documentData = [
  171. {
  172. id: 1,
  173. name: 'JavaScript基础语法',
  174. type: 'PDF文档',
  175. viewCount: 4856,
  176. completedCount: 3234,
  177. completionRate: 66.5,
  178. avgReadTime: '25:30',
  179. exitRate: 24.6,
  180. downloadCount: 1456
  181. },
  182. {
  183. id: 2,
  184. name: 'Python数据分析入门',
  185. type: '在线文档',
  186. viewCount: 3234,
  187. completedCount: 2587,
  188. completionRate: 80.0,
  189. avgReadTime: '32:15',
  190. exitRate: 18.5,
  191. downloadCount: 1234
  192. },
  193. {
  194. id: 3,
  195. name: 'React组件开发指南',
  196. type: 'PDF文档',
  197. viewCount: 2987,
  198. completedCount: 2289,
  199. completionRate: 76.6,
  200. avgReadTime: '28:45',
  201. exitRate: 22.3,
  202. downloadCount: 989
  203. },
  204. {
  205. id: 4,
  206. name: '机器学习算法详解',
  207. type: '在线文档',
  208. viewCount: 2756,
  209. completedCount: 1908,
  210. completionRate: 69.2,
  211. avgReadTime: '45:20',
  212. exitRate: 30.8,
  213. downloadCount: 823
  214. },
  215. {
  216. id: 5,
  217. name: '前端工程化实践',
  218. type: 'PDF文档',
  219. viewCount: 2523,
  220. completedCount: 1665,
  221. completionRate: 66.0,
  222. avgReadTime: '38:15',
  223. exitRate: 34.0,
  224. downloadCount: 689
  225. },
  226. {
  227. id: 6,
  228. name: 'Vue.js进阶开发',
  229. type: '在线文档',
  230. viewCount: 2678,
  231. completedCount: 1802,
  232. completionRate: 67.3,
  233. avgReadTime: '35:40',
  234. exitRate: 32.7,
  235. downloadCount: 756
  236. },
  237. {
  238. id: 7,
  239. name: 'Node.js后端开发',
  240. type: 'PDF文档',
  241. viewCount: 2345,
  242. completedCount: 1567,
  243. completionRate: 66.8,
  244. avgReadTime: '42:10',
  245. exitRate: 28.9,
  246. downloadCount: 634
  247. },
  248. {
  249. id: 8,
  250. name: '数据库设计原理',
  251. type: '在线文档',
  252. viewCount: 2123,
  253. completedCount: 1489,
  254. completionRate: 70.1,
  255. avgReadTime: '38:25',
  256. exitRate: 26.4,
  257. downloadCount: 567
  258. },
  259. {
  260. id: 9,
  261. name: 'Web安全防护',
  262. type: 'PDF文档',
  263. viewCount: 1987,
  264. completedCount: 1345,
  265. completionRate: 67.7,
  266. avgReadTime: '33:50',
  267. exitRate: 29.8,
  268. downloadCount: 498
  269. },
  270. {
  271. id: 10,
  272. name: '移动端开发实战',
  273. type: '在线文档',
  274. viewCount: 1876,
  275. completedCount: 1234,
  276. completionRate: 65.8,
  277. avgReadTime: '40:15',
  278. exitRate: 31.2,
  279. downloadCount: 445
  280. }
  281. ]
  282. // 生成课程选项数据
  283. const generateCourseOptions = () => {
  284. return {
  285. code: 200,
  286. message: '获取成功',
  287. data: courseOptions
  288. }
  289. }
  290. // 生成核心统计数据
  291. const generateStatsData = (options) => {
  292. const urlParts = options.url.split('?')
  293. const params = urlParts.length > 1 ? new URLSearchParams(urlParts[1]) : new URLSearchParams()
  294. const courseId = params.get('courseId') || ''
  295. const timeRange = parseInt(params.get('timeRange')) || 30
  296. // 全部课程的数据应该是所有课程的总和,单个课程数据相对较少
  297. const baseMultiplier = courseId ? 0.15 + Math.random() * 0.25 : 1.0 // 单个课程占总数的15%-40%
  298. const timeMultiplier = timeRange / 30
  299. // 基础数据 - 全部课程的总数据
  300. const baseStats = {
  301. totalDocViewers: 8560,
  302. completedDocViewers: 6234,
  303. totalDocExits: 1890,
  304. totalDiscussions: 1456,
  305. totalReplies: 8934
  306. }
  307. const calculatedStats = {
  308. totalDocViewers: Math.round(baseStats.totalDocViewers * baseMultiplier * timeMultiplier),
  309. completedDocViewers: Math.round(baseStats.completedDocViewers * baseMultiplier * timeMultiplier),
  310. totalDocExits: Math.round(baseStats.totalDocExits * baseMultiplier * timeMultiplier),
  311. totalDiscussions: Math.round(baseStats.totalDiscussions * baseMultiplier * timeMultiplier),
  312. totalReplies: Math.round(baseStats.totalReplies * baseMultiplier * timeMultiplier)
  313. }
  314. // 确保完成人数不超过总观看人数
  315. if (calculatedStats.completedDocViewers > calculatedStats.totalDocViewers) {
  316. calculatedStats.completedDocViewers = Math.round(calculatedStats.totalDocViewers * 0.7)
  317. }
  318. // 计算完成率
  319. const docCompletionRate =
  320. calculatedStats.totalDocViewers > 0
  321. ? parseFloat(((calculatedStats.completedDocViewers / calculatedStats.totalDocViewers) * 100).toFixed(1))
  322. : 0
  323. // 计算跳出率 - 单个课程跳出率可能更高
  324. const baseExitRate = courseId ? 25 + Math.random() * 15 : 18 + Math.random() * 12
  325. const docExitRate = parseFloat(baseExitRate.toFixed(1))
  326. // 计算平均回帖数
  327. const avgRepliesPerDiscussion =
  328. calculatedStats.totalDiscussions > 0
  329. ? parseFloat((calculatedStats.totalReplies / calculatedStats.totalDiscussions).toFixed(1))
  330. : 0
  331. return {
  332. code: 200,
  333. message: '获取成功',
  334. data: {
  335. totalDocViewers: calculatedStats.totalDocViewers,
  336. completedDocViewers: calculatedStats.completedDocViewers,
  337. docCompletionRate,
  338. totalDocExits: calculatedStats.totalDocExits,
  339. docExitRate,
  340. avgDocExitTime: `${String(Math.floor(Math.random() * 15) + 8).padStart(2, '0')}:${String(
  341. Math.floor(Math.random() * 60)
  342. ).padStart(2, '0')}`,
  343. totalDiscussions: calculatedStats.totalDiscussions,
  344. totalReplies: calculatedStats.totalReplies,
  345. avgRepliesPerDiscussion
  346. }
  347. }
  348. }
  349. // 生成每周统计数据
  350. const generateWeeklyStatsData = (options) => {
  351. const urlParts = options.url.split('?')
  352. const params = urlParts.length > 1 ? new URLSearchParams(urlParts[1]) : new URLSearchParams()
  353. const courseId = params.get('courseId') || ''
  354. // 全部课程数据应该是所有课程的总和
  355. const baseMultiplier = courseId ? 0.12 + Math.random() * 0.28 : 1.0 // 单个课程占总数的12%-40%
  356. // 基础数据 - 全部课程的总数据
  357. const baseWeeklyStats = {
  358. studentWeeklyPosts: 456,
  359. studentWeeklyReplies: 1678,
  360. teacherWeeklyPosts: 123,
  361. teacherWeeklyReplies: 567
  362. }
  363. const calculatedStats = {
  364. studentWeeklyPosts: Math.round(baseWeeklyStats.studentWeeklyPosts * baseMultiplier),
  365. studentWeeklyReplies: Math.round(baseWeeklyStats.studentWeeklyReplies * baseMultiplier),
  366. teacherWeeklyPosts: Math.round(baseWeeklyStats.teacherWeeklyPosts * baseMultiplier),
  367. teacherWeeklyReplies: Math.round(baseWeeklyStats.teacherWeeklyReplies * baseMultiplier)
  368. }
  369. return {
  370. code: 200,
  371. message: '获取成功',
  372. data: {
  373. studentWeeklyPosts: calculatedStats.studentWeeklyPosts,
  374. studentWeeklyReplies: calculatedStats.studentWeeklyReplies,
  375. studentAvgPostsPerDay: parseFloat((calculatedStats.studentWeeklyPosts / 7).toFixed(1)),
  376. teacherWeeklyPosts: calculatedStats.teacherWeeklyPosts,
  377. teacherWeeklyReplies: calculatedStats.teacherWeeklyReplies,
  378. teacherAvgPostsPerDay: parseFloat((calculatedStats.teacherWeeklyPosts / 7).toFixed(1))
  379. }
  380. }
  381. }
  382. // 生成每日访问数据
  383. const generateDailyVisitsData = (options) => {
  384. const urlParts = options.url.split('?')
  385. const params = urlParts.length > 1 ? new URLSearchParams(urlParts[1]) : new URLSearchParams()
  386. const courseId = params.get('courseId') || ''
  387. const timeRange = parseInt(params.get('timeRange')) || 30
  388. const dates = generateDateData(timeRange)
  389. const visits = generateVisitData(timeRange, courseId)
  390. return {
  391. code: 200,
  392. message: '获取成功',
  393. data: {
  394. dates,
  395. visits
  396. }
  397. }
  398. }
  399. // 生成讨论数据
  400. const generateDiscussionData = (options) => {
  401. const urlParts = options.url.split('?')
  402. const params = urlParts.length > 1 ? new URLSearchParams(urlParts[1]) : new URLSearchParams()
  403. const courseId = params.get('courseId') || ''
  404. const page = parseInt(params.get('page')) || 1
  405. const pageSize = parseInt(params.get('pageSize')) || 10
  406. let filteredData = discussionData
  407. if (courseId) {
  408. // 单个课程只显示部分讨论数据,模拟该课程相关的讨论
  409. const courseDiscussionCount = Math.floor(discussionData.length * (0.2 + Math.random() * 0.3)) // 20%-50%
  410. filteredData = discussionData.slice(0, Math.max(courseDiscussionCount, 2)) // 至少显示2条
  411. }
  412. const startIndex = (page - 1) * pageSize
  413. const endIndex = startIndex + pageSize
  414. const pageData = filteredData.slice(startIndex, endIndex)
  415. return {
  416. code: 200,
  417. message: '获取成功',
  418. data: {
  419. list: pageData,
  420. total: filteredData.length,
  421. page,
  422. pageSize
  423. }
  424. }
  425. }
  426. // 生成文档数据
  427. const generateDocumentData = (options) => {
  428. const urlParts = options.url.split('?')
  429. const params = urlParts.length > 1 ? new URLSearchParams(urlParts[1]) : new URLSearchParams()
  430. const courseId = params.get('courseId') || ''
  431. const page = parseInt(params.get('page')) || 1
  432. const pageSize = parseInt(params.get('pageSize')) || 10
  433. let filteredData = [...documentData]
  434. if (courseId) {
  435. // 单个课程只显示该课程相关的文档,数量相对较少
  436. const courseDocumentCount = Math.floor(documentData.length * (0.25 + Math.random() * 0.35)) // 25%-60%
  437. filteredData = documentData.slice(0, Math.max(courseDocumentCount, 3)) // 至少显示3条
  438. // 调整单个课程文档的数据,使其相对较小但合理
  439. filteredData = filteredData.map((doc) => ({
  440. ...doc,
  441. viewCount: Math.round(doc.viewCount * (0.15 + Math.random() * 0.25)), // 15%-40%的观看量
  442. completedCount: Math.round(doc.completedCount * (0.15 + Math.random() * 0.25)),
  443. downloadCount: Math.round(doc.downloadCount * (0.15 + Math.random() * 0.25)),
  444. // 完成率和跳出率保持相对合理的范围
  445. completionRate: parseFloat((doc.completionRate + (Math.random() - 0.5) * 10).toFixed(1)),
  446. exitRate: parseFloat((doc.exitRate + (Math.random() - 0.5) * 8).toFixed(1))
  447. }))
  448. }
  449. const startIndex = (page - 1) * pageSize
  450. const endIndex = startIndex + pageSize
  451. const pageData = filteredData.slice(startIndex, endIndex)
  452. return {
  453. code: 200,
  454. message: '获取成功',
  455. data: {
  456. list: pageData,
  457. total: filteredData.length,
  458. page,
  459. pageSize
  460. }
  461. }
  462. }
  463. // Mock 接口定义
  464. Mock.mock(/\/api\/webapp\/teaching-analysis\/course-options/, 'get', generateCourseOptions)
  465. Mock.mock(/\/api\/webapp\/teaching-analysis\/stats/, 'get', generateStatsData)
  466. Mock.mock(/\/api\/webapp\/teaching-analysis\/weekly-stats/, 'get', generateWeeklyStatsData)
  467. Mock.mock(/\/api\/webapp\/teaching-analysis\/daily-visits/, 'get', generateDailyVisitsData)
  468. Mock.mock(/\/api\/webapp\/teaching-analysis\/discussions/, 'get', generateDiscussionData)
  469. Mock.mock(/\/api\/webapp\/teaching-analysis\/documents/, 'get', generateDocumentData)
  470. // 导出的API函数
  471. export const getCourseOptions = (params = {}) => {
  472. return request('disk/courseinfo/allList', params, 'get')
  473. }
  474. // 文档观看统计
  475. export const getTeachingStats = (params = {}) => {
  476. return request('teachingActivity/documentStatistic', params, 'get')
  477. }
  478. // 文档跳出分析
  479. export const documentJumpStatistic = (params = {}) => {
  480. return request('teachingActivity/documentJumpStatistic', params, 'get')
  481. }
  482. export const getWeeklyStats = (params = {}) => {
  483. return request('teaching-analysis/weekly-stats', params, 'get')
  484. }
  485. // 开课每日访问人数统计
  486. export const getDailyVisits = (params = {}) => {
  487. return request('teachingActivity/courseOpenStatistic', params, 'get')
  488. }
  489. export const getDiscussionData = (params = {}) => {
  490. return request('teaching-analysis/discussions', params, 'get')
  491. }
  492. export const getDocumentStats = (params = {}) => {
  493. return request('teaching-analysis/documents', params, 'get')
  494. }