neu.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // 文件: neu.js
  2. // 1. 显示校区选择弹窗
  3. async function showCampusSelection() {
  4. const campuses = ["南湖校区", "浑南校区"];
  5. try {
  6. const selectedIndex = await window.AndroidBridgePromise.showSingleSelection(
  7. "选择你所在的校区",
  8. JSON.stringify(campuses),
  9. 2
  10. );
  11. if (selectedIndex !== -1) {
  12. return campuses[selectedIndex]; // 返回用户选择的校区
  13. } else {
  14. return false; // 用户取消时返回 false
  15. }
  16. } catch (error) {
  17. console.error("显示单选列表弹窗时发生错误:", error);
  18. AndroidBridge.showToast("Single Selection:显示列表出错!" + error.message);
  19. return false; // 出现错误时也返回 false
  20. }
  21. }
  22. // 2. 从课表页面中提取课程数据
  23. async function extractCoursesFromPage() {
  24. const iframe = document.querySelector('iframe');
  25. const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
  26. const dayCols = iframeDoc.querySelectorAll('.kbappTimetableDayColumnRoot');
  27. const lessons = [];
  28. dayCols.forEach((dayCol, dayIndex) => {
  29. const timeSlots = dayCol.children;
  30. const day = dayIndex >= 1 ? dayIndex : 7;
  31. let startSection = 0;
  32. let endSection = 0;
  33. for (let slot of timeSlots) {
  34. const flexValue = slot.style.flex;
  35. const nums = parseInt(flexValue.split(' ')[0]);
  36. startSection = endSection + 1;
  37. endSection = startSection + nums - 1;
  38. const courseColumns = slot.querySelectorAll('.kbappTimetableCourseRenderColumn');
  39. courseColumns.forEach(courseColumn => {
  40. const divsWithoutClass = courseColumn.querySelector('div[style*="flex"]:not([class])');
  41. let currentSection = startSection;
  42. if (divsWithoutClass) {
  43. empty_num = parseInt(divsWithoutClass.style.flex.split(' ')[0]);
  44. currentSection += empty_num;
  45. }
  46. const courseItem = courseColumn.querySelector('.kbappTimetableCourseRenderCourseItemContainer');
  47. const column_nums = parseInt(courseItem.style.flex.split(' ')[0]);
  48. const tooltip = courseItem.querySelector('.el-popover');
  49. // 检查tooltip是否存在
  50. if (tooltip) {
  51. // 获取每一行作为一个单独的元素
  52. const infoItems = tooltip.querySelectorAll('[class*="CourseItemInfoPopperInfo"]');
  53. let name, details;
  54. if (infoItems.length > 0) {
  55. infoItems.forEach((item, idx) => {
  56. if (idx === 0) name = item.textContent.trim();
  57. else if (idx === 1) details = parseCourseDetails(item.textContent.trim());
  58. else if (idx === 2) return;
  59. });
  60. }
  61. lessons.push({
  62. name: name || "",
  63. teacher: details?.teacher || "",
  64. position: details?.position || "",
  65. day: day,
  66. startSection: currentSection,
  67. endSection: currentSection + column_nums - 1,
  68. weeks: details?.weeks || []
  69. });
  70. }
  71. });
  72. }
  73. });
  74. return { lessons: lessons };
  75. }
  76. // 2.1 解析课程详情字符串,提取周次、教师和地点信息
  77. function parseCourseDetails(detailStr) {
  78. // 匹配所有周次模式
  79. const weekPattern = /(\d+-\d+周(?:\([单双]\))?|\d+周(?:\([单双]\))?)/g;
  80. const weekMatches = detailStr.match(weekPattern);
  81. let weeks = '';
  82. let remaining = detailStr;
  83. if (weekMatches) {
  84. // 提取所有周次部分
  85. weeks = weekMatches.join(',');
  86. // 从原字符串中移除周次部分
  87. weekMatches.forEach(match => {
  88. remaining = remaining.replace(match, '');
  89. });
  90. }
  91. // 按空格分割剩余部分
  92. const parts = remaining.trim().split(/\s+/).filter(p => p);
  93. let teacher = '';
  94. let position = '';
  95. if (parts.length > 0) {
  96. teacher = parts[0];
  97. if (parts.length > 1) {
  98. position = parts.slice(1).join(' '); // 修正这一行
  99. }
  100. }
  101. // 清理教师名中的多余逗号
  102. teacher = teacher.replace(/^[,,]/, '').replace(/[,,]$/, '');
  103. return {
  104. weeks: parseWeeksString(weeks),
  105. teacher: teacher.trim(),
  106. position: position.trim()
  107. };
  108. }
  109. // 2.2将周次文字提取成数组
  110. function parseWeeksString(weeksStr) {
  111. if (!weeksStr) return [];
  112. const result = [];
  113. const weekParts = weeksStr.split(/[,,]/).map(part => part.trim());
  114. weekParts.forEach(part => {
  115. // 匹配单个数字周
  116. const singleMatch = part.match(/^(\d+)周(?:\(([单双])\))?$/);
  117. if (singleMatch) {
  118. const num = parseInt(singleMatch[1]);
  119. const type = singleMatch[2];
  120. if (!type || (type === '单' && num % 2 === 1) || (type === '双' && num % 2 === 0)) {
  121. result.push(num);
  122. }
  123. return;
  124. }
  125. // 匹配范围周
  126. const rangeMatch = part.match(/^(\d+)-(\d+)周(?:\(([单双])\))?$/);
  127. if (rangeMatch) {
  128. const start = parseInt(rangeMatch[1]);
  129. const end = parseInt(rangeMatch[2]);
  130. const type = rangeMatch[3];
  131. if (!type) {
  132. for (let i = start; i <= end; i++) result.push(i);
  133. } else if (type === '单') {
  134. for (let i = start; i <= end; i++) {
  135. if (i % 2 === 1) result.push(i);
  136. }
  137. } else if (type === '双') {
  138. for (let i = start; i <= end; i++) {
  139. if (i % 2 === 0) result.push(i);
  140. }
  141. }
  142. }
  143. });
  144. return [...new Set(result)].sort((a, b) => a - b);
  145. }
  146. // 3. 导入课程数据
  147. async function SaveCourses(lessons) {
  148. const testCourses = lessons;
  149. try {
  150. const result = await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(testCourses));
  151. } catch (error) {
  152. console.error("导入课程时发生错误:", error);
  153. AndroidBridge.showToast("导入课程失败: " + error.message);
  154. }
  155. }
  156. // 4. 根据校区导入对应的时间段
  157. async function importTimeSlotsByCampus(campus) {
  158. const hunNanTimeSlots = [
  159. { "number": 1, "startTime": "08:30", "endTime": "09:15" },
  160. { "number": 2, "startTime": "09:25", "endTime": "10:10" },
  161. { "number": 3, "startTime": "10:30", "endTime": "11:15" },
  162. { "number": 4, "startTime": "11:25", "endTime": "12:10" },
  163. { "number": 5, "startTime": "14:00", "endTime": "14:45" },
  164. { "number": 6, "startTime": "14:55", "endTime": "15:40" },
  165. { "number": 7, "startTime": "16:00", "endTime": "16:45" },
  166. { "number": 8, "startTime": "16:55", "endTime": "17:40" },
  167. { "number": 9, "startTime": "18:30", "endTime": "19:15" },
  168. { "number": 10, "startTime": "19:25", "endTime": "20:10" },
  169. { "number": 11, "startTime": "20:30", "endTime": "21:15" },
  170. { "number": 12, "startTime": "21:15", "endTime": "22:10" },
  171. ];
  172. const nanHuTimeSlots = [
  173. { "number": 1, "startTime": "08:00", "endTime": "08:45" },
  174. { "number": 2, "startTime": "08:55", "endTime": "09:40" },
  175. { "number": 3, "startTime": "10:00", "endTime": "10:45" },
  176. { "number": 4, "startTime": "10:55", "endTime": "11:40" },
  177. { "number": 5, "startTime": "14:00", "endTime": "14:45" },
  178. { "number": 6, "startTime": "14:55", "endTime": "15:40" },
  179. { "number": 7, "startTime": "16:00", "endTime": "16:45" },
  180. { "number": 8, "startTime": "16:55", "endTime": "17:40" },
  181. { "number": 9, "startTime": "18:30", "endTime": "19:15" },
  182. { "number": 10, "startTime": "19:25", "endTime": "20:10" },
  183. { "number": 11, "startTime": "20:20", "endTime": "21:05" },
  184. { "number": 12, "startTime": "21:15", "endTime": "22:00" },
  185. ];
  186. // 根据校区选择对应的时间表
  187. const timeSlotsToImport = (campus === "南湖校区") ? nanHuTimeSlots : hunNanTimeSlots;
  188. try {
  189. const result = await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(timeSlotsToImport));
  190. } catch (error) {
  191. console.error("导入时间段时发生错误:", error);
  192. window.AndroidBridge.showToast("导入时间段失败: " + error.message);
  193. }
  194. }
  195. // 5. 导入课表配置
  196. async function SaveConfig() {
  197. // 注意:只传入要修改的字段,其他字段(如 semesterTotalWeeks)会使用 Kotlin 模型中的默认值
  198. const courseConfigData = {
  199. "semesterTotalWeeks": 18,
  200. "defaultClassDuration": 45,
  201. "defaultBreakDuration": 10,
  202. "firstDayOfWeek": 7
  203. };
  204. try {
  205. const configJsonString = JSON.stringify(courseConfigData);
  206. const result = await window.AndroidBridgePromise.saveCourseConfig(configJsonString);
  207. } catch (error) {
  208. console.error("导入配置时发生错误:", error);
  209. AndroidBridge.showToast("导入配置失败: " + error.message);
  210. }
  211. }
  212. /**
  213. * 编排这些异步操作,并在用户取消时停止后续执行。
  214. */
  215. async function runAllDemosSequentially() {
  216. AndroidBridge.showToast("开始导入课表...");
  217. // 1. 校区选择
  218. const selectedCampus = await showCampusSelection();
  219. if (!selectedCampus) {
  220. console.log("用户取消了校区选择,停止后续执行。");
  221. AndroidBridge.showToast("已取消导入");
  222. return; // 用户取消,立即退出函数
  223. }
  224. // 3. 从课表页面中提取课程数据
  225. const PageInfo = await extractCoursesFromPage();
  226. const lessons = PageInfo.lessons;
  227. // 4. 保存课程数据到数据库
  228. await SaveCourses(lessons);
  229. // 5. 根据选择的校区导入对应的时间段
  230. await importTimeSlotsByCampus(selectedCampus);
  231. // 6. 保存底层配置
  232. await SaveConfig();
  233. // 发送最终的生命周期完成信号
  234. AndroidBridge.notifyTaskCompletion();
  235. AndroidBridge.showToast("课表导入完成!");
  236. }
  237. // 启动所有演示
  238. runAllDemosSequentially();