JSNU_01.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /**
  2. * 解析强智系统的周次字符串
  3. */
  4. function parseWeeks(weekStr) {
  5. let weeks = [];
  6. let parts = weekStr.split(',');
  7. for (let part of parts) {
  8. if (part.includes('-')) {
  9. let [start, end] = part.split('-');
  10. for (let i = parseInt(start); i <= parseInt(end); i++) {
  11. if (!weeks.includes(i)) weeks.push(i);
  12. }
  13. } else {
  14. let w = parseInt(part);
  15. if (!isNaN(w) && !weeks.includes(w)) weeks.push(w);
  16. }
  17. }
  18. return weeks.sort((a, b) => a - b);
  19. }
  20. /**
  21. * 提取课程数据(已适配带有 item-box 二维码和特殊字体的强智新版)
  22. */
  23. function extractCoursesFromDoc(doc) {
  24. let parsedCourses = [];
  25. const table = doc.getElementById('timetable');
  26. if (!table) throw new Error("请求成功但未找到课表表格,请确认教务系统状态。");
  27. const rows = table.getElementsByTagName('tr');
  28. // 跳过表头(0)和尾部的备注行
  29. for (let i = 1; i < rows.length - 1; i++) {
  30. const cells = rows[i].getElementsByTagName('td');
  31. for (let j = 0; j < cells.length; j++) {
  32. const dayOfWeek = j + 1; // 强智表格:第0个td是周一
  33. const cell = cells[j];
  34. // 找到包含详细信息的隐藏 div
  35. const detailDivs = cell.querySelectorAll('div.kbcontent');
  36. if (detailDivs.length === 0) continue;
  37. detailDivs.forEach(div => {
  38. let htmlContent = div.innerHTML;
  39. if (!htmlContent.trim() || htmlContent === '&nbsp;') return;
  40. // 强智同一个时间段如果有多门课,用连续破折号分隔
  41. let courseBlocks = htmlContent.split(/-{10,}\s*<br\s*\/?>/i);
  42. courseBlocks.forEach(block => {
  43. if (!block.trim()) return;
  44. let tempDiv = document.createElement('div');
  45. tempDiv.innerHTML = block;
  46. let courseObj = {
  47. day: dayOfWeek,
  48. isCustomTime: false
  49. };
  50. // 1. 提取课程名 (移除二维码 div 后,取第一行纯文本)
  51. let itemBoxes = tempDiv.querySelectorAll('.item-box');
  52. itemBoxes.forEach(box => box.remove()); // 剔除干扰项
  53. let lines = tempDiv.innerHTML.split(/<br\s*\/?>/i);
  54. for (let line of lines) {
  55. let cleanLine = line.replace(/<[^>]+>/g, '').trim(); // 剥离 font 等 HTML 标签
  56. if (cleanLine && cleanLine !== "") {
  57. courseObj.name = cleanLine;
  58. break;
  59. }
  60. }
  61. // 2. 提取教师
  62. let teacherFont = tempDiv.querySelector('font[title="教师"]');
  63. courseObj.teacher = teacherFont ? teacherFont.innerText.trim() : "未知";
  64. // 3. 提取教室
  65. let positionFont = tempDiv.querySelector('font[title="教室"]');
  66. courseObj.position = positionFont ? positionFont.innerText.trim() : "待定";
  67. // 4. 提取周次和节次 (适配 [01-02节], [03-04-05节], [10节] 等格式)
  68. let timeFont = tempDiv.querySelector('font[title="周次(节次)"]');
  69. if (timeFont) {
  70. let timeText = timeFont.innerText.trim();
  71. // 正则:匹配 "X-Y(周)[A-B-C节]" 或 "X(周)[A节]"
  72. let timeMatch = timeText.match(/(.+?)\(周\)(?:\[([\d-]+)节\])?/);
  73. if (timeMatch) {
  74. courseObj.weeks = parseWeeks(timeMatch[1]);
  75. if (timeMatch[2]) {
  76. let secParts = timeMatch[2].split('-');
  77. courseObj.startSection = parseInt(secParts[0]);
  78. courseObj.endSection = parseInt(secParts[secParts.length - 1]);
  79. } else {
  80. // 兜底:如果没有标明节次,则根据行号估算
  81. courseObj.startSection = i * 2 - 1;
  82. courseObj.endSection = i * 2;
  83. }
  84. }
  85. } else {
  86. return; // 如果没有时间信息,抛弃该条记录(比如无课表课程)
  87. }
  88. if (courseObj.name && courseObj.weeks && courseObj.weeks.length > 0) {
  89. parsedCourses.push(courseObj);
  90. }
  91. });
  92. });
  93. }
  94. }
  95. return parsedCourses;
  96. }
  97. /**
  98. * 生成学校专属的作息时间段
  99. */
  100. function getPresetTimeSlots() {
  101. return [
  102. { "number": 1, "startTime": "08:00", "endTime": "08:40" },
  103. { "number": 2, "startTime": "08:45", "endTime": "09:25" },
  104. { "number": 3, "startTime": "09:45", "endTime": "10:25" },
  105. { "number": 4, "startTime": "10:30", "endTime": "11:10" },
  106. { "number": 5, "startTime": "11:15", "endTime": "11:55" },
  107. { "number": 6, "startTime": "14:00", "endTime": "14:40" },
  108. { "number": 7, "startTime": "14:45", "endTime": "15:25" },
  109. { "number": 8, "startTime": "15:45", "endTime": "16:25" },
  110. { "number": 9, "startTime": "16:30", "endTime": "17:10" },
  111. { "number": 10, "startTime": "18:30", "endTime": "19:10" },
  112. { "number": 11, "startTime": "19:15", "endTime": "19:55" }
  113. ];
  114. }
  115. /**
  116. * 生成全局课表配置
  117. */
  118. function getCourseConfig() {
  119. return {
  120. "defaultClassDuration": 40, // 单节课 40 分钟
  121. "defaultBreakDuration": 5 // 默认课间(长课间靠自定义时间段覆盖)
  122. };
  123. }
  124. /**
  125. * 异步编排流程
  126. */
  127. async function runImportFlow() {
  128. try {
  129. if (typeof window.AndroidBridge !== 'undefined') {
  130. AndroidBridge.showToast("正在获取课表数据,请稍候...");
  131. } else {
  132. console.log("正在发起请求获取课表...");
  133. }
  134. const response = await fetch('/jsxsd/xskb/xskb_list.do', { method: 'GET' });
  135. const htmlText = await response.text();
  136. const parser = new DOMParser();
  137. let doc = parser.parseFromString(htmlText, 'text/html');
  138. const selectElem = doc.getElementById('xnxq01id');
  139. let semesters = [];
  140. let semesterValues = [];
  141. let defaultIndex = 0;
  142. if (selectElem) {
  143. const options = selectElem.querySelectorAll('option');
  144. options.forEach((opt, index) => {
  145. semesters.push(opt.innerText.trim());
  146. semesterValues.push(opt.value);
  147. if (opt.hasAttribute('selected')) {
  148. defaultIndex = index;
  149. }
  150. });
  151. }
  152. if (semesters.length > 0 && typeof window.AndroidBridgePromise !== 'undefined') {
  153. let selectedIdx = await window.AndroidBridgePromise.showSingleSelection(
  154. "请选择要导入的学期",
  155. JSON.stringify(semesters),
  156. defaultIndex
  157. );
  158. if (selectedIdx === null) {
  159. AndroidBridge.showToast("已取消导入");
  160. return;
  161. }
  162. if (selectedIdx !== defaultIndex) {
  163. AndroidBridge.showToast(`正在获取 [${semesters[selectedIdx]}] 课表...`);
  164. let formData = new URLSearchParams();
  165. formData.append('xnxq01id', semesterValues[selectedIdx]);
  166. const postResponse = await fetch('/jsxsd/xskb/xskb_list.do', {
  167. method: 'POST',
  168. headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  169. body: formData.toString()
  170. });
  171. const postHtml = await postResponse.text();
  172. doc = parser.parseFromString(postHtml, 'text/html');
  173. }
  174. }
  175. const courses = extractCoursesFromDoc(doc);
  176. if (courses.length === 0) {
  177. const errMsg = "未能解析到任何课程,请检查是否暂无排课。";
  178. if (typeof window.AndroidBridgePromise !== 'undefined') {
  179. await window.AndroidBridgePromise.showAlert("提示", errMsg, "好的");
  180. } else {
  181. alert(errMsg);
  182. }
  183. return;
  184. }
  185. const config = getCourseConfig();
  186. const timeSlots = getPresetTimeSlots();
  187. // 浏览器测试环境
  188. if (typeof window.AndroidBridgePromise === 'undefined') {
  189. console.log("【测试成功】课表配置:", config);
  190. console.log("【测试成功】作息时间:", timeSlots);
  191. console.log("【测试成功】课程数据:\n", JSON.stringify(courses, null, 2));
  192. alert(`解析成功!获取到 ${courses.length} 门课程以及定制版作息时间。请打开F12控制台查看。`);
  193. return;
  194. }
  195. // APP 环境保存数据
  196. await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify(config));
  197. await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(timeSlots));
  198. const saveResult = await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(courses));
  199. if (!saveResult) {
  200. AndroidBridge.showToast("保存课程失败,请重试!");
  201. return;
  202. }
  203. AndroidBridge.showToast(`成功导入 ${courses.length} 节课程及作息时间!`);
  204. AndroidBridge.notifyTaskCompletion();
  205. } catch (error) {
  206. if (typeof window.AndroidBridge !== 'undefined') {
  207. AndroidBridge.showToast("导入发生异常: " + error.message);
  208. } else {
  209. console.error("【导入发生异常】", error);
  210. alert("导入发生异常: " + error.message);
  211. }
  212. }
  213. }
  214. runImportFlow();