qingguo_01.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /**
  2. * 青果教务系统通用适配脚本
  3. * 采用“倒数 7 列”逻辑,解决星期偏移
  4. */
  5. function parseWeeks(weekStr) {
  6. const weeks = [];
  7. weekStr.split(',').forEach(part => {
  8. if (part.includes('-')) {
  9. const [start, end] = part.split('-').map(Number);
  10. for (let i = start; i <= end; i++) weeks.push(i);
  11. } else {
  12. const w = parseInt(part);
  13. if (!isNaN(w)) weeks.push(w);
  14. }
  15. });
  16. return weeks;
  17. }
  18. async function fetchAndParseCourses() {
  19. const rawItems = [];
  20. function findTable(win) {
  21. const t = Array.from(win.document.querySelectorAll('table')).find(x => x.innerText.includes("星期一") && x.innerText.includes("["));
  22. if (t) return t;
  23. for (let i = 0; i < win.frames.length; i++) {
  24. try { const st = findTable(win.frames[i]); if (st) return st; } catch (e) {}
  25. }
  26. return null;
  27. }
  28. const table = findTable(window);
  29. if (!table) return null;
  30. // 原始数据清洗抓取
  31. Array.from(table.rows).forEach(row => {
  32. const cells = Array.from(row.cells);
  33. if (cells.length < 7) return;
  34. cells.forEach((cell, colIndex) => {
  35. const distanceToLast = cells.length - 1 - colIndex;
  36. if (distanceToLast > 6) return;
  37. const day = 7 - distanceToLast;
  38. const rawText = cell.innerText.trim();
  39. if (!rawText.includes('[')) return;
  40. // 过滤掉空白行,并清洗每一行的首尾空格
  41. const lines = rawText.split('\n').map(l => l.trim()).filter(l => l);
  42. lines.forEach((line, i) => {
  43. const match = line.match(/([\d\-,]+)\[(\d+)-(\d+)\]/);
  44. if (match) {
  45. let name = "未知课程";
  46. if (i >= 2) name = lines[i-2];
  47. else if (i >= 1) name = lines[i-1];
  48. let teacher = (i >= 1 && !lines[i-1].includes('[')) ? lines[i-1] : "未知教师";
  49. let position = (i < lines.length - 1) ? lines[i+1] : "未知地点";
  50. rawItems.push({
  51. name: name.replace(/\s/g, ""), // 去除所有空格
  52. teacher: teacher.replace(/\s/g, ""),
  53. position: position.replace(/\s/g, ""),
  54. day: day,
  55. startSection: parseInt(match[2]),
  56. endSection: parseInt(match[3]),
  57. weeks: parseWeeks(match[1])
  58. });
  59. }
  60. });
  61. });
  62. });
  63. // 矩阵合并
  64. const groupMap = new Map();
  65. rawItems.forEach(item => {
  66. const key = `${item.name}|${item.teacher}|${item.position}|${item.day}`;
  67. if (!groupMap.has(key)) groupMap.set(key, {});
  68. const weekMap = groupMap.get(key);
  69. item.weeks.forEach(w => {
  70. if (!weekMap[w]) weekMap[w] = new Set();
  71. for (let s = item.startSection; s <= item.endSection; s++) {
  72. weekMap[w].add(s);
  73. }
  74. });
  75. });
  76. const finalCourses = [];
  77. groupMap.forEach((weekMap, key) => {
  78. const [name, teacher, position, day] = key.split('|');
  79. // 模式聚合:寻找具有相同“节次跨度”的周次
  80. const patternMap = new Map();
  81. Object.keys(weekMap).forEach(w => {
  82. const week = parseInt(w);
  83. const sections = Array.from(weekMap[week]).sort((a, b) => a - b);
  84. if (sections.length === 0) return;
  85. // 重新切分连续节次
  86. let start = sections[0];
  87. for (let i = 0; i < sections.length; i++) {
  88. if (i === sections.length - 1 || sections[i+1] !== sections[i] + 1) {
  89. const pKey = `${start}-${sections[i]}`;
  90. if (!patternMap.has(pKey)) patternMap.set(pKey, []);
  91. patternMap.get(pKey).push(week);
  92. if (i < sections.length - 1) start = sections[i+1];
  93. }
  94. }
  95. });
  96. patternMap.forEach((weeks, pKey) => {
  97. const [sStart, sEnd] = pKey.split('-').map(Number);
  98. finalCourses.push({
  99. name, teacher, position,
  100. day: parseInt(day),
  101. startSection: sStart,
  102. endSection: sEnd,
  103. weeks: weeks.sort((a, b) => a - b)
  104. });
  105. });
  106. });
  107. return finalCourses;
  108. }
  109. async function runImportFlow() {
  110. try {
  111. AndroidBridge.showToast("正在合并课表数据...");
  112. const courses = await fetchAndParseCourses();
  113. if (!courses || courses.length === 0) {
  114. AndroidBridge.showToast("未找到可导入课程");
  115. return;
  116. }
  117. await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(courses));
  118. AndroidBridge.showToast(`成功:已优化合并为 ${courses.length} 个课块`);
  119. AndroidBridge.notifyTaskCompletion();
  120. } catch (error) {
  121. AndroidBridge.showToast("解析失败: " + error.message);
  122. }
  123. }
  124. runImportFlow();