sdipct.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /**
  2. * 拾光课程表适配脚本 - 山东石油化工学院 (sdipct.edu.cn)
  3. * 非该大学开发者适配,开发者无法及时发现问题
  4. * 出现问题请提联系开发者或者提交pr更改,这更加快速
  5. */
  6. /**
  7. * 解析周次字符串,例如 "1,2,3,4,5" -> [1, 2, 3, 4, 5]
  8. */
  9. function parseWeeks(weekStr) {
  10. if (!weekStr) return [];
  11. const weeks = weekStr.split(',')
  12. .map(w => Number(w.trim()))
  13. .filter(w => !isNaN(w) && w > 0);
  14. return [...new Set(weeks)].sort((a, b) => a - b);
  15. }
  16. /**
  17. * 将教务系统返回的 JSON 转换为拾光标准的 CourseJsonModel 数组
  18. */
  19. function parseJsonData(jsonData) {
  20. console.log("JS: 开始解析课程 JSON...");
  21. if (!jsonData || jsonData.code !== 0 || !Array.isArray(jsonData.data)) {
  22. return [];
  23. }
  24. let allProcessedCourses = [];
  25. jsonData.data.forEach(raw => {
  26. // 基础字段校验
  27. if (!(raw.kcmc && raw.xq && raw.ps && raw.pe)) return;
  28. // 识别并解析 jxcdmc2
  29. const locationMap = new Map();
  30. if (raw.jxcdmc2) {
  31. const parts = raw.jxcdmc2.split(',');
  32. parts.forEach(part => {
  33. const lastDashIndex = part.lastIndexOf('-');
  34. if (lastDashIndex !== -1) {
  35. const room = part.substring(0, lastDashIndex).trim();
  36. const week = Number(part.substring(lastDashIndex + 1));
  37. if (!isNaN(week)) {
  38. if (!locationMap.has(room)) locationMap.set(room, []);
  39. locationMap.get(room).push(week);
  40. }
  41. }
  42. });
  43. }
  44. if (locationMap.size > 0) {
  45. // 如果解析成功,则根据地点拆分为多个课程对象
  46. locationMap.forEach((weeks, room) => {
  47. allProcessedCourses.push({
  48. name: raw.kcmc.trim(),
  49. teacher: (raw.teaxms || "未知教师").trim(),
  50. position: room || "未知地点",
  51. day: Number(raw.xq),
  52. startSection: Number(raw.ps),
  53. endSection: Number(raw.pe),
  54. weeks: weeks.sort((a, b) => a - b) // 必须升序排列
  55. });
  56. });
  57. } else if (raw.zc) {
  58. // 兜底方案:如果 jxcdmc2 无效,使用原有的 jxcdmc 和 zc 逻辑
  59. allProcessedCourses.push({
  60. name: raw.kcmc.trim(),
  61. teacher: (raw.teaxms || "未知教师").trim(),
  62. position: (raw.jxcdmc || "未知地点").trim(),
  63. day: Number(raw.xq),
  64. startSection: Number(raw.ps),
  65. endSection: Number(raw.pe),
  66. weeks: parseWeeks(raw.zc)
  67. });
  68. }
  69. });
  70. return allProcessedCourses.filter(course => {
  71. return course.weeks.length > 0 && course.startSection <= course.endSection;
  72. });
  73. }
  74. /**
  75. * 校验函数:验证用户输入的学年格式
  76. */
  77. function validateYearInput(input) {
  78. return /^[0-9]{4}$/.test(input) ? false : "请输入四位数字的起始学年(如 2025)";
  79. }
  80. /**
  81. * 步骤 A: 显示引导公告
  82. */
  83. async function promptUserToStart() {
  84. return await window.AndroidBridgePromise.showAlert(
  85. "教务导入说明",
  86. "1. 请确保已在浏览器中成功登录教务系统\n2. 导入过程中请勿关闭页面",
  87. "好的,开始导入"
  88. );
  89. }
  90. /**
  91. * 步骤 B: 获取用户输入的学年
  92. */
  93. async function getAcademicYear() {
  94. const currentYear = new Date().getFullYear().toString();
  95. return await window.AndroidBridgePromise.showPrompt(
  96. "选择学年",
  97. "请输入要导入的起始学年(例如 2025-2026 应输入2025):",
  98. currentYear,
  99. "validateYearInput"
  100. );
  101. }
  102. /**
  103. * 步骤 C: 选择学期
  104. */
  105. async function selectSemester() {
  106. const semesters = ["第一学期 (秋季)", "第二学期 (春季)"];
  107. return await window.AndroidBridgePromise.showSingleSelection(
  108. "选择学期",
  109. JSON.stringify(semesters),
  110. 0
  111. );
  112. }
  113. /**
  114. * 发起网络请求并获取数据
  115. */
  116. async function fetchCourses(academicYear, semesterIndex) {
  117. AndroidBridge.showToast("正在请求教务数据...");
  118. const semesterCode = semesterIndex === 0 ? "01" : "02";
  119. const body = `xnxqdm=${academicYear}${semesterCode}`;
  120. const url = "https://webvpn.sdipct.edu.cn/https/77726476706e69737468656265737421fae0598869236c596e0b9de29d51367bd943/new/student/xsgrkb/getCalendarWeekDatas?vpn-12-o2-jwxt.sdipct.edu.cn";
  121. try {
  122. const response = await fetch(url, {
  123. method: "POST",
  124. headers: { "content-type": "application/x-www-form-urlencoded; charset=UTF-8" },
  125. body: body,
  126. credentials: "include"
  127. });
  128. if (!response.ok) throw new Error(`HTTP Error: ${response.status}`);
  129. const jsonData = await response.json();
  130. return parseJsonData(jsonData);
  131. } catch (e) {
  132. console.error("Fetch Error:", e);
  133. AndroidBridge.showToast("网络请求失败,请检查登录状态");
  134. return null;
  135. }
  136. }
  137. /**
  138. * 预设时间段数据
  139. */
  140. const StandardTimeSlots = [
  141. { number: 1, startTime: "08:00", endTime: "08:50" },
  142. { number: 2, startTime: "09:00", endTime: "09:50" },
  143. { number: 3, startTime: "10:10", endTime: "11:00" },
  144. { number: 4, startTime: "11:10", endTime: "12:00" },
  145. { number: 5, startTime: "14:00", endTime: "14:50" },
  146. { number: 6, startTime: "15:00", endTime: "15:50" },
  147. { number: 7, startTime: "16:10", endTime: "17:00" },
  148. { number: 8, startTime: "17:10", endTime: "18:00" },
  149. { number: 9, startTime: "19:00", endTime: "19:50" },
  150. { number: 10, startTime: "20:00", endTime: "20:50" }
  151. ];
  152. // 主流程编排
  153. async function runImportFlow() {
  154. // 1. 前置检查
  155. const isReady = await promptUserToStart();
  156. if (!isReady) return; // 用户点击取消,静默退出
  157. // 2. 获取参数(学年、学期)
  158. const year = await getAcademicYear();
  159. if (!year) {
  160. AndroidBridge.showToast("导入已取消");
  161. return;
  162. }
  163. const semesterIdx = await selectSemester();
  164. if (semesterIdx === null) {
  165. AndroidBridge.showToast("导入已取消");
  166. return;
  167. }
  168. // 3. 执行获取与解析
  169. const courses = await fetchCourses(year, semesterIdx);
  170. if (!courses || courses.length === 0) {
  171. if (courses && courses.length === 0) AndroidBridge.showToast("该学期暂无课程数据");
  172. return;
  173. }
  174. // 4. 数据保存
  175. try {
  176. // 保存课程
  177. await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(courses));
  178. // 保存预设时间表
  179. await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(StandardTimeSlots));
  180. AndroidBridge.showToast(`成功导入 ${courses.length} 门课程!`);
  181. // 5. 流程收尾:通知原生端任务完成
  182. AndroidBridge.notifyTaskCompletion();
  183. console.log("JS: 流程成功完成");
  184. } catch (e) {
  185. AndroidBridge.showToast("保存失败: " + e.message);
  186. }
  187. }
  188. // 启动入口
  189. runImportFlow();