ahszu_01.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // 宿州学院(ahszu.edu.cn)拾光适配代码
  2. // 非该大学开发者适配,开发者无法及时发现问题
  3. // 出现问题请提issues或者提交pr更改,这更加快速
  4. /**
  5. * 基础工具函数:Base64 编码
  6. */
  7. function encodeParams(xn, xq) {
  8. const rawStr = `xn=${xn}&xq=${xq}`;
  9. return btoa(rawStr);
  10. }
  11. /**
  12. * 判断是否处于教务系统登录环境
  13. */
  14. async function checkLoginEnvironment() {
  15. const currentUrl = window.location.href;
  16. const loginUrl = "http://211.86.128.194/suzxyjw/cas/login.action";
  17. if (currentUrl.includes(loginUrl)) {
  18. AndroidBridge.showToast("请先登录教务系统再进行导入");
  19. return false;
  20. }
  21. return true;
  22. }
  23. /**
  24. * 深度解析周数字符串 (支持 1-16, 1-8单, 9-17双, 1,3,5等格式)
  25. */
  26. function parseWeeks(weekStr) {
  27. const weeks = [];
  28. const groups = weekStr.split(',');
  29. groups.forEach(group => {
  30. const isSingle = group.includes('单');
  31. const isDouble = group.includes('双');
  32. const rangeMatch = group.match(/(\d+)-(\d+)/);
  33. if (rangeMatch) {
  34. const start = parseInt(rangeMatch[1]);
  35. const end = parseInt(rangeMatch[2]);
  36. for (let i = start; i <= end; i++) {
  37. if (isSingle && i % 2 === 0) continue;
  38. if (isDouble && i % 2 !== 0) continue;
  39. weeks.push(i);
  40. }
  41. } else {
  42. const num = parseInt(group.replace(/[^\d]/g, ''));
  43. if (!isNaN(num)) weeks.push(num);
  44. }
  45. });
  46. return Array.from(new Set(weeks)).sort((a, b) => a - b);
  47. }
  48. /**
  49. * 数据解析函数
  50. */
  51. function parseAndMergeXmcuData(htmlText) {
  52. const parser = new DOMParser();
  53. const doc = parser.parseFromString(htmlText, 'text/html');
  54. const rawItems = [];
  55. const table = doc.getElementById('mytable');
  56. if (!table) return [];
  57. const rows = table.querySelectorAll('tr');
  58. rows.forEach((row) => {
  59. // 排除表头和特殊行,只处理包含课程内容的行
  60. const cells = row.querySelectorAll('td.td');
  61. if (cells.length === 0) return;
  62. cells.forEach((cell, dayIndex) => {
  63. const day = dayIndex + 1; // 对应 星期一 到 星期五
  64. // 找到所有包含课程信息的 div
  65. const courseDivs = cell.querySelectorAll('div[style*="padding-bottom:5px"]');
  66. courseDivs.forEach(div => {
  67. // 提取文本并过滤空行
  68. const lines = Array.from(div.childNodes)
  69. .map(n => n.textContent.trim())
  70. .filter(t => t.length > 0);
  71. if (lines.length >= 3) {
  72. const name = lines[0];
  73. const teacher = lines[1];
  74. // 匹配格式:周数[节次] -> 例如 "1-8 单 [3-4]"
  75. const timeMatch = lines[2].match(/(.*)\[(.*)\]/);
  76. const position = lines[3] || "未知地点";
  77. if (timeMatch) {
  78. const weeks = parseWeeks(timeMatch[1]);
  79. const sections = timeMatch[2].split('-').map(Number);
  80. rawItems.push({
  81. name,
  82. teacher,
  83. position,
  84. day,
  85. startSection: sections[0],
  86. endSection: sections[sections.length - 1],
  87. weeks
  88. });
  89. }
  90. }
  91. });
  92. });
  93. });
  94. const groupMap = new Map();
  95. rawItems.forEach(item => {
  96. const key = `${item.name}|${item.teacher}|${item.position}|${item.day}`;
  97. if (!groupMap.has(key)) groupMap.set(key, []);
  98. groupMap.get(key).push(item);
  99. });
  100. const finalCourses = [];
  101. groupMap.forEach((items, key) => {
  102. const matrix = {};
  103. items.forEach(item => {
  104. item.weeks.forEach(w => {
  105. if (!matrix[w]) matrix[w] = new Set();
  106. for (let s = item.startSection; s <= item.endSection; s++) matrix[w].add(s);
  107. });
  108. });
  109. const patternMap = new Map();
  110. Object.keys(matrix).forEach(w => {
  111. const week = parseInt(w);
  112. const sections = Array.from(matrix[week]).sort((a, b) => a - b);
  113. let start = sections[0];
  114. for (let i = 0; i < sections.length; i++) {
  115. if (i === sections.length - 1 || sections[i+1] !== sections[i] + 1) {
  116. const pKey = `${start}-${sections[i]}`;
  117. if (!patternMap.has(pKey)) patternMap.set(pKey, []);
  118. patternMap.get(pKey).push(week);
  119. if (i < sections.length - 1) start = sections[i+1];
  120. }
  121. }
  122. });
  123. const [name, teacher, position, day] = key.split('|');
  124. patternMap.forEach((weeks, pKey) => {
  125. const [sStart, sEnd] = pKey.split('-').map(Number);
  126. finalCourses.push({
  127. name, teacher, position,
  128. day: parseInt(day),
  129. startSection: sStart,
  130. endSection: sEnd,
  131. weeks: weeks.sort((a, b) => a - b)
  132. });
  133. });
  134. });
  135. return finalCourses;
  136. }
  137. /**
  138. * 学期获取函数
  139. */
  140. async function getYearAndSemester() {
  141. try {
  142. AndroidBridge.showToast("正在获取学期列表...");
  143. const response = await fetch("http://211.86.128.194/suzxyjw/frame/droplist/getDropLists.action", {
  144. method: "POST",
  145. headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
  146. body: "comboBoxName=StMsXnxqDxDesc&paramValue=&isYXB=0&isCDDW=0&isXQ=0&isDJKSLB=0&isZY=0",
  147. credentials: "include"
  148. });
  149. const list = await response.json();
  150. const names = list.map(item => item.name);
  151. const selectedIndex = await window.AndroidBridgePromise.showSingleSelection("选择导入学期", JSON.stringify(names), 0);
  152. if (selectedIndex === null) return null;
  153. const [xn, xq] = list[selectedIndex].code.split('-');
  154. return { xn, xq };
  155. } catch (error) {
  156. AndroidBridge.showToast("获取列表失败");
  157. return null;
  158. }
  159. }
  160. /**
  161. * 课表抓取函数
  162. */
  163. async function fetchCourses(xn, xq) {
  164. try {
  165. const paramsBase64 = encodeParams(xn, xq);
  166. const url = `http://211.86.128.194/suzxyjw/student/wsxk.xskcb10319.jsp?params=${paramsBase64}`;
  167. AndroidBridge.showToast("正在提取数据...");
  168. const response = await fetch(url, { method: "GET", credentials: "include" });
  169. const arrayBuffer = await response.arrayBuffer();
  170. const htmlText = new TextDecoder('gbk').decode(arrayBuffer);
  171. return parseAndMergeXmcuData(htmlText);
  172. } catch (error) {
  173. AndroidBridge.showToast("抓取课表失败");
  174. return null;
  175. }
  176. }
  177. /**
  178. * 时间段导入函数
  179. */
  180. async function importPresetTimeSlots() {
  181. const slots = [
  182. { "number": 1, "startTime": "08:05", "endTime": "08:50" },
  183. { "number": 2, "startTime": "09:00", "endTime": "09:45" },
  184. { "number": 3, "startTime": "10:05", "endTime": "10:50" },
  185. { "number": 4, "startTime": "11:00", "endTime": "11:45" },
  186. { "number": 5, "startTime": "14:40", "endTime": "15:25" },
  187. { "number": 6, "startTime": "15:35", "endTime": "16:20" },
  188. { "number": 7, "startTime": "16:30", "endTime": "17:15" },
  189. { "number": 8, "startTime": "17:25", "endTime": "18:10" },
  190. { "number": 9, "startTime": "19:00", "endTime": "19:45" },
  191. { "number": 10, "startTime": "19:55", "endTime": "20:40" },
  192. { "number": 11, "startTime": "20:50", "endTime": "21:35" }
  193. ];
  194. await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(slots)).catch(() => {});
  195. }
  196. /**
  197. * 最终流程控制
  198. */
  199. async function runImportFlow() {
  200. // 环境检查 (非教务页面直接 Toast 退出)
  201. const isReady = await checkLoginEnvironment();
  202. if (!isReady) return;
  203. // 弹窗确认
  204. const confirmed = await window.AndroidBridgePromise.showAlert("教务导入", "建议在‘课表查询’页面进行导入以确保数据最全。", "确定");
  205. if (!confirmed) return;
  206. // 选择学期
  207. const params = await getYearAndSemester();
  208. if (!params) return;
  209. // 获取并解析数据
  210. const courses = await fetchCourses(params.xn, params.xq);
  211. if (!courses || courses.length === 0) {
  212. AndroidBridge.showToast("未找到有效课程");
  213. return;
  214. }
  215. // 存储
  216. await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(courses));
  217. await importPresetTimeSlots();
  218. // 完成
  219. AndroidBridge.showToast(`导入成功:共 ${courses.length} 门课程`);
  220. AndroidBridge.notifyTaskCompletion();
  221. }
  222. // 启动
  223. runImportFlow();