tcu_01.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // 天津城建大学(tcu.edu.cn)拾光课程表适配脚本
  2. // 非该大学开发者适配,开发者无法及时发现问题
  3. // 出现问题请提联系开发者或者提交pr更改,这更加快速
  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. // 解析 classWeek 字符串 (支持不定长度)
  12. function parseWeekString(weekStr) {
  13. let weeks = [];
  14. if (!weekStr) return weeks;
  15. for (let i = 0; i < weekStr.length; i++) {
  16. if (weekStr[i] === '1') weeks.push(i + 1);
  17. }
  18. return weeks;
  19. }
  20. // 格式化时间 (0800 -> 08:00)
  21. function formatTime(timeStr) {
  22. if (timeStr && timeStr.length === 4) {
  23. return timeStr.substring(0, 2) + ":" + timeStr.substring(2);
  24. }
  25. return timeStr;
  26. }
  27. async function promptUserToStart() {
  28. return await window.AndroidBridgePromise.showAlert(
  29. "教务系统课表导入",
  30. "导入前请确保您已在浏览器中成功登录教务系统",
  31. "好的,开始导入"
  32. );
  33. }
  34. /**
  35. * 获取学年
  36. */
  37. async function getAcademicYear() {
  38. return await window.AndroidBridgePromise.showPrompt(
  39. "学年设置",
  40. "请输入要导入课程的起始学年(例如 2025-2026 应输入2025):",
  41. "",
  42. "validateYear"
  43. );
  44. }
  45. /**
  46. * 获取学期
  47. */
  48. async function selectSemester() {
  49. const semesters = ["1(第一学期)", "2(第二学期)"];
  50. return await window.AndroidBridgePromise.showSingleSelection(
  51. "选择学期",
  52. JSON.stringify(semesters),
  53. -1
  54. );
  55. }
  56. /**
  57. * 网络请求和数据解析
  58. */
  59. async function fetchAndParseJwData(academicYear, semesterIndex) {
  60. try {
  61. const semesterValue = parseInt(semesterIndex) + 1;
  62. const endYear = parseInt(academicYear) + 1;
  63. const planCode = `${academicYear}-${endYear}-${semesterValue}-1`;
  64. AndroidBridge.showToast("正在获取教务数据...");
  65. const response = await fetch("https://jwxs.tcu.edu.cn/student/courseSelect/thisSemesterCurriculum/ajaxStudentSchedule/callback", {
  66. "headers": { "content-type": "application/x-www-form-urlencoded; charset=UTF-8" },
  67. "body": `&planCode=${planCode}`,
  68. "method": "POST",
  69. "credentials": "include"
  70. });
  71. const data = await response.json();
  72. if (!data) throw new Error("服务器未返回任何数据");
  73. if (!data.dateList || !Array.isArray(data.dateList)) {
  74. console.error("教务返回数据异常:", data);
  75. throw new Error("未能获取到课程列表,请检查是否已登录或该学期是否有课");
  76. }
  77. // 解析时间段
  78. const timeSlots = (data.jcsjbs || []).map(item => ({
  79. number: parseInt(item.jc),
  80. startTime: formatTime(item.kssj),
  81. endTime: formatTime(item.jssj)
  82. }));
  83. // 解析课程
  84. let courses = [];
  85. data.dateList.forEach(plan => {
  86. // 修正:确保 selectCourseList 存在且是数组
  87. if (plan && plan.selectCourseList && Array.isArray(plan.selectCourseList)) {
  88. plan.selectCourseList.forEach(c => {
  89. const teacher = (c.attendClassTeacher || "").replace(/\* /g, "").trim();
  90. if (c.timeAndPlaceList && Array.isArray(c.timeAndPlaceList)) {
  91. c.timeAndPlaceList.forEach(tp => {
  92. courses.push({
  93. name: c.courseName,
  94. teacher: teacher,
  95. position: (tp.teachingBuildingName || "") + (tp.classroomName || ""),
  96. day: tp.classDay,
  97. startSection: tp.classSessions,
  98. endSection: tp.classSessions + tp.continuingSession - 1,
  99. weeks: parseWeekString(tp.classWeek),
  100. isCustomTime: false
  101. });
  102. });
  103. }
  104. });
  105. }
  106. });
  107. if (courses.length === 0) {
  108. throw new Error("该学期暂无排课数据");
  109. }
  110. return { courses, timeSlots };
  111. } catch (e) {
  112. AndroidBridge.showToast("同步失败: " + e.message);
  113. return null;
  114. }
  115. }
  116. /**
  117. * 保存数据到应用
  118. */
  119. async function saveToApp(result) {
  120. const courseSuccess = await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(result.courses));
  121. if (!courseSuccess) return false;
  122. await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(result.timeSlots));
  123. await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify({
  124. semesterTotalWeeks: 20
  125. }));
  126. return true;
  127. }
  128. /**
  129. * 流程控制
  130. */
  131. async function runImportFlow() {
  132. // 公告
  133. const alertResult = await promptUserToStart();
  134. if (!alertResult) return;
  135. // 获取学年
  136. const academicYear = await getAcademicYear();
  137. if (academicYear === null) {
  138. AndroidBridge.showToast("导入已取消");
  139. return;
  140. }
  141. // 获取学期
  142. const semesterIndex = await selectSemester();
  143. if (semesterIndex === null) {
  144. AndroidBridge.showToast("导入已取消");
  145. return;
  146. }
  147. // 请求与解析
  148. const result = await fetchAndParseJwData(academicYear, semesterIndex);
  149. if (!result || result.courses.length === 0) return;
  150. // 保存并结束
  151. if (await saveToApp(result)) {
  152. AndroidBridge.showToast(`成功导入 ${result.courses.length} 个课程时段`);
  153. AndroidBridge.notifyTaskCompletion();
  154. }
  155. }
  156. // 启动
  157. runImportFlow();