urp_01.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // 通用 URP 教务拾光课程表适配脚本
  2. /**
  3. * 验证学年输入(4位数字)
  4. */
  5. function validateYear(year) {
  6. if (!year || year.trim().length === 0) return "学年不能为空!";
  7. const yearRegex = /^\d{4}$/;
  8. if (!yearRegex.test(year)) return "请输入正确的4位数字学年(例如:2025)";
  9. return false;
  10. }
  11. /**
  12. * 解析位图格式的周次 (011100...)
  13. */
  14. function parseWeekString(weekStr) {
  15. let weeks = [];
  16. if (!weekStr) return weeks;
  17. for (let i = 0; i < weekStr.length; i++) {
  18. if (weekStr[i] === '1') weeks.push(i + 1);
  19. }
  20. return weeks;
  21. }
  22. /**
  23. * 格式化时间 (0800 -> 08:00)
  24. */
  25. function formatTime(timeStr) {
  26. if (timeStr && timeStr.length === 4) {
  27. return timeStr.substring(0, 2) + ":" + timeStr.substring(2);
  28. }
  29. return timeStr;
  30. }
  31. /**
  32. * 动态获取 API 路径
  33. */
  34. function getApiUrl() {
  35. const baseUrl = window.location.origin;
  36. return `${baseUrl}/student/courseSelect/thisSemesterCurriculum/ajaxStudentSchedule/callback`;
  37. }
  38. async function promptUserToStart() {
  39. return await window.AndroidBridgePromise.showAlert(
  40. "教务系统课表导入",
  41. "请确保您已进入教务系统课表查询页面后再开始导入",
  42. "好的,开始导入"
  43. );
  44. }
  45. /**
  46. * 获取学年
  47. */
  48. async function getAcademicYear() {
  49. return await window.AndroidBridgePromise.showPrompt(
  50. "学年设置",
  51. "请输入要导入课程的起始学年(例如 2025-2026 应输入2025):",
  52. "",
  53. "validateYear"
  54. );
  55. }
  56. /**
  57. * 获取学期
  58. */
  59. async function selectSemester() {
  60. const semesters = ["1(第一学期)", "2(第二学期)"];
  61. return await window.AndroidBridgePromise.showSingleSelection(
  62. "选择学期",
  63. JSON.stringify(semesters),
  64. -1
  65. );
  66. }
  67. /**
  68. * 网络请求和数据解析
  69. */
  70. async function fetchAndParseJwData(academicYear, semesterIndex) {
  71. try {
  72. const semesterValue = parseInt(semesterIndex) + 1;
  73. const endYear = parseInt(academicYear) + 1;
  74. const planCode = `${academicYear}-${endYear}-${semesterValue}-1`;
  75. const apiUrl = getApiUrl();
  76. console.log("正在通过动态地址获取教务数据:", apiUrl);
  77. AndroidBridge.showToast("正在获取教务数据...");
  78. const response = await fetch(apiUrl, {
  79. "headers": { "content-type": "application/x-www-form-urlencoded; charset=UTF-8" },
  80. "body": `&planCode=${planCode}`,
  81. "method": "POST",
  82. "credentials": "include"
  83. });
  84. const data = await response.json();
  85. if (!data) throw new Error("服务器未返回任何数据");
  86. // 严格遵循 dateList 结构解析
  87. if (!data.dateList || !Array.isArray(data.dateList)) {
  88. console.error("教务返回数据异常:", data);
  89. throw new Error("未能获取到课程列表,请确认是否已登录或页面正确");
  90. }
  91. // 解析时间段 (jcsjbs)
  92. const timeSlots = (data.jcsjbs || []).map(item => ({
  93. number: parseInt(item.jc),
  94. startTime: formatTime(item.kssj),
  95. endTime: formatTime(item.jssj)
  96. }));
  97. // 解析课程
  98. let courses = [];
  99. data.dateList.forEach(plan => {
  100. if (plan && plan.selectCourseList && Array.isArray(plan.selectCourseList)) {
  101. plan.selectCourseList.forEach(c => {
  102. const teacher = (c.attendClassTeacher || "").replace(/\* /g, "").trim();
  103. if (c.timeAndPlaceList && Array.isArray(c.timeAndPlaceList)) {
  104. c.timeAndPlaceList.forEach(tp => {
  105. courses.push({
  106. name: c.courseName,
  107. teacher: teacher,
  108. position: (tp.teachingBuildingName || "") + (tp.classroomName || ""),
  109. day: parseInt(tp.classDay),
  110. startSection: parseInt(tp.classSessions),
  111. endSection: parseInt(tp.classSessions) + parseInt(tp.continuingSession) - 1,
  112. weeks: parseWeekString(tp.classWeek),
  113. isCustomTime: false
  114. });
  115. });
  116. }
  117. });
  118. }
  119. });
  120. if (courses.length === 0) {
  121. throw new Error("该学期暂无排课数据");
  122. }
  123. return { courses, timeSlots };
  124. } catch (e) {
  125. console.error("解析失败详情:", e);
  126. AndroidBridge.showToast("同步失败: " + e.message);
  127. return null;
  128. }
  129. }
  130. /**
  131. * 保存数据到应用
  132. */
  133. async function saveToApp(result) {
  134. const courseSuccess = await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(result.courses));
  135. if (!courseSuccess) return false;
  136. if (result.timeSlots && result.timeSlots.length > 0) {
  137. await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(result.timeSlots));
  138. }
  139. await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify({
  140. semesterTotalWeeks: 20
  141. }));
  142. return true;
  143. }
  144. /**
  145. * 流程控制
  146. */
  147. async function runImportFlow() {
  148. const alertResult = await promptUserToStart();
  149. if (!alertResult) return;
  150. const academicYear = await getAcademicYear();
  151. if (academicYear === null) {
  152. AndroidBridge.showToast("导入已取消");
  153. return;
  154. }
  155. const semesterIndex = await selectSemester();
  156. if (semesterIndex === null) {
  157. AndroidBridge.showToast("导入已取消");
  158. return;
  159. }
  160. const result = await fetchAndParseJwData(academicYear, semesterIndex);
  161. if (!result || result.courses.length === 0) return;
  162. if (await saveToApp(result)) {
  163. AndroidBridge.showToast(`成功导入 ${result.courses.length} 个课程时段`);
  164. AndroidBridge.notifyTaskCompletion();
  165. }
  166. }
  167. // 启动
  168. runImportFlow();