hrbeu_yjs.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. // 哈尔滨工程大学(yjs.hrbeu.edu.cn) 研究生课表适配脚本
  2. // 研究生教育管理信息系统
  3. var HEU_YJS_TIME_SLOTS = [
  4. { number: 1, startTime: "08:00", endTime: "08:45" },
  5. { number: 2, startTime: "08:50", endTime: "09:35" },
  6. { number: 3, startTime: "09:55", endTime: "10:40" },
  7. { number: 4, startTime: "10:50", endTime: "11:35" },
  8. { number: 5, startTime: "11:35", endTime: "12:20" },
  9. { number: 6, startTime: "13:30", endTime: "14:15" },
  10. { number: 7, startTime: "14:20", endTime: "15:05" },
  11. { number: 8, startTime: "15:25", endTime: "16:10" },
  12. { number: 9, startTime: "16:20", endTime: "17:05" },
  13. { number: 10, startTime: "17:05", endTime: "17:50" },
  14. { number: 11, startTime: "18:30", endTime: "19:15" },
  15. { number: 12, startTime: "19:25", endTime: "20:10" },
  16. { number: 13, startTime: "20:10", endTime: "20:55" }
  17. ];
  18. var DAY_FIELDS = [
  19. { field: "Monday", day: 1 },
  20. { field: "Tuesday", day: 2 },
  21. { field: "Wednesday", day: 3 },
  22. { field: "Thursday", day: 4 },
  23. { field: "Friday", day: 5 },
  24. { field: "Saturday", day: 6 },
  25. { field: "Sunday", day: 7 }
  26. ];
  27. function findTimetableRequest() {
  28. var resources = performance.getEntriesByType("resource");
  29. var matches = resources.map(function(r) { return r.name; })
  30. .filter(function(url) { return url.indexOf("GetTimeTableByStudent") !== -1; });
  31. return matches.length ? matches[matches.length - 1] : null;
  32. }
  33. function parseWeeks(text) {
  34. if (!text) return [];
  35. var weeks = {};
  36. var parts = text.split(/[;;]/);
  37. for (var i = 0; i < parts.length; i++) {
  38. var part = parts[i].replace(/\([^)]*\)/g, "").trim();
  39. if (!part) continue;
  40. var segs = part.split(/[,,]/);
  41. for (var j = 0; j < segs.length; j++) {
  42. var seg = segs[j].trim();
  43. var rangeMatch = seg.match(/(\d+)\s*[-~]\s*(\d+)/);
  44. if (rangeMatch) {
  45. for (var w = parseInt(rangeMatch[1]); w <= parseInt(rangeMatch[2]); w++) {
  46. weeks[w] = true;
  47. }
  48. } else {
  49. var num = parseInt(seg);
  50. if (!isNaN(num) && num > 0) weeks[num] = true;
  51. }
  52. }
  53. }
  54. return Object.keys(weeks).map(Number).sort(function(a, b) { return a - b; });
  55. }
  56. function parseMultiTeacherWeeks(weekText) {
  57. // 格式: "1-5(张翼飞);7-9(张翼飞);10-17(郝龙)"
  58. var result = [];
  59. var re = /([\d,\-~]+)\(([^)]+)\)/g;
  60. var match;
  61. while ((match = re.exec(weekText)) !== null) {
  62. result.push({ weeks: parseWeeks(match[1]), teacher: match[2].trim() });
  63. }
  64. return result;
  65. }
  66. function parseSections(text) {
  67. if (!text) return null;
  68. var match = text.match(/(\d+)/g);
  69. if (!match || !match.length) return null;
  70. var nums = match.map(Number);
  71. return { start: Math.min.apply(null, nums), end: Math.max.apply(null, nums) };
  72. }
  73. function parseCourseCell(cellStr, day, sections) {
  74. if (!cellStr || typeof cellStr !== "string") return [];
  75. var courses = [];
  76. var text = cellStr.replace(/\\r\\n/g, "\r\n");
  77. var blocks = text.split(/\r?\n\r?\n/).filter(function(b) { return b.trim(); });
  78. for (var i = 0; i < blocks.length; i++) {
  79. var lines = blocks[i].split(/\r?\n/).map(function(l) { return l.trim(); }).filter(function(l) { return l; });
  80. if (lines.length < 2) continue;
  81. var name = lines[0];
  82. var teacherLine = lines[1] || "";
  83. var defaultTeacher = teacherLine.split(/\s+/)[0] || "未知";
  84. var weekText = "", position = "";
  85. for (var j = 0; j < lines.length; j++) {
  86. var wm = lines[j].match(/^周次[::]\s*(.+)/);
  87. if (wm) weekText = wm[1].trim();
  88. var pm = lines[j].match(/^地点[::]\s*(.+)/);
  89. if (pm) position = pm[1].trim();
  90. }
  91. if (!weekText) continue;
  92. var secInfo = null;
  93. for (var k = 0; k < lines.length; k++) {
  94. var sm = lines[k].match(/^节次[::]\s*(.+)/);
  95. if (sm) { secInfo = parseSections(sm[1]); break; }
  96. }
  97. var startSection = secInfo ? secInfo.start : sections.start;
  98. var endSection = secInfo ? secInfo.end : sections.end;
  99. // 一班多师: "1-5(张翼飞);7-9(张翼飞);10-17(郝龙)"
  100. var multiTeacher = parseMultiTeacherWeeks(weekText);
  101. if (multiTeacher.length > 0) {
  102. for (var m = 0; m < multiTeacher.length; m++) {
  103. courses.push({
  104. name: name,
  105. teacher: multiTeacher[m].teacher,
  106. position: position || "待定",
  107. day: day,
  108. startSection: startSection,
  109. endSection: endSection,
  110. weeks: multiTeacher[m].weeks
  111. });
  112. }
  113. } else {
  114. var weeks = parseWeeks(weekText);
  115. if (!weeks.length) continue;
  116. courses.push({
  117. name: name,
  118. teacher: defaultTeacher,
  119. position: position || "待定",
  120. day: day,
  121. startSection: startSection,
  122. endSection: endSection,
  123. weeks: weeks
  124. });
  125. }
  126. }
  127. return courses;
  128. }
  129. function parseScheduleData(data) {
  130. var allCourses = [];
  131. if (!Array.isArray(data)) return allCourses;
  132. for (var i = 0; i < data.length; i++) {
  133. var row = data[i];
  134. var sections = parseSections(row.JieCi);
  135. if (!sections) continue;
  136. for (var j = 0; j < DAY_FIELDS.length; j++) {
  137. var cellStr = row[DAY_FIELDS[j].field];
  138. if (!cellStr) continue;
  139. var courses = parseCourseCell(cellStr, DAY_FIELDS[j].day, sections);
  140. allCourses.push.apply(allCourses, courses);
  141. }
  142. }
  143. return allCourses;
  144. }
  145. function dedup(courses) {
  146. var index = {};
  147. var result = [];
  148. for (var i = 0; i < courses.length; i++) {
  149. var c = courses[i];
  150. var key = c.day + "|" + c.startSection + "|" + c.endSection + "|" + c.name + "|" + c.teacher + "|" + c.position;
  151. if (index[key] === undefined) {
  152. index[key] = result.length;
  153. result.push(c);
  154. } else {
  155. var ex = result[index[key]];
  156. c.weeks.forEach(function(w) { if (ex.weeks.indexOf(w) === -1) ex.weeks.push(w); });
  157. }
  158. }
  159. result.forEach(function(c) { c.weeks.sort(function(a, b) { return a - b; }); });
  160. return result;
  161. }
  162. async function fetchSchedule() {
  163. // 方案1: 拦截已有请求
  164. var url = findTimetableRequest();
  165. if (url) {
  166. console.log("HEU研: 拦截到课表请求 " + url);
  167. var resp = await fetch(url, {
  168. method: "GET",
  169. credentials: "include",
  170. headers: { "Accept": "application/json, text/javascript, */*; q=0.01", "X-Requested-With": "XMLHttpRequest" }
  171. });
  172. if (resp.ok) return await resp.json();
  173. }
  174. throw new Error("未找到课表请求,请先在页面上查询课表后再点击导入");
  175. }
  176. async function runImportFlow() {
  177. var confirmed = await window.shiguangBridgePromise.showAlert(
  178. "哈工程研究生课表导入",
  179. "请确保已登录研究生系统,并已打开课表查询页面,等待课表加载完成后点击确定。",
  180. "确定,开始导入"
  181. );
  182. if (!confirmed) {
  183. window.shiguangBridge.showToast("已取消导入");
  184. return;
  185. }
  186. window.shiguangBridge.showToast("正在获取课表数据...");
  187. var data = await fetchSchedule();
  188. console.log("HEU研: 获取到 " + data.length + " 行数据");
  189. var courses = dedup(parseScheduleData(data));
  190. console.log("HEU研: 解析出 " + courses.length + " 门课程");
  191. if (courses.length === 0) {
  192. await window.shiguangBridgePromise.showAlert("导入提示", "未解析到课程数据,请确认课表页面已加载完成。", "知道了");
  193. return;
  194. }
  195. await window.shiguangBridgePromise.savePresetTimeSlots(JSON.stringify(HEU_YJS_TIME_SLOTS));
  196. await window.shiguangBridgePromise.saveCourseConfig(JSON.stringify({
  197. semesterStartDate: null,
  198. semesterTotalWeeks: 20,
  199. defaultClassDuration: 45,
  200. defaultBreakDuration: 5,
  201. firstDayOfWeek: 1
  202. }));
  203. await window.shiguangBridgePromise.saveImportedCourses(JSON.stringify(courses));
  204. window.shiguangBridge.showToast("研究生课表导入成功,共 " + courses.length + " 门课程");
  205. window.shiguangBridge.notifyTaskCompletion();
  206. }
  207. (async function() {
  208. try {
  209. await runImportFlow();
  210. } catch (error) {
  211. console.error("HEU研究生课表导入失败:", error);
  212. if (window.shiguangBridgePromise) {
  213. await window.shiguangBridgePromise.showAlert(
  214. "导入失败",
  215. "错误: " + error.message + "\n\n请确认:\n1. 已登录研究生系统\n2. 已打开课表查询页面并加载完成\n3. 处于校园网或VPN环境",
  216. "确定"
  217. );
  218. }
  219. }
  220. })();