uimk

岐黄天使刷课助手的用户界面模块,负责生成和管理控制面板及相关UI元素。

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/537080/1595113/uimk.js

  1. // ==UserScript==
  2. // @name 岐黄天使刷课助手 - UI模块
  3. // @namespace http://tampermonkey.net/qhtx-modules
  4. // @version 1.4.3
  5. // @description 岐黄天使刷课助手的用户界面模块,负责生成和管理控制面板及相关UI元素。
  6. // @author AI助手
  7. // ==/UserScript==
  8.  
  9. // UI模块
  10. (function() {
  11. 'use strict';
  12.  
  13. // 创建控制面板
  14. function createPanel() {
  15. console.log('[UI模块] createPanel 函数开始执行');
  16.  
  17. // 强制初始化 window.qh 对象和必要属性
  18. try {
  19. if (!window.qh) {
  20. window.qh = {};
  21. console.log('[UI模块] 初始化 window.qh 对象');
  22. }
  23.  
  24. if (!window.qh.questionBankData) {
  25. window.qh.questionBankData = [];
  26. console.log('[UI模块] 初始化 window.qh.questionBankData 为空数组');
  27. }
  28.  
  29. if (typeof window.qh.panelCreated === 'undefined') {
  30. window.qh.panelCreated = false;
  31. console.log('[UI模块] 初始化 window.qh.panelCreated 为 false');
  32. }
  33.  
  34. if (!window.qh.courseList) {
  35. window.qh.courseList = [];
  36. console.log('[UI模块] 初始化 window.qh.courseList 为空数组');
  37. }
  38.  
  39. if (typeof window.qh.currentCourseIndex === 'undefined') {
  40. window.qh.currentCourseIndex = 0;
  41. console.log('[UI模块] 初始化 window.qh.currentCourseIndex 为 0');
  42. }
  43.  
  44. } catch (e) {
  45. console.error('[UI模块] 初始化 window.qh 对象时出错:', e);
  46. return;
  47. }
  48.  
  49. // 检查是否在iframe中,如果是则不创建面板
  50. try {
  51. if (window.self !== window.top) {
  52. console.log('[UI模块] 在iframe中,不创建控制面板');
  53. return;
  54. }
  55. } catch (e) {
  56. console.error('[UI模块] 检查iframe状态出错:', e);
  57. return;
  58. }
  59.  
  60. // 检查是否已经创建过面板,避免重复创建
  61. if (window.qh.panelCreated || document.querySelector('.qh-assistant-panel')) {
  62. console.log('[UI模块] 面板已存在,跳过创建');
  63. // 如果面板已存在但不可见,则显示它
  64. const existingPanel = document.querySelector('.qh-assistant-panel');
  65. if (existingPanel && existingPanel.style.display === 'none') {
  66. existingPanel.style.display = 'block';
  67. console.log('[UI模块] 显示已存在的面板');
  68. }
  69. return;
  70. }
  71.  
  72. const panel = document.createElement('div');
  73. panel.className = 'qh-assistant-panel';
  74. panel.id = 'qh-assistant-panel';
  75. panel.innerHTML = `
  76. <div class="qh-assistant-title">
  77. 岐黄天使刷课助手 v1.4.0
  78. <span class="qh-assistant-minimize">_</span>
  79. <span class="qh-assistant-close">✖</span>
  80. </div>
  81. <div class="qh-assistant-content">
  82. <div><span>状态:</span> <span id="qh-status">初始化中...</span></div>
  83. <div><span>当前课程:</span> <span id="qh-current-course">无</span></div>
  84. <div><span>进度:</span> <span id="qh-progress">0%</span></div>
  85. <div class="qh-assistant-progress">
  86. <div class="qh-assistant-progress-bar" id="qh-progress-bar"></div>
  87. </div>
  88. <div><span>每日上限:</span> <span id="qh-daily-limit-status">未检测</span></div>
  89. <div><span>距离重置:</span> <span id="qh-daily-limit-countdown">--:--:--</span></div>
  90. <div style="text-align: center; margin-top: 5px;">
  91. <button class="qh-assistant-btn" id="qh-manual-reset-btn" style="background: linear-gradient(90deg, #607D8B, #455A64); font-size: 12px; padding: 4px 8px;">手动重置上限</button>
  92. </div>
  93. </div>
  94. <div class="qh-assistant-nav-btns">
  95. <button class="qh-assistant-nav-btn" id="qh-prev-btn" disabled>上一课</button>
  96. <button class="qh-assistant-nav-btn" id="qh-next-btn" disabled>下一课</button>
  97. </div>
  98. <button class="qh-assistant-btn" id="qh-toggle-btn">开始自动刷课</button>
  99. <button class="qh-assistant-btn" id="qh-get-questions-btn" style="background: linear-gradient(90deg, #FF9800, #F57C00);">查看题库</button>
  100. <button class="qh-assistant-btn" id="qh-auto-answer-btn" style="background: linear-gradient(90deg, #E91E63, #C2185B);">自动答题</button>
  101. <button class="qh-assistant-btn" id="qh-chapter-exam-btn" style="background: linear-gradient(90deg, #9C27B0, #7B1FA2);">章节考试</button>
  102. <button class="qh-assistant-btn" id="qh-final-exam-btn" style="background: linear-gradient(90deg, #3F51B5, #303F9F);">结业考试</button>
  103. <button class="qh-assistant-btn" id="qh-auto-flow-btn" style="background: linear-gradient(90deg, #FF5722, #E64A19);">自动化设置</button>
  104. <div id="qh-question-status" style="font-size: 12px; text-align: center; margin-top: 5px; color: rgba(255,255,255,0.7);">
  105. 题库状态: 未加载
  106. </div>
  107. `;
  108.  
  109. try {
  110. document.body.appendChild(panel);
  111. window.qh.panelCreated = true;
  112. console.log('[UI模块] ✅ 控制面板创建成功');
  113. } catch (e) {
  114. console.error('[UI模块] ❌ 添加面板到页面时出错:', e);
  115. return;
  116. }
  117.  
  118. // 绑定事件处理器
  119. try {
  120. // 绑定关闭按钮事件
  121. const closeBtn = document.querySelector('.qh-assistant-close');
  122. if (closeBtn) {
  123. closeBtn.addEventListener('click', function() {
  124. panel.style.display = 'none';
  125. });
  126. console.log('[UI模块] 关闭按钮事件绑定成功');
  127. }
  128.  
  129. // 绑定最小化按钮事件
  130. const minimizeBtn = document.querySelector('.qh-assistant-minimize');
  131. if (minimizeBtn) {
  132. minimizeBtn.addEventListener('click', function() {
  133. panel.classList.toggle('minimized');
  134. if (panel.classList.contains('minimized')) {
  135. this.textContent = '+';
  136. } else {
  137. this.textContent = '_';
  138. }
  139. });
  140. console.log('[UI模块] 最小化按钮事件绑定成功');
  141. }
  142. } catch (e) {
  143. console.error('[UI模块] 绑定基础按钮事件时出错:', e);
  144. }
  145.  
  146. // 绑定功能按钮事件
  147. try {
  148. // 绑定开始/暂停按钮事件
  149. const toggleBtn = document.getElementById('qh-toggle-btn');
  150. if (toggleBtn) {
  151. toggleBtn.addEventListener('click', function() {
  152. if (typeof toggleAutoLearn === 'function') {
  153. toggleAutoLearn();
  154. } else {
  155. console.warn('[UI模块] toggleAutoLearn 函数不可用');
  156. }
  157. });
  158. console.log('[UI模块] 开始/暂停按钮事件绑定成功');
  159. }
  160.  
  161. // 绑定上一课按钮事件
  162. const prevBtn = document.getElementById('qh-prev-btn');
  163. if (prevBtn) {
  164. prevBtn.addEventListener('click', function() {
  165. if (typeof navigateToPrevCourse === 'function') {
  166. navigateToPrevCourse();
  167. } else {
  168. console.warn('[UI模块] navigateToPrevCourse 函数不可用');
  169. }
  170. });
  171. console.log('[UI模块] 上一课按钮事件绑定成功');
  172. }
  173.  
  174. // 绑定下一课按钮事件
  175. const nextBtn = document.getElementById('qh-next-btn');
  176. if (nextBtn) {
  177. nextBtn.addEventListener('click', function() {
  178. if (typeof navigateToNextCourse === 'function') {
  179. navigateToNextCourse();
  180. } else {
  181. console.warn('[UI模块] navigateToNextCourse 函数不可用');
  182. }
  183. });
  184. console.log('[UI模块] 下一课按钮事件绑定成功');
  185. }
  186. } catch (e) {
  187. console.error('[UI模块] 绑定导航按钮事件时出错:', e);
  188. }
  189.  
  190. // 绑定其他功能按钮事件
  191. try {
  192. // 绑定获取题目和答案按钮事件
  193. const questionsBtn = document.getElementById('qh-get-questions-btn');
  194. if (questionsBtn) {
  195. questionsBtn.addEventListener('click', function() {
  196. if (typeof showQuestionPanel === 'function') {
  197. showQuestionPanel();
  198. } else {
  199. console.warn('[UI模块] showQuestionPanel 函数不可用');
  200. }
  201. });
  202. console.log('[UI模块] 查看题库按钮事件绑定成功');
  203. }
  204.  
  205. // 绑定自动答题按钮事件
  206. const autoAnswerBtn = document.getElementById('qh-auto-answer-btn');
  207. if (autoAnswerBtn) {
  208. autoAnswerBtn.addEventListener('click', function() {
  209. if (typeof toggleAutoAnswer === 'function') {
  210. toggleAutoAnswer();
  211. } else {
  212. console.warn('[UI模块] toggleAutoAnswer 函数不可用');
  213. }
  214. });
  215. console.log('[UI模块] 自动答题按钮事件绑定成功');
  216. }
  217.  
  218. // 绑定章节考试按钮事件
  219. const chapterExamBtn = document.getElementById('qh-chapter-exam-btn');
  220. if (chapterExamBtn) {
  221. chapterExamBtn.addEventListener('click', function() {
  222. if (typeof showChapterExamPanel === 'function') {
  223. showChapterExamPanel();
  224. } else {
  225. console.warn('[UI模块] showChapterExamPanel 函数不可用');
  226. }
  227. });
  228. console.log('[UI模块] 章节考试按钮事件绑定成功');
  229. }
  230.  
  231. // 绑定结业考试按钮事件
  232. const finalExamBtn = document.getElementById('qh-final-exam-btn');
  233. if (finalExamBtn) {
  234. finalExamBtn.addEventListener('click', function() {
  235. if (typeof checkFinalExamStatus === 'function') {
  236. checkFinalExamStatus();
  237. } else {
  238. console.warn('[UI模块] checkFinalExamStatus 函数不可用');
  239. }
  240. });
  241. console.log('[UI模块] 结业考试按钮事件绑定成功');
  242. }
  243.  
  244. // 绑定自动化设置按钮事件
  245. const autoFlowBtn = document.getElementById('qh-auto-flow-btn');
  246. if (autoFlowBtn) {
  247. autoFlowBtn.addEventListener('click', function() {
  248. if (typeof showAutoFlowSettingsPanel === 'function') {
  249. showAutoFlowSettingsPanel();
  250. } else {
  251. console.warn('[UI模块] showAutoFlowSettingsPanel 函数不可用');
  252. }
  253. });
  254. console.log('[UI模块] 自动化设置按钮事件绑定成功');
  255. }
  256.  
  257. // 绑定手动重置上限按钮事件
  258. const manualResetBtn = document.getElementById('qh-manual-reset-btn');
  259. if (manualResetBtn) {
  260. manualResetBtn.addEventListener('click', function() {
  261. if (window.qh && window.qh.dailyLimitManager && typeof window.qh.dailyLimitManager.manualReset === 'function') {
  262. console.log('[UI模块] 执行手动重置每日上限');
  263. window.qh.dailyLimitManager.manualReset();
  264. alert('每日学习上限已手动重置!');
  265. } else {
  266. console.warn('[UI模块] dailyLimitManager 不可用');
  267. alert('每日上限管理器未初始化,无法重置');
  268. }
  269. });
  270. console.log('[UI模块] 手动重置按钮事件绑定成功');
  271. }
  272.  
  273. console.log('[UI模块] ✅ 所有按钮事件绑定完成');
  274. } catch (e) {
  275. console.error('[UI模块] 绑定功能按钮事件时出错:', e);
  276. }
  277.  
  278. console.log('[UI模块] ✅ createPanel 函数执行完成');
  279. };
  280.  
  281. // 更新状态显示
  282. function updateStatus(status) {
  283. // 在控制台记录状态,确保在iframe中也能看到
  284. console.log('状态更新:', status);
  285.  
  286. // 尝试更新UI状态
  287. const statusEl = document.getElementById('qh-status');
  288. if (statusEl) {
  289. statusEl.textContent = status;
  290. }
  291.  
  292. // 如果在iframe中,尝试向父窗口发送消息
  293. try {
  294. if (window.self !== window.top) {
  295. window.parent.postMessage({
  296. type: 'qh-status-update',
  297. status: status
  298. }, '*');
  299. }
  300. } catch (e) {
  301. console.error('向父窗口发送状态更新失败:', e);
  302. }
  303. };
  304.  
  305. // 更新当前课程显示
  306. function updateCurrentCourse(course) {
  307. const courseEl = document.getElementById('qh-current-course');
  308. if (courseEl) {
  309. if (course && course.trim()) {
  310. courseEl.textContent = course;
  311. console.log('更新当前课程:', course);
  312. } else {
  313. // 如果没有提供课程名称,尝试从页面中获取
  314. const detectedCourse = detectCurrentCourse();
  315. if (detectedCourse) {
  316. courseEl.textContent = detectedCourse;
  317. console.log('自动检测到当前课程:', detectedCourse);
  318. } else {
  319. courseEl.textContent = '无';
  320. console.log('未能检测到当前课程');
  321. }
  322. }
  323. }
  324. };
  325.  
  326. // 检测当前页面课程标题
  327. function detectCurrentCourse() {
  328. try {
  329. // 1. 首先尝试从kcTitle元素获取(最优先)
  330. const kcTitleElement = document.getElementById('kcTitle');
  331. if (kcTitleElement && kcTitleElement.textContent.trim()) {
  332. const title = kcTitleElement.textContent.trim();
  333. console.log('从kcTitle元素获取课程:', title);
  334. return title;
  335. }
  336.  
  337. // 2. 尝试从iframe中查找kcTitle元素
  338. const frames = document.querySelectorAll('iframe');
  339. for (const frame of frames) {
  340. try {
  341. const frameDoc = frame.contentDocument || frame.contentWindow.document;
  342. const frameTitleElement = frameDoc.getElementById('kcTitle');
  343. if (frameTitleElement && frameTitleElement.textContent.trim()) {
  344. const title = frameTitleElement.textContent.trim();
  345. console.log('从iframe中的kcTitle元素获取课程:', title);
  346. return title;
  347. }
  348. } catch (e) {
  349. console.error('无法访问iframe内容:', e);
  350. }
  351. }
  352.  
  353. // 3. 如果从navigateToCourse函数中提取了视频ID和标题,使用该信息
  354. if (window.qh.lastExtractedVideoInfo) {
  355. console.log('使用上次提取的视频信息:', window.qh.lastExtractedVideoInfo);
  356. return window.qh.lastExtractedVideoInfo;
  357. }
  358.  
  359. // 4. 尝试从当前活动的课程链接获取
  360. if (window.qh.courseList.length > 0 && window.qh.currentCourseIndex >= 0 && window.qh.currentCourseIndex < window.qh.courseList.length) {
  361. return window.qh.courseList[window.qh.currentCourseIndex].title;
  362. }
  363.  
  364. return null;
  365. } catch (e) {
  366. console.error('检测当前课程出错:', e);
  367. return null;
  368. }
  369. }
  370.  
  371. // 更新进度条显示
  372. function updateProgress(progress) {
  373. const progressEl = document.getElementById('qh-progress');
  374. const progressBarEl = document.getElementById('qh-progress-bar');
  375. if (progressEl && progressBarEl) {
  376. progressEl.textContent = progress + '%';
  377. progressBarEl.style.width = progress + '%';
  378. }
  379. };
  380.  
  381. // 更新按钮状态
  382. function updateButtonStatus() {
  383. // 获取当前状态,如果GM_getValue不可用,则使用localStorage
  384. let isRunning;
  385. if (typeof GM_getValue !== 'undefined') {
  386. isRunning = GM_getValue('qh-is-running', false);
  387. } else {
  388. isRunning = localStorage.getItem('qh-is-running') === 'true';
  389. }
  390.  
  391. const btn = document.getElementById('qh-toggle-btn');
  392. if (btn) {
  393. btn.textContent = isRunning ? '暂停自动刷课' : '开始自动刷课';
  394. // 使用背景渐变而不是backgroundColor,以匹配CSS样式
  395. if (isRunning) {
  396. btn.style.background = 'linear-gradient(90deg, #f44336, #e53935)';
  397. } else {
  398. btn.style.background = 'linear-gradient(90deg, #4CAF50, #45a049)';
  399. }
  400. console.log('播放状态已更新:', isRunning ? '播放中' : '已暂停');
  401. }
  402.  
  403. // 更新题库状态显示 (增强安全检查)
  404. const qStatusEl = document.getElementById('qh-question-status');
  405. if (qStatusEl) {
  406. try {
  407. // 确保 window.qh 和 questionBankData 都存在且为数组
  408. if (window.qh &&
  409. window.qh.questionBankData &&
  410. Array.isArray(window.qh.questionBankData) &&
  411. window.qh.questionBankData.length > 0) {
  412. qStatusEl.textContent = `题库: ${window.qh.questionBankData.length} 道`;
  413. } else {
  414. // 初始化 questionBankData 如果不存在
  415. if (window.qh && !window.qh.questionBankData) {
  416. window.qh.questionBankData = [];
  417. console.log('[UI模块] 初始化 questionBankData 为空数组');
  418. }
  419. qStatusEl.textContent = '题库: 未加载或为空';
  420. }
  421. } catch (error) {
  422. console.error('[UI模块] 更新题库状态时出错:', error);
  423. qStatusEl.textContent = '题库: 状态错误';
  424.  
  425. // 确保 questionBankData 被正确初始化
  426. if (window.qh && !Array.isArray(window.qh.questionBankData)) {
  427. window.qh.questionBankData = [];
  428. console.log('[UI模块] 修复 questionBankData 初始化');
  429. }
  430. }
  431. }
  432. }
  433.  
  434. // 更新上一课/下一课按钮状态
  435. function updateNavButtons(canPrev, canNext) {
  436. const prevBtn = document.getElementById('qh-prev-btn');
  437. const nextBtn = document.getElementById('qh-next-btn');
  438. if (prevBtn) {
  439. prevBtn.disabled = !canPrev;
  440. }
  441. if (nextBtn) {
  442. nextBtn.disabled = !canNext;
  443. }
  444. }
  445.  
  446. // 更新每日学习上限相关的UI元素
  447. function updateDailyLimitDisplay(statusText, countdownText) {
  448. const limitStatusEl = document.getElementById('qh-daily-limit-status');
  449. const countdownEl = document.getElementById('qh-daily-limit-countdown');
  450.  
  451. if (limitStatusEl) {
  452. limitStatusEl.textContent = statusText;
  453. }
  454. if (countdownEl) {
  455. countdownEl.textContent = countdownText;
  456. }
  457. }
  458.  
  459. // 导出模块API
  460. window.qh = window.qh || {};
  461. window.qh.createPanel = createPanel; // 保留导出以防万一,但主要应由模块自行调用
  462. window.qh.updateStatus = updateStatus;
  463. window.qh.updateCurrentCourse = updateCurrentCourse;
  464. window.qh.detectCurrentCourse = detectCurrentCourse;
  465. window.qh.updateProgress = updateProgress;
  466. window.qh.updateButtonStatus = updateButtonStatus;
  467. window.qh.updateNavButtons = updateNavButtons;
  468. window.qh.updateDailyLimitDisplay = updateDailyLimitDisplay;
  469. // window.showQuestionPanel 由 questionBank.js 或类似模块处理,这里不重复导出
  470. // 其他按钮的点击事件直接绑定,不需要导出其处理函数
  471.  
  472. // 模块加载时自动创建面板
  473. function initUI() {
  474. console.log('[UI模块] 准备初始化UI...');
  475. // 确保 qh 对象存在
  476. window.qh = window.qh || {};
  477. createPanel();
  478. // 可以在这里进行其他UI相关的初始化设置
  479. // 例如,根据存储的状态更新按钮文本等
  480. updateButtonStatus(); // 根据初始状态更新按钮
  481. updateNavButtons(false, false); // 初始禁用导航按钮
  482. }
  483.  
  484. if (document.readyState === 'loading') {
  485. document.addEventListener('DOMContentLoaded', initUI);
  486. } else {
  487. // DOMContentLoaded has already fired or state is 'interactive' or 'complete'
  488. setTimeout(initUI, 0); // Use setTimeout to ensure it runs after current script block
  489. }
  490.  
  491. console.log('[模块加载] ui 模块已加载并配置为自动初始化');
  492. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址