cuit_bk_new.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. // == 成都信息工程大学(CUIT)课表适配脚本(Fetch API)==
  2. // 适用页面:https://sjjx.cuit.edu.cn:56443/labms/#/course/my
  3. // 适配实际 API 返回的扁平数组结构
  4. (async function () {
  5. "use strict";
  6. function showToast(msg) {
  7. if (typeof AndroidBridge !== "undefined" && AndroidBridge.showToast) {
  8. AndroidBridge.showToast(msg);
  9. } else {
  10. console.log("[Toast]", msg);
  11. }
  12. }
  13. async function showAlert(title, content, confirmText = "确定") {
  14. if (typeof window.AndroidBridgePromise !== "undefined") {
  15. return await window.AndroidBridgePromise.showAlert(
  16. title,
  17. content,
  18. confirmText,
  19. );
  20. } else {
  21. alert(`${title}\n${content}`);
  22. return true;
  23. }
  24. }
  25. // ---------- 从页面提取用户信息 ----------
  26. function getUserInfoFromPage() {
  27. try {
  28. const initialState = window.__INITIAL_STATE__ || window.g_initialState;
  29. if (initialState?.info?.userCode) {
  30. return {
  31. status: 200,
  32. data: {
  33. userCode: initialState.info.userCode,
  34. nickName: initialState.info.nickName || "",
  35. },
  36. };
  37. }
  38. const usernameSpan = document.querySelector(".username___LBEmQ");
  39. if (usernameSpan) {
  40. const text = usernameSpan.textContent.trim();
  41. const idMatch = text.match(/^\d+/);
  42. if (idMatch) {
  43. return {
  44. status: 200,
  45. data: { userCode: idMatch[0], nickName: text },
  46. };
  47. }
  48. }
  49. return null;
  50. } catch (e) {
  51. console.warn("提取页面用户信息失败", e);
  52. return null;
  53. }
  54. }
  55. async function fetchUserInfo() {
  56. const pageInfo = getUserInfoFromPage();
  57. if (pageInfo) {
  58. console.log("从页面全局变量获取用户信息成功");
  59. return pageInfo;
  60. }
  61. const baseUrl = window.location.origin;
  62. const url = `${baseUrl}/labms/user/info?sf_request_type=ajax`;
  63. const resp = await fetch(url, {
  64. method: "GET",
  65. headers: { "X-Requested-With": "XMLHttpRequest" },
  66. credentials: "include",
  67. });
  68. if (!resp.ok) throw new Error(`获取用户信息失败: ${resp.status}`);
  69. const data = await resp.json();
  70. if (data.status !== 200)
  71. throw new Error(data.message || "获取用户信息失败");
  72. return data;
  73. }
  74. // ---------- 获取当前学期 ----------
  75. function getCurrentSemester() {
  76. const selectItem = document.querySelector(
  77. ".ant-select-selection-item[title]",
  78. );
  79. if (selectItem) {
  80. const title = selectItem.getAttribute("title");
  81. if (title?.includes("学年")) return title;
  82. }
  83. try {
  84. const state = window.__INITIAL_STATE__ || window.g_initialState;
  85. if (state?.semester?.current?.name) return state.semester.current.name;
  86. } catch (e) {}
  87. return "2025-2026学年第二学期";
  88. }
  89. // ---------- 请求课表数据 ----------
  90. async function fetchCourseSchedule(studentId, semester) {
  91. const baseUrl = window.location.origin;
  92. const url = `${baseUrl}/labms/course/schedule/list/type?sf_request_type=ajax`;
  93. const requestBody = {
  94. studentIds: [studentId],
  95. labIds: [],
  96. classIds: [],
  97. teacherIds: [studentId],
  98. status: 2,
  99. semester: semester,
  100. week: null,
  101. showMode: "table",
  102. toBeDeleted: 0,
  103. };
  104. const resp = await fetch(url, {
  105. method: "POST",
  106. headers: {
  107. "Content-Type": "application/json",
  108. "X-Requested-With": "XMLHttpRequest",
  109. },
  110. credentials: "include",
  111. body: JSON.stringify(requestBody),
  112. });
  113. if (!resp.ok) throw new Error(`课表接口请求失败: ${resp.status}`);
  114. const data = await resp.json();
  115. if (data.status !== 200) throw new Error(data.message || "获取课表失败");
  116. return data.data; // 直接返回数组
  117. }
  118. // ---------- 解析课表数据(扁平数组结构)----------
  119. function convertApiDataToCourses(apiData) {
  120. const courses = [];
  121. if (!Array.isArray(apiData)) return courses;
  122. for (const item of apiData) {
  123. // 必须的字段校验
  124. if (!item.courseName || !item.weeks || item.weeks.length === 0) continue;
  125. const startSection =
  126. item.sections && item.sections.length > 0 ? item.sections[0] : 1;
  127. const endSection =
  128. item.sections && item.sections.length > 0
  129. ? item.sections[item.sections.length - 1]
  130. : 1;
  131. // 解析时间(去除秒)
  132. let startTime = item.startTime ? item.startTime.substring(0, 5) : "";
  133. let endTime = item.endTime ? item.endTime.substring(0, 5) : "";
  134. courses.push({
  135. name: item.courseName,
  136. teacher: item.teacherName || "",
  137. position: item.location || "",
  138. day: item.weekDay, // 1=周一 ... 7=周日
  139. startSection: startSection,
  140. endSection: endSection,
  141. weeks: item.weeks, // 数字数组,例如 [2,3,4,...]
  142. isCustomTime: !!(startTime && endTime),
  143. customStartTime: startTime,
  144. customEndTime: endTime,
  145. });
  146. }
  147. return courses;
  148. }
  149. // ---------- 导入预设时间段 ----------
  150. async function importTimeSlots() {
  151. const timeSlots = [
  152. { number: 1, startTime: "08:20", endTime: "09:05" },
  153. { number: 2, startTime: "09:15", endTime: "10:00" },
  154. { number: 3, startTime: "10:20", endTime: "11:05" },
  155. { number: 4, startTime: "11:15", endTime: "12:00" },
  156. { number: 5, startTime: "14:00", endTime: "14:45" },
  157. { number: 6, startTime: "14:55", endTime: "15:40" },
  158. { number: 7, startTime: "15:50", endTime: "16:35" },
  159. { number: 8, startTime: "16:45", endTime: "17:30" },
  160. { number: 9, startTime: "17:40", endTime: "18:25" },
  161. { number: 10, startTime: "19:30", endTime: "20:15" },
  162. { number: 11, startTime: "20:25", endTime: "21:10" },
  163. { number: 12, startTime: "21:20", endTime: "22:05" },
  164. ];
  165. await window.AndroidBridgePromise.savePresetTimeSlots(
  166. JSON.stringify(timeSlots),
  167. );
  168. }
  169. // ---------- 导入学期配置 ----------
  170. async function importConfig(semester) {
  171. let startDate = "2026-02-23";
  172. if (semester.includes("2025-2026") && semester.includes("第一学期")) {
  173. startDate = "2025-09-01";
  174. }
  175. const config = {
  176. semesterStartDate: startDate,
  177. semesterTotalWeeks: 20,
  178. defaultClassDuration: 45,
  179. defaultBreakDuration: 10,
  180. firstDayOfWeek: 1,
  181. };
  182. await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify(config));
  183. }
  184. // ---------- 主流程 ----------
  185. async function runImportFlow() {
  186. try {
  187. showToast("正在获取用户信息...");
  188. const userInfo = await fetchUserInfo();
  189. const studentId = userInfo.data.userCode;
  190. const semester = getCurrentSemester();
  191. if (!studentId) throw new Error("无法获取学号");
  192. showToast(`正在获取 ${semester} 课表...`);
  193. const apiData = await fetchCourseSchedule(studentId, semester);
  194. showToast("正在解析课程数据...");
  195. const courses = convertApiDataToCourses(apiData);
  196. if (courses.length === 0) throw new Error("未解析到任何课程");
  197. showToast(`解析到 ${courses.length} 门课程,正在保存...`);
  198. await window.AndroidBridgePromise.saveImportedCourses(
  199. JSON.stringify(courses),
  200. );
  201. await importTimeSlots();
  202. await importConfig(semester);
  203. showToast(`导入完成!共 ${courses.length} 门课程`);
  204. if (
  205. typeof AndroidBridge !== "undefined" &&
  206. AndroidBridge.notifyTaskCompletion
  207. ) {
  208. AndroidBridge.notifyTaskCompletion();
  209. }
  210. } catch (error) {
  211. console.error(error);
  212. showToast(`导入失败: ${error.message}`);
  213. await showAlert("导入失败", error.message);
  214. }
  215. }
  216. setTimeout(runImportFlow, 800);
  217. })();