cupk_01.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. // resources/CUPK/CUPK_01.js
  2. // 中国石油大学(北京)克拉玛依校区拾光课程表适配脚本
  3. // 由larryyan适配的中国石油大学(北京)本科生拾光课程表适配脚本(CUP_01.js)修改而来
  4. // 由于克拉玛依校区本科生自2026春季学期起更换为本部同一套教务系统,本克砖鼠鼠遂充当CV工程师完成适配(拿来把你)
  5. // 在此感谢前人的智慧!
  6. // 1. 显示一个公告信息弹窗
  7. async function promptUserToStart() {
  8. try {
  9. console.log("即将显示公告弹窗...");
  10. const confirmed = await window.AndroidBridgePromise.showAlert(
  11. "重要通知",
  12. "导入前请确保您已成功登录教务系统,并选定正确的学期。",
  13. "好的,开始"
  14. );
  15. if (confirmed) {
  16. console.log("用户点击了确认按钮。Alert Promise Resolved: " + confirmed);
  17. AndroidBridge.showToast("Alert:用户点击了确认!");
  18. return true; // 成功时返回 true
  19. } else {
  20. console.log("用户点击了取消按钮或关闭了弹窗。Alert Promise Resolved: " + confirmed);
  21. AndroidBridge.showToast("Alert:用户取消了!");
  22. return false; // 用户取消时返回 false
  23. }
  24. } catch (error) {
  25. console.error("显示公告弹窗时发生错误:", error);
  26. AndroidBridge.showToast("Alert:显示弹窗出错!" + error.message);
  27. return false; // 出现错误时也返回 false
  28. }
  29. }
  30. // 2. 获取学期信息
  31. async function getSemesterIndex() {
  32. try {
  33. const response = await fetch(`https://eams.cupk.edu.cn/student/for-std/course-table`)
  34. const htmlString = await response.text();
  35. const parser = new DOMParser();
  36. const dom = parser.parseFromString(htmlString, 'text/html');
  37. const selectElement = dom.getElementById('semesters') || dom.getElementById('allSemesters');
  38. if (!selectElement) {
  39. throw new Error("页面中未找到学期选择框");
  40. }
  41. // 1. 将所有 option 转换为数组
  42. const options = Array.from(selectElement.options);
  43. // 2. 过滤掉 "全部学期" (value="all"),因为导入课表通常只能导具体的某一学期
  44. const validOptions = options.filter(opt => opt.value !== "all");
  45. if (validOptions.length === 0) {
  46. throw new Error("未解析到有效的学期列表");
  47. }
  48. // 3. 提取用于展示的文本数组和用于请求的 value 数组
  49. const semesterTexts = validOptions.map(opt => opt.text); // 例: ["2025-2026-2", "2025-2026-1", ...]
  50. const semesterValues = validOptions.map(opt => opt.value); // 例: ["191", "171", ...]
  51. // 4. 调用安卓原生弹窗,让用户选择
  52. const selectedIndex = await window.AndroidBridgePromise.showSingleSelection(
  53. "选择学期",
  54. JSON.stringify(semesterTexts), // 必须是 JSON 字符串
  55. 0 // 默认选中第一个(通常是最新学期)
  56. );
  57. // 5. 判断用户的选择结果
  58. if (selectedIndex !== null && selectedIndex >= 0) {
  59. // 根据选中的索引,获取对应的学期 ID (value)
  60. const selectedValue = semesterValues[selectedIndex];
  61. if (typeof AndroidBridge !== 'undefined' && AndroidBridge.showToast) {
  62. AndroidBridge.showToast("已选择学期: " + semesterTexts[selectedIndex]);
  63. }
  64. return selectedValue; // 成功时返回学期编号 (例如 "191")
  65. } else {
  66. // 用户取消了选择
  67. console.log("用户取消了学期选择");
  68. return null;
  69. }
  70. } catch (error) {
  71. console.error("获取学期信息时发生错误:", error);
  72. AndroidBridge.showToast("Alert:获取学期信息出错!" + error.message);
  73. return null; // 出现错误时返回 null
  74. }
  75. }
  76. // 3. 获取课程数据
  77. async function fetchPrintData(semesterIndex) {
  78. try {
  79. const responds = await fetch(`https://eams.cupk.edu.cn/student/for-std/course-table/semester/${semesterIndex}/print-data`);
  80. if (!responds.ok) {
  81. throw new Error(`网络请求失败,状态码: ${responds.status}`);
  82. }
  83. const printData = await responds.json();
  84. return printData;
  85. } catch (error) {
  86. console.error("获取数据时发生错误:", error);
  87. AndroidBridge.showToast("Alert:获取数据出错!" + error.message);
  88. return null;
  89. }
  90. }
  91. // 4. 导入课程数据
  92. async function parseCourses(printData) {
  93. console.log("正在导入课程数据...");
  94. const activities = printData.studentTableVms[0].activities;
  95. const parsedCourses = activities.map(activity => {
  96. // 返回拾光要求的标准结构
  97. return {
  98. name: activity.courseName, // 课程名称
  99. teacher: activity.teachers ? activity.teachers.join(" ") : "", // 授课教师
  100. position: activity.campus ? `${activity.campus} ${activity.room}` : (activity.room || "未知地点"),
  101. // 上课地点
  102. day: activity.weekday, // 星期几 (1-7)
  103. startSection: activity.startUnit, // 开始节次
  104. endSection: activity.endUnit, // 结束节次
  105. weeks: activity.weekIndexes // 上课周次数组
  106. };
  107. });
  108. try {
  109. console.log("正在尝试导入课程...");
  110. const result = await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(parsedCourses));
  111. if (result === true) {
  112. console.log("课程导入成功!");
  113. AndroidBridge.showToast("测试课程导入成功!");
  114. } else {
  115. console.log("课程导入未成功,结果:" + result);
  116. AndroidBridge.showToast("测试课程导入失败,请查看日志。");
  117. }
  118. } catch (error) {
  119. console.error("导入课程时发生错误:", error);
  120. AndroidBridge.showToast("导入课程失败: " + error.message);
  121. }
  122. }
  123. // 5. 导入预设时间段
  124. async function importPresetTimeSlots(printData) {
  125. console.log("正在准备预设时间段数据...");
  126. function formatTime(timeInt) {
  127. // 将数字转为字符串,并在前面补0直到长度为4
  128. const timeStr = timeInt.toString().padStart(4, '0');
  129. // 截取前两位作为小时,后两位作为分钟,中间加冒号
  130. return `${timeStr.slice(0, 2)}:${timeStr.slice(2, 4)}`;
  131. }
  132. const courseUnitList = printData.studentTableVms[0].timeTableLayout.courseUnitList;
  133. const presetTimeSlots = courseUnitList.map(unit => {
  134. return {
  135. number: unit.indexNo, // 节次编号
  136. startTime: formatTime(unit.startTime), // 开始时间
  137. endTime: formatTime(unit.endTime) // 结束时间
  138. };
  139. });
  140. try {
  141. console.log("正在尝试导入预设时间段...");
  142. const result = await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(presetTimeSlots));
  143. if (result === true) {
  144. console.log("预设时间段导入成功!");
  145. window.AndroidBridge.showToast("测试时间段导入成功!");
  146. } else {
  147. console.log("预设时间段导入未成功,结果:" + result);
  148. window.AndroidBridge.showToast("测试时间段导入失败,请查看日志。");
  149. }
  150. return result; // 返回导入结果,供流程控制使用
  151. } catch (error) {
  152. console.error("导入时间段时发生错误:", error);
  153. window.AndroidBridge.showToast("导入时间段失败: " + error.message);
  154. }
  155. }
  156. // 6. 导入课表配置
  157. async function saveConfig(semesterIndex) {
  158. console.log("正在准备配置数据...");
  159. const responds = await fetch(`https://eams.cupk.edu.cn/student/ws/semester/get/${semesterIndex}`);
  160. if (!responds.ok) {
  161. throw new Error(`网络请求失败,状态码: ${responds.status}`);
  162. }
  163. const semesterInfo = await responds.json();
  164. const startDate = new Date(semesterInfo.startDate);
  165. const endDate = new Date(semesterInfo.endDate);
  166. const diffDays = Math.ceil(Math.abs(endDate - startDate) / (1000 * 60 * 60 * 24));
  167. const calculatedWeeks = Math.ceil(diffDays / 7);
  168. // 注意:只传入要修改的字段,其他字段(如 semesterTotalWeeks)会使用 Kotlin 模型中的默认值
  169. const courseConfigData = {
  170. "semesterStartDate": semesterInfo.startDate,
  171. "semesterTotalWeeks": calculatedWeeks,
  172. "defaultClassDuration": 45,
  173. "defaultBreakDuration": 5,
  174. "firstDayOfWeek": semesterInfo.weekStartOnSunday ? 7 : 1
  175. };
  176. try {
  177. console.log("正在尝试导入课表配置...");
  178. const configJsonString = JSON.stringify(courseConfigData);
  179. const result = await window.AndroidBridgePromise.saveCourseConfig(configJsonString);
  180. if (result === true) {
  181. console.log("课表配置导入成功!");
  182. AndroidBridge.showToast("测试配置导入成功!开学日期: " + startDate.toISOString().split('T')[0]);
  183. } else {
  184. console.log("课表配置导入未成功,结果:" + result);
  185. AndroidBridge.showToast("测试配置导入失败,请查看日志。");
  186. }
  187. return result; // 返回导入结果,供流程控制使用
  188. } catch (error) {
  189. console.error("导入配置时发生错误:", error);
  190. AndroidBridge.showToast("导入配置失败: " + error.message);
  191. }
  192. }
  193. /**
  194. * 编排整个课程导入流程。
  195. * 在任何一步用户取消或发生错误时,都会立即退出,AndroidBridge.notifyTaskCompletion()应该只在成功后调用
  196. */
  197. async function runImportFlow() {
  198. AndroidBridge.showToast("课程导入流程即将开始...");
  199. // 1. 公告和前置检查。
  200. const alertConfirmed = await promptUserToStart();
  201. if (!alertConfirmed) {
  202. return; // 用户取消,立即退出函数
  203. }
  204. // 2. 获取学期。
  205. const semesterIndex = await getSemesterIndex();
  206. if (semesterIndex === null) {
  207. AndroidBridge.showToast("导入已取消。");
  208. // 用户取消,直接退出
  209. return;
  210. }
  211. // 3. 获取课程数据
  212. const printData = await fetchPrintData(semesterIndex);
  213. if (printData === null) {
  214. AndroidBridge.showToast("导入已取消。");
  215. // 请求失败或无数据,直接退出
  216. return;
  217. }
  218. // 4. 解析课程信息。
  219. const courses = await parseCourses(printData);
  220. if (courses === null) {
  221. // 请求失败或无数据,直接退出
  222. return;
  223. }
  224. // 5. 导入时间段数据。
  225. const timeSlotImportResult = await importPresetTimeSlots(printData);
  226. if (!timeSlotImportResult) {
  227. // 时间段导入失败,直接退出
  228. return;
  229. }
  230. // 6. 保存配置数据 (例如学期开始日期)
  231. const configSaveResult = await saveConfig(semesterIndex);
  232. if (!configSaveResult) {
  233. // 保存配置失败,直接退出
  234. return;
  235. }
  236. // 7. 流程**完全成功**,发送结束信号。
  237. AndroidBridge.showToast("所有任务已完成!");
  238. AndroidBridge.notifyTaskCompletion();
  239. }
  240. // 启动所有演示
  241. runImportFlow();