HUAT.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. // 文件: HUAT1_fixed_shift.js - 修复节次偏移 + 季节时间选择 (夏季/秋季)
  2. // ==================== 验证函数 ====================
  3. function validateDate(dateStr) {
  4. if (!dateStr || dateStr.trim().length === 0) {
  5. return "日期不能为空!";
  6. }
  7. const datePattern = /^\d{4}-\d{2}-\d{2}$/;
  8. if (!datePattern.test(dateStr)) {
  9. return "日期格式必须是 YYYY-MM-DD!";
  10. }
  11. const parts = dateStr.split('-');
  12. const year = parseInt(parts[0]);
  13. const month = parseInt(parts[1]);
  14. const day = parseInt(parts[2]);
  15. const date = new Date(year, month - 1, day);
  16. if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {
  17. return "请输入有效的日期!";
  18. }
  19. return false;
  20. }
  21. function validateName(name) {
  22. if (name === null || name.trim().length === 0) {
  23. return "输入不能为空!";
  24. }
  25. if (name.length < 2) {
  26. return "至少需要2个字符!";
  27. }
  28. return false;
  29. }
  30. // ==================== 课表数据提取函数 ====================
  31. /**
  32. * 从页面提取课表数据 - 修复3-4和7-8节偏移
  33. */
  34. function extractCourseData() {
  35. console.log("开始提取湖北汽车工业学院课表数据...");
  36. const courses = [];
  37. // 获取所有课表行 - 使用更精确的选择器
  38. const rows = document.querySelectorAll('.el-table__body-wrapper tbody tr');
  39. console.log(`找到 ${rows.length} 行课表数据`);
  40. // 获取时间标签,用于验证行对应的节次
  41. const timeLabels = document.querySelectorAll('.el-table__header-wrapper th .cell, .el-table__header-wrapper th span');
  42. console.log("时间标签:", Array.from(timeLabels).map(el => el.textContent));
  43. rows.forEach((row, rowIndex) => {
  44. // 获取该行的所有单元格
  45. const cells = row.querySelectorAll('td');
  46. // 从第1个单元格开始是周一至周日(跳过第0个时间单元格)
  47. for (let dayIndex = 1; dayIndex < cells.length; dayIndex++) {
  48. const cell = cells[dayIndex];
  49. // 星期映射修正:第1列=周一(0), 第2列=周二(1), ... 第7列=周日(6)
  50. let day = dayIndex - 1;
  51. // 修复3-4节和7-8节的偏移
  52. // 3-4节在行索引2-3,7-8节在行索引6-7
  53. if ((rowIndex >= 2 && rowIndex <= 3) || (rowIndex >= 6 && rowIndex <= 7)) {
  54. // 将周一向后推一天
  55. if (day === 0) day = 1; // 周一 -> 周二
  56. else if (day === 1) day = 2; // 周二 -> 周三
  57. else if (day === 2) day = 3; // 周三 -> 周四
  58. else if (day === 3) day = 4; // 周四 -> 周五
  59. else if (day === 4) day = 5; // 周五 -> 周六
  60. else if (day === 5) day = 6; // 周六 -> 周日
  61. // 周日(6)保持不变
  62. }
  63. // 查找单元格内的所有课程块 - 使用更通用的选择器
  64. const courseBlocks = cell.querySelectorAll('[class*="theory"], .theory, [class*="course"], div[style*="background"]');
  65. if (courseBlocks.length > 0) {
  66. courseBlocks.forEach(block => {
  67. try {
  68. const course = parseCourseBlock(block, day, rowIndex);
  69. if (course) {
  70. courses.push(course);
  71. }
  72. } catch (e) {
  73. console.error("解析课程块失败:", e);
  74. }
  75. });
  76. }
  77. }
  78. });
  79. // 如果没找到课程,尝试另一种选择器
  80. if (courses.length === 0) {
  81. console.log("尝试使用备用选择器...");
  82. const allCourseElements = document.querySelectorAll('[class*="theory"], .theory, [class*="course"]');
  83. console.log(`找到 ${allCourseElements.length} 个可能的课程元素`);
  84. allCourseElements.forEach(element => {
  85. // 尝试找到元素所在的单元格和行
  86. const td = element.closest('td');
  87. if (td) {
  88. const tr = td.closest('tr');
  89. if (tr) {
  90. const rowIndex = Array.from(tr.parentNode.children).indexOf(tr);
  91. const dayIndex = Array.from(td.parentNode.children).indexOf(td);
  92. if (dayIndex >= 1 && dayIndex <= 7) {
  93. // 星期映射修正:第1列=周一(0), 第2列=周二(1), ... 第7列=周日(6)
  94. let day = dayIndex - 1;
  95. // 修复3-4节和7-8节的偏移
  96. if ((rowIndex >= 2 && rowIndex <= 3) || (rowIndex >= 6 && rowIndex <= 7)) {
  97. if (day === 0) day = 1;
  98. else if (day === 1) day = 2;
  99. else if (day === 2) day = 3;
  100. else if (day === 3) day = 4;
  101. else if (day === 4) day = 5;
  102. else if (day === 5) day = 6;
  103. }
  104. const course = parseCourseBlock(element, day, rowIndex);
  105. if (course) {
  106. courses.push(course);
  107. }
  108. }
  109. }
  110. }
  111. });
  112. }
  113. // 去重
  114. const uniqueCourses = removeDuplicates(courses);
  115. // 按星期和节次排序
  116. uniqueCourses.sort((a, b) => {
  117. if (a.day !== b.day) return a.day - b.day;
  118. return a.startSection - b.startSection;
  119. });
  120. console.log(`共提取到 ${uniqueCourses.length} 门课程`);
  121. uniqueCourses.forEach(c => {
  122. console.log(`星期${c.day+1}: ${c.name} - ${c.teacher} - ${c.position} - 第${c.startSection}-${c.endSection}节`);
  123. });
  124. return uniqueCourses;
  125. }
  126. /**
  127. * 解析课程块 - 修复节次映射
  128. */
  129. function parseCourseBlock(block, day, rowIndex) {
  130. // 获取所有子元素
  131. const children = block.children;
  132. let name = '', teacher = '', position = '', weeks = [];
  133. // 方法1:通过子元素解析
  134. if (children.length >= 3) {
  135. // 课程名称通常在h3标签中
  136. const nameElement = block.querySelector('h3');
  137. if (nameElement) {
  138. name = nameElement.innerText.trim();
  139. // 只移除括号内是纯数字且不是课程名称标识的情况
  140. // 但保留(24)这种课程代码
  141. // 如果括号内是纯数字且长度大于2,可能是周次信息,否则保留
  142. name = name.replace(/[((]\d+[))]/g, function(match) {
  143. // 提取括号内的数字
  144. const num = match.replace(/[(())]/g, '');
  145. // 如果数字大于30,可能是周次信息,移除;否则保留(如24是课程代码)
  146. if (parseInt(num) > 30) {
  147. return '';
  148. }
  149. return match;
  150. }).trim();
  151. }
  152. // 教师信息通常在第一个p标签中
  153. const teacherElement = block.querySelector('p:first-child span:first-child, p:nth-child(1) span');
  154. if (teacherElement) {
  155. teacher = teacherElement.innerText.trim();
  156. // 移除课时信息(如"2H")
  157. teacher = teacher.replace(/\d+H?$/, '').trim();
  158. }
  159. // 周次和教室信息通常在第二个p标签中
  160. const weekPositionElement = block.querySelector('p:nth-child(2) span, p:last-child span');
  161. if (weekPositionElement) {
  162. const text = weekPositionElement.innerText.trim();
  163. const result = parseWeekAndPosition(text);
  164. weeks = result.weeks;
  165. position = result.position;
  166. }
  167. }
  168. // 方法2:如果子元素解析失败,通过文本行解析
  169. if (!name || !teacher || !position) {
  170. const text = block.innerText;
  171. const lines = text.split('\n').filter(line => line.trim());
  172. if (lines.length >= 3) {
  173. // 第1行:课程名称
  174. if (!name) {
  175. name = lines[0].trim();
  176. // 只移除括号内是纯数字且不是课程名称标识的情况
  177. name = name.replace(/[((]\d+[))]/g, function(match) {
  178. const num = match.replace(/[(())]/g, '');
  179. if (parseInt(num) > 30) {
  180. return '';
  181. }
  182. return match;
  183. }).trim();
  184. }
  185. // 第2行:教师信息
  186. if (!teacher && lines[1]) {
  187. teacher = lines[1].trim();
  188. teacher = teacher.replace(/\d+H?$/, '').trim();
  189. }
  190. // 第3行:周次和教室
  191. if (!position && lines[2]) {
  192. const result = parseWeekAndPosition(lines[2].trim());
  193. weeks = result.weeks;
  194. position = result.position;
  195. }
  196. }
  197. }
  198. // 如果还是没有找到教室,尝试从整个块中提取数字教室
  199. if (!position || position === '未知教室') {
  200. position = extractClassroom(block.innerText);
  201. }
  202. // 节次映射修正:按行索引重新定义startSection和endSection
  203. let startSection, endSection;
  204. switch(rowIndex) {
  205. case 0: // 第1行:第1节
  206. startSection = 1;
  207. endSection = 1;
  208. break;
  209. case 1: // 第2行:第2节
  210. startSection = 2;
  211. endSection = 2;
  212. break;
  213. case 2: // 第3行:第3节
  214. startSection = 3;
  215. endSection = 3;
  216. break;
  217. case 3: // 第4行:第4节
  218. startSection = 4;
  219. endSection = 4;
  220. break;
  221. case 4: // 第5行:第5节
  222. startSection = 5;
  223. endSection = 5;
  224. break;
  225. case 5: // 第6行:第6节
  226. startSection = 6;
  227. endSection = 6;
  228. break;
  229. case 6: // 第7行:第7节
  230. startSection = 7;
  231. endSection = 7;
  232. break;
  233. case 7: // 第8行:第8节
  234. startSection = 8;
  235. endSection = 8;
  236. break;
  237. case 8: // 第9行:第9节
  238. startSection = 9;
  239. endSection = 9;
  240. break;
  241. case 9: // 第10行:第10节
  242. startSection = 10;
  243. endSection = 10;
  244. break;
  245. case 10: // 第11行:第11节
  246. startSection = 11;
  247. endSection = 11;
  248. break;
  249. default: // 默认第1节
  250. startSection = 1;
  251. endSection = 1;
  252. }
  253. // 检查是否有rowspan(连堂课程)- 修正连堂节次计算逻辑
  254. const td = block.closest('td');
  255. if (td) {
  256. const rowspan = td.getAttribute('rowspan');
  257. if (rowspan) {
  258. const span = parseInt(rowspan);
  259. if (span > 1) {
  260. // 连堂时,结束节次 = 开始节次 + 跨行数 - 1
  261. endSection = startSection + span - 1;
  262. // 限制节次最大值不超过11
  263. if (endSection > 11) endSection = 11;
  264. }
  265. }
  266. }
  267. // 只有提取到有效的课程名称才返回
  268. if (!name || name.includes('节') || name.length < 2 || name.includes('理论课')) {
  269. return null;
  270. }
  271. // 特殊处理体育课(可能没有教室)
  272. if (name.includes('体育') && position === '未知教室') {
  273. position = '操场';
  274. }
  275. const course = {
  276. name: name,
  277. teacher: teacher || '未知教师',
  278. position: position || '未知教室',
  279. day: day,
  280. startSection: startSection,
  281. endSection: endSection,
  282. weeks: weeks.length > 0 ? weeks : [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],
  283. isCustomTime: false
  284. };
  285. return course;
  286. }
  287. /**
  288. * 解析周次和教室
  289. */
  290. function parseWeekAndPosition(text) {
  291. let weeks = [];
  292. let position = '未知教室';
  293. if (!text) return { weeks, position };
  294. console.log("解析周次和教室:", text);
  295. // 匹配周次模式:如 "3-16周"、"1-12周"、"6-8周"
  296. const weekPattern = /(\d+-\d+周|\d+,\d+周|\d+周)/;
  297. const weekMatch = text.match(weekPattern);
  298. if (weekMatch) {
  299. const weekStr = weekMatch[1];
  300. weeks = parseWeeks(weekStr);
  301. // 剩余部分可能是教室
  302. let remaining = text.replace(weekStr, '').trim();
  303. // 如果剩余部分包含数字,很可能是教室
  304. if (remaining && /\d+/.test(remaining)) {
  305. position = remaining;
  306. } else {
  307. // 尝试从原文本中提取教室(通常是4位数字)
  308. const roomMatch = text.match(/\b\d{3,4}\b/);
  309. if (roomMatch) {
  310. position = roomMatch[0];
  311. }
  312. }
  313. } else {
  314. // 如果没有周次信息,直接尝试提取教室
  315. const roomMatch = text.match(/\b\d{3,4}\b/);
  316. if (roomMatch) {
  317. position = roomMatch[0];
  318. weeks = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
  319. }
  320. }
  321. // 清理教室字符串
  322. if (position && position !== '未知教室') {
  323. // 只保留数字
  324. position = position.replace(/[^\d]/g, '');
  325. }
  326. return { weeks, position };
  327. }
  328. /**
  329. * 从文本中提取教室
  330. */
  331. function extractClassroom(text) {
  332. // 匹配常见的教室格式:1205、6604、1231、2303、6403、6212、2103、1233、6104、1203
  333. const roomPatterns = [
  334. /\b\d{4}\b/, // 4位数字
  335. /\b\d{3}\b/, // 3位数字
  336. /[0-9]{3,4}/ // 任意3-4位数字
  337. ];
  338. for (let pattern of roomPatterns) {
  339. const match = text.match(pattern);
  340. if (match) {
  341. return match[0];
  342. }
  343. }
  344. return '未知教室';
  345. }
  346. /**
  347. * 解析周次字符串
  348. */
  349. function parseWeeks(weekStr) {
  350. const weeks = [];
  351. if (!weekStr) return [];
  352. // 移除"周"字
  353. weekStr = weekStr.replace(/周/g, '').trim();
  354. try {
  355. if (weekStr.includes(',')) {
  356. weekStr.split(',').forEach(part => {
  357. if (part.includes('-')) {
  358. const [start, end] = part.split('-').map(Number);
  359. for (let i = start; i <= end; i++) weeks.push(i);
  360. } else {
  361. const w = parseInt(part);
  362. if (!isNaN(w)) weeks.push(w);
  363. }
  364. });
  365. } else if (weekStr.includes('-')) {
  366. const [start, end] = weekStr.split('-').map(Number);
  367. for (let i = start; i <= end; i++) weeks.push(i);
  368. } else {
  369. const w = parseInt(weekStr);
  370. if (!isNaN(w)) weeks.push(w);
  371. }
  372. } catch (e) {
  373. console.error("解析周次失败:", weekStr, e);
  374. }
  375. return weeks.length > 0 ? weeks.sort((a,b) => a-b) : [];
  376. }
  377. /**
  378. * 去重
  379. */
  380. function removeDuplicates(courses) {
  381. const seen = new Map();
  382. return courses.filter(course => {
  383. // 创建唯一键:课程名+教师+星期+开始节次
  384. const key = `${course.name}-${course.teacher}-${course.day}-${course.startSection}`;
  385. if (seen.has(key)) {
  386. return false;
  387. }
  388. seen.set(key, true);
  389. return true;
  390. });
  391. }
  392. /**
  393. * 提取学期信息
  394. */
  395. function extractSemesterInfo() {
  396. return {
  397. semesterStartDate: "2026-03-01",
  398. semesterTotalWeeks: 20
  399. };
  400. }
  401. // ========== 根据季节获取时间段(夏季/秋季) ==========
  402. /**
  403. * 根据季节返回对应的时间段数组
  404. * @param {string} season - "summer" 或 "autumn"
  405. * @returns {Array} 时间段对象数组
  406. */
  407. function getSeasonTimeSlots(season) {
  408. // 上午固定时间(夏秋通用)
  409. const morning_classes = [
  410. {"number": 1, "startTime": "08:10", "endTime": "08:55"},
  411. {"number": 2, "startTime": "09:00", "endTime": "09:45"},
  412. {"number": 3, "startTime": "10:05", "endTime": "10:50"},
  413. {"number": 4, "startTime": "10:55", "endTime": "11:40"}
  414. ];
  415. // 夏季下午/晚上时间
  416. const summer_afternoon_evening = [
  417. {"number": 5, "startTime": "14:30", "endTime": "15:15"},
  418. {"number": 6, "startTime": "15:20", "endTime": "16:05"},
  419. {"number": 7, "startTime": "16:25", "endTime": "17:10"},
  420. {"number": 8, "startTime": "17:15", "endTime": "18:00"},
  421. {"number": 9, "startTime": "18:45", "endTime": "19:30"},
  422. {"number": 10, "startTime": "19:35", "endTime": "20:20"},
  423. {"number": 11, "startTime": "20:25", "endTime": "21:10"}
  424. ];
  425. // 秋季下午/晚上时间
  426. const autumn_afternoon_evening = [
  427. {"number": 5, "startTime": "14:00", "endTime": "14:45"},
  428. {"number": 6, "startTime": "14:50", "endTime": "15:35"},
  429. {"number": 7, "startTime": "15:55", "endTime": "16:40"},
  430. {"number": 8, "startTime": "16:45", "endTime": "17:30"},
  431. {"number": 9, "startTime": "18:15", "endTime": "19:00"},
  432. {"number": 10, "startTime": "19:05", "endTime": "19:50"},
  433. {"number": 11, "startTime": "19:55", "endTime": "20:40"}
  434. ];
  435. if (season === 'summer') {
  436. return morning_classes.concat(summer_afternoon_evening);
  437. } else if (season === 'autumn') {
  438. return morning_classes.concat(autumn_afternoon_evening);
  439. } else {
  440. // 默认返回夏季(兼容旧调用)
  441. console.warn("未知季节参数,使用夏季时间");
  442. return morning_classes.concat(summer_afternoon_evening);
  443. }
  444. }
  445. // ==================== 弹窗和导入函数 ====================
  446. async function demoAlert() {
  447. try {
  448. const confirmed = await window.AndroidBridgePromise.showAlert(
  449. "📚 湖北汽车工业学院课表导入",
  450. "将提取当前页面的课表数据并导入到App\n\n" +
  451. "📌 请确认已在课表页面\n" +
  452. "📌 将提取所有可见课程",
  453. "开始导入",
  454. "取消"
  455. );
  456. return confirmed;
  457. } catch (error) {
  458. console.error("显示弹窗错误:", error);
  459. return false;
  460. }
  461. }
  462. async function demoPrompt() {
  463. try {
  464. const semesterInfo = extractSemesterInfo();
  465. const semesterStart = await window.AndroidBridgePromise.showPrompt(
  466. "📅 设置开学日期",
  467. "请输入本学期开学日期",
  468. semesterInfo.semesterStartDate,
  469. "validateDate"
  470. );
  471. return semesterStart || semesterInfo.semesterStartDate;
  472. } catch (error) {
  473. console.error("日期输入错误:", error);
  474. return "2026-03-01";
  475. }
  476. }
  477. /**
  478. * 导入时间段 - 让用户选择夏季或秋季
  479. * 修复:使用更可靠的方式来处理两个选项
  480. */
  481. async function importPresetTimeSlots() {
  482. console.log("正在准备预设时间段数据...");
  483. // 修复:使用showConfirmDialog而不是showAlert来确保两个按钮都显示
  484. try {
  485. // 尝试使用showConfirmDialog(如果有的话)
  486. if (window.AndroidBridgePromise && typeof window.AndroidBridgePromise.showConfirmDialog === 'function') {
  487. const seasonChoice = await window.AndroidBridgePromise.showConfirmDialog(
  488. "⏰ 选择作息时间",
  489. "请根据当前学期选择作息时间:\n\n🌞 夏季(5月1日-9月30日)\n🍂 秋季(10月1日-次年4月30日)",
  490. "夏季",
  491. "秋季"
  492. );
  493. // showConfirmDialog 点击左侧按钮返回 true,点击右侧按钮返回 false
  494. let season = seasonChoice === true ? 'summer' : 'autumn';
  495. // 获取对应季节的时间段
  496. const presetTimeSlots = getSeasonTimeSlots(season);
  497. console.log(`选择的季节: ${season},时间段:`, presetTimeSlots);
  498. // 导入时间段
  499. const result = await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(presetTimeSlots));
  500. if (result === true) {
  501. console.log("预设时间段导入成功!");
  502. window.AndroidBridge.showToast(`✅ ${season === 'summer' ? '夏季' : '秋季'}时间段导入成功!`);
  503. return true;
  504. } else {
  505. console.log("预设时间段导入未成功,结果:" + result);
  506. window.AndroidBridge.showToast("❌ 时间段导入失败,请查看日志。");
  507. return false;
  508. }
  509. }
  510. // 使用showAlert但确保两个按钮都显示
  511. else {
  512. // 方法1:尝试使用showAlert两个按钮 - 修复按钮显示问题
  513. // 注意:有些AndroidBridge实现中,showAlert的第三个参数是左侧按钮,第四个参数是右侧按钮
  514. // 确保两个按钮文本都不为空
  515. const seasonChoice = await window.AndroidBridgePromise.showAlert(
  516. "⏰ 选择作息时间",
  517. "请根据当前学期选择作息时间:\n\n🌞 夏季(5月1日-9月30日)\n🍂 秋季(10月1日-次年4月30日)",
  518. "夏季", // 左侧按钮
  519. "秋季" // 右侧按钮
  520. );
  521. // 根据返回值判断用户点击了哪个按钮
  522. // showAlert 点击左侧按钮返回 true,点击右侧按钮返回 false
  523. let season = 'summer';
  524. // 更精确地判断返回值
  525. if (seasonChoice === true || seasonChoice === "true" || seasonChoice === 1) {
  526. season = 'summer';
  527. console.log("用户选择了夏季");
  528. } else if (seasonChoice === false || seasonChoice === "false" || seasonChoice === 0) {
  529. season = 'autumn';
  530. console.log("用户选择了秋季");
  531. } else if (typeof seasonChoice === 'string') {
  532. // 如果返回的是按钮文本
  533. if (seasonChoice === '夏季') {
  534. season = 'summer';
  535. } else {
  536. season = 'autumn';
  537. }
  538. }
  539. // 获取对应季节的时间段
  540. const presetTimeSlots = getSeasonTimeSlots(season);
  541. console.log(`选择的季节: ${season},时间段:`, presetTimeSlots);
  542. // 导入时间段
  543. const result = await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(presetTimeSlots));
  544. if (result === true) {
  545. console.log("预设时间段导入成功!");
  546. window.AndroidBridge.showToast(`✅ ${season === 'summer' ? '夏季' : '秋季'}时间段导入成功!`);
  547. return true;
  548. } else {
  549. console.log("预设时间段导入未成功,结果:" + result);
  550. window.AndroidBridge.showToast("❌ 时间段导入失败,请查看日志。");
  551. return false;
  552. }
  553. }
  554. } catch (error) {
  555. console.error("导入时间段时发生错误:", error);
  556. // 方法2:如果两个按钮的方法失败,尝试使用showPrompt让用户输入
  557. try {
  558. console.log("尝试使用备用方法选择季节...");
  559. const seasonInput = await window.AndroidBridgePromise.showPrompt(
  560. "⏰ 选择作息时间",
  561. "请输入季节(输入 1 选择夏季,输入 2 选择秋季):\n\n1 - 夏季 (5月1日-9月30日)\n2 - 秋季 (10月1日-次年4月30日)",
  562. "1"
  563. );
  564. let season = 'summer';
  565. if (seasonInput === '2') {
  566. season = 'autumn';
  567. }
  568. const presetTimeSlots = getSeasonTimeSlots(season);
  569. const result = await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(presetTimeSlots));
  570. if (result === true) {
  571. window.AndroidBridge.showToast(`✅ ${season === 'summer' ? '夏季' : '秋季'}时间段导入成功!`);
  572. return true;
  573. }
  574. } catch (secondError) {
  575. console.error("备用方法也失败:", secondError);
  576. window.AndroidBridge.showToast("导入时间段失败,使用默认夏季时间");
  577. // 方法3:使用默认夏季
  578. const defaultTimeSlots = getSeasonTimeSlots('summer');
  579. await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(defaultTimeSlots));
  580. window.AndroidBridge.showToast("✅ 默认夏季时间段导入成功");
  581. return true;
  582. }
  583. }
  584. }
  585. async function importSchedule() {
  586. try {
  587. AndroidBridge.showToast("正在提取课表数据...");
  588. const courses = extractCourseData();
  589. if (courses.length === 0) {
  590. await window.AndroidBridgePromise.showAlert(
  591. "⚠️ 提取失败",
  592. "未找到课表数据,请确认已在课表页面",
  593. "知道了"
  594. );
  595. return false;
  596. }
  597. // 预览
  598. const preview = await window.AndroidBridgePromise.showAlert(
  599. "📊 数据预览",
  600. `共找到 ${courses.length} 门课程\n\n` +
  601. `示例:\n${courses.slice(0, 5).map(c =>
  602. `• 周${c.day+1} ${c.name} - 第${c.startSection}-${c.endSection}节`
  603. ).join('\n')}`,
  604. "确认导入",
  605. "取消"
  606. );
  607. if (!preview) {
  608. return false;
  609. }
  610. // 导入课程
  611. AndroidBridge.showToast("正在导入课程...");
  612. const result = await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(courses));
  613. if (result === true) {
  614. const semesterDate = await demoPrompt();
  615. const configResult = await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify({
  616. semesterStartDate: semesterDate,
  617. semesterTotalWeeks: 20,
  618. defaultClassDuration: 45,
  619. defaultBreakDuration: 10,
  620. firstDayOfWeek: 1
  621. }));
  622. if (configResult === true) {
  623. AndroidBridge.showToast(`✅ 导入成功!共${courses.length}门课程`);
  624. return true;
  625. }
  626. }
  627. AndroidBridge.showToast("❌ 导入失败");
  628. return false;
  629. } catch (error) {
  630. console.error("导入错误:", error);
  631. AndroidBridge.showToast("导入出错: " + error.message);
  632. return false;
  633. }
  634. }
  635. async function runAllDemosSequentially() {
  636. AndroidBridge.showToast("🚀 课表导入助手启动...");
  637. // 检查页面
  638. if (!window.location.href.includes('studentHome/expectCourseTable')) {
  639. const goToPage = await window.AndroidBridgePromise.showAlert(
  640. "页面提示",
  641. "当前不在课表页面,是否跳转?",
  642. "跳转",
  643. "取消"
  644. );
  645. if (goToPage) {
  646. window.location.href = 'http://neweas.huat.edu.cn/#/studentHome/expectCourseTable';
  647. }
  648. return;
  649. }
  650. const start = await demoAlert();
  651. if (!start) {
  652. AndroidBridge.showToast("已取消");
  653. return;
  654. }
  655. // 导入时间段(带季节选择:夏季/秋季)
  656. await importPresetTimeSlots();
  657. // 导入课表
  658. await importSchedule();
  659. AndroidBridge.notifyTaskCompletion();
  660. }
  661. // 导出函数
  662. window.validateDate = validateDate;
  663. window.validateName = validateName;
  664. window.extractCourseData = extractCourseData;
  665. window.importPresetTimeSlots = importPresetTimeSlots;
  666. window.getSeasonTimeSlots = getSeasonTimeSlots;
  667. // 启动
  668. runAllDemosSequentially();