huayu.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. // 山东华宇工学院 拾光课程表适配脚本
  2. // 基于正方教务系统API适配
  3. /**
  4. * 解析周次字符串
  5. */
  6. function parseWeeks(weekStr) {
  7. var weeks = [];
  8. if (!weekStr) return weeks;
  9. var weekSets = weekStr.split(',');
  10. for (var i = 0; i < weekSets.length; i++) {
  11. var set = weekSets[i].trim();
  12. var isSingle = set.indexOf('(单)') !== -1;
  13. var isDouble = set.indexOf('(双)') !== -1;
  14. set = set.split('(单)').join('');
  15. set = set.split('(双)').join('');
  16. set = set.trim();
  17. var dashIdx = set.indexOf('-');
  18. var start = 0;
  19. var end = 0;
  20. var processed = false;
  21. if (dashIdx !== -1 && set.indexOf('周') !== -1) {
  22. start = parseInt(set.substring(0, dashIdx));
  23. var endPart = set.substring(dashIdx + 1);
  24. endPart = endPart.split('周').join('');
  25. end = parseInt(endPart);
  26. processed = true;
  27. } else {
  28. var weekNum = parseInt(set);
  29. if (!isNaN(weekNum)) {
  30. start = end = weekNum;
  31. processed = true;
  32. }
  33. }
  34. if (processed) {
  35. for (var w = start; w <= end; w++) {
  36. if (isSingle && w % 2 === 0) continue;
  37. if (isDouble && w % 2 !== 0) continue;
  38. weeks.push(w);
  39. }
  40. }
  41. }
  42. var uniqueWeeks = [];
  43. for (var j = 0; j < weeks.length; j++) {
  44. if (uniqueWeeks.indexOf(weeks[j]) === -1) {
  45. uniqueWeeks.push(weeks[j]);
  46. }
  47. }
  48. uniqueWeeks.sort(function(a, b) { return a - b; });
  49. return uniqueWeeks;
  50. }
  51. /**
  52. * 解析API返回的JSON数据
  53. */
  54. function parseJsonData(jsonData) {
  55. console.log('JS: parseJsonData 正在解析JSON数据...');
  56. if (!jsonData || !Array.isArray(jsonData.kbList)) {
  57. console.warn('JS: JSON数据结构错误或缺少kbList字段');
  58. return [];
  59. }
  60. var rawCourseList = jsonData.kbList;
  61. var finalCourseList = [];
  62. for (var i = 0; i < rawCourseList.length; i++) {
  63. var rawCourse = rawCourseList[i];
  64. if (!rawCourse.kcmc || !rawCourse.xm || !rawCourse.cdmc ||
  65. !rawCourse.xqj || !rawCourse.jcs || !rawCourse.zcd) {
  66. continue;
  67. }
  68. var weeksArray = parseWeeks(rawCourse.zcd);
  69. if (weeksArray.length === 0) {
  70. continue;
  71. }
  72. var sectionParts = rawCourse.jcs.split('-');
  73. var startSection = parseInt(sectionParts[0]);
  74. var endSection = parseInt(sectionParts[sectionParts.length - 1]);
  75. var day = parseInt(rawCourse.xqj);
  76. if (isNaN(day) || isNaN(startSection) || isNaN(endSection) ||
  77. day < 1 || day > 7 || startSection > endSection) {
  78. continue;
  79. }
  80. var courseName = rawCourse.kcmc.trim();
  81. courseName = courseName.split('★').join('');
  82. courseName = courseName.split('●').join('');
  83. courseName = courseName.split('◆').join('');
  84. courseName = courseName.split('◇').join('');
  85. courseName = courseName.split('○').join('');
  86. finalCourseList.push({
  87. name: courseName,
  88. teacher: rawCourse.xm.trim(),
  89. position: rawCourse.cdmc.trim(),
  90. day: day,
  91. startSection: startSection,
  92. endSection: endSection,
  93. weeks: weeksArray
  94. });
  95. }
  96. finalCourseList.sort(function(a, b) {
  97. if (a.day !== b.day) return a.day - b.day;
  98. if (a.startSection !== b.startSection) return a.startSection - b.startSection;
  99. return a.name.localeCompare(b.name);
  100. });
  101. console.log('JS: JSON数据解析完成,共找到 ' + finalCourseList.length + ' 门课程');
  102. return finalCourseList;
  103. }
  104. /**
  105. * 检查是否在登录页面
  106. */
  107. function isLoginPage() {
  108. var url = window.location.href;
  109. var loginUrls = [
  110. 'login_slogin.html',
  111. 'login_login.html'
  112. ];
  113. for (var i = 0; i < loginUrls.length; i++) {
  114. if (url.indexOf(loginUrls[i]) !== -1) return true;
  115. }
  116. return false;
  117. }
  118. /**
  119. * 自动获取当前学年
  120. * 优先从页面下拉框获取,失败则根据日期计算
  121. */
  122. function getCurrentAcademicYear() {
  123. // 尝试从页面获取
  124. var xnmSelect = document.getElementById('xnm');
  125. if (xnmSelect && xnmSelect.value) {
  126. var year = xnmSelect.value;
  127. console.log('JS: 从页面获取到学年: ' + year);
  128. return year;
  129. }
  130. // 根据当前日期计算学年
  131. var now = new Date();
  132. var year = now.getFullYear();
  133. var month = now.getMonth() + 1;
  134. // 9月及以后属于新学年
  135. if (month >= 9) {
  136. console.log('JS: 根据日期计算学年: ' + year);
  137. return year.toString();
  138. } else {
  139. console.log('JS: 根据日期计算学年: ' + (year - 1));
  140. return (year - 1).toString();
  141. }
  142. }
  143. /**
  144. * 自动获取当前学期
  145. * 优先从页面下拉框获取,失败则根据日期计算
  146. */
  147. function getCurrentSemesterIndex() {
  148. // 尝试从页面获取
  149. var xqmSelect = document.getElementById('xqm');
  150. if (xqmSelect && xqmSelect.value) {
  151. var xqm = xqmSelect.value;
  152. // xqm: 3=第一学期, 12=第二学期
  153. if (xqm === '3') {
  154. console.log('JS: 从页面获取到学期: 第一学期');
  155. return 0;
  156. } else if (xqm === '12') {
  157. console.log('JS: 从页面获取到学期: 第二学期');
  158. return 1;
  159. }
  160. }
  161. // 根据日期计算:9月-次年2月为第一学期,3月-7月为第二学期
  162. var month = new Date().getMonth() + 1;
  163. if (month >= 3 && month <= 7) {
  164. console.log('JS: 根据日期计算学期: 第二学期');
  165. return 1;
  166. } else {
  167. console.log('JS: 根据日期计算学期: 第一学期');
  168. return 0;
  169. }
  170. }
  171. function getSemesterCode(semesterIndex) {
  172. return semesterIndex === 0 ? '3' : '12';
  173. }
  174. function getSemesterName(semesterIndex) {
  175. return semesterIndex === 0 ? '第一学期' : '第二学期';
  176. }
  177. function validateDateInput(input) {
  178. console.log('JS: validateDateInput 被调用,输入: ' + input);
  179. if (/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(input)) {
  180. console.log('JS: validateDateInput 验证通过');
  181. return false;
  182. } else {
  183. console.log('JS: validateDateInput 验证失败');
  184. return '请输入正确格式的开学日期(如2025-09-01)!';
  185. }
  186. }
  187. async function promptUserToStart() {
  188. console.log('JS: 流程开始:显示公告');
  189. return await window.AndroidBridgePromise.showAlert(
  190. '教务系统课表导入',
  191. '导入前请确保您已在浏览器中成功登录教务系统。',
  192. '好的,开始导入'
  193. );
  194. }
  195. async function confirmAcademicYear(year, semesterName) {
  196. console.log('JS: 显示学年学期确认弹窗');
  197. return await window.AndroidBridgePromise.showAlert(
  198. '确认学年学期',
  199. '检测到当前为 ' + year + '-' + (parseInt(year) + 1) + ' 学年' + semesterName + ',确认导入该学期课程吗?',
  200. '确认导入'
  201. );
  202. }
  203. async function getSemesterStartDate() {
  204. console.log('JS: 提示用户输入开学日期');
  205. return await window.AndroidBridgePromise.showPrompt(
  206. '选择开学日期',
  207. '请输入本学期开学日期(格式:YYYY-MM-DD,如2025-09-01):',
  208. '2025-09-01',
  209. 'validateDateInput'
  210. );
  211. }
  212. /**
  213. * 通过API请求和解析课程数据
  214. */
  215. async function fetchAndParseCourses(academicYear, semesterIndex) {
  216. AndroidBridge.showToast('正在请求课表数据...');
  217. var semesterCode = getSemesterCode(semesterIndex);
  218. var baseUrl = window.location.origin;
  219. var url = baseUrl + '/jwglxt/kbcx/xskbcx_cxXsgrkb.html?gnmkdm=N2151';
  220. var body = 'xnm=' + academicYear + '&xqm=' + semesterCode + '&kzlx=ck';
  221. console.log('JS: 发送请求到 ' + url + ', body: ' + body);
  222. try {
  223. var response = await fetch(url, {
  224. headers: {
  225. 'content-type': 'application/x-www-form-urlencoded;charset=UTF-8'
  226. },
  227. referer: baseUrl + '/jwglxt/kbcx/xskbcx_cxXskbcxIndex.html?gnmkdm=N2151',
  228. body: body,
  229. method: 'POST',
  230. credentials: 'include'
  231. });
  232. if (!response.ok) {
  233. throw new Error('网络请求失败。状态码: ' + response.status);
  234. }
  235. var jsonText = await response.text();
  236. var jsonData;
  237. try {
  238. jsonData = JSON.parse(jsonText);
  239. } catch (e) {
  240. console.error('JS: JSON解析失败,可能是会话过期:', e);
  241. AndroidBridge.showToast('数据返回格式错误,可能是您未成功登录或会话已过期。');
  242. return null;
  243. }
  244. var courses = parseJsonData(jsonData);
  245. if (courses.length === 0) {
  246. AndroidBridge.showToast('未找到任何课程数据,请检查所选学年学期是否正确。');
  247. return null;
  248. }
  249. console.log('JS: 课程数据解析成功,共找到 ' + courses.length + ' 门课程');
  250. return courses;
  251. } catch (error) {
  252. AndroidBridge.showToast('请求或解析失败: ' + error.message);
  253. console.error('JS: Fetch/Parse Error:', error);
  254. return null;
  255. }
  256. }
  257. async function saveCourses(parsedCourses) {
  258. AndroidBridge.showToast('正在保存 ' + parsedCourses.length + ' 门课程...');
  259. console.log('JS: 尝试保存 ' + parsedCourses.length + ' 门课程');
  260. try {
  261. await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(parsedCourses));
  262. console.log('JS: 课程保存成功');
  263. return true;
  264. } catch (error) {
  265. AndroidBridge.showToast('课程保存失败: ' + error.message);
  266. console.error('JS: Save Courses Error:', error);
  267. return false;
  268. }
  269. }
  270. async function saveConfig(startDate) {
  271. console.log('JS: 尝试保存课表配置,开学日期: ' + startDate);
  272. var config = {
  273. semesterStartDate: startDate,
  274. semesterTotalWeeks: 20,
  275. defaultClassDuration: 45,
  276. defaultBreakDuration: 10,
  277. firstDayOfWeek: 1
  278. };
  279. try {
  280. await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify(config));
  281. AndroidBridge.showToast('课表配置更新成功!开学日期:' + startDate);
  282. console.log('JS: 配置保存成功');
  283. return true;
  284. } catch (error) {
  285. AndroidBridge.showToast('课表配置保存失败: ' + error.message);
  286. console.error('JS: Save Config Error:', error);
  287. return false;
  288. }
  289. }
  290. // 山东华宇工学院西区冬季作息时间
  291. var TimeSlots = [
  292. { number: 1, startTime: '08:10', endTime: '08:55' },
  293. { number: 2, startTime: '09:05', endTime: '09:50' },
  294. { number: 3, startTime: '10:15', endTime: '11:00' },
  295. { number: 4, startTime: '11:15', endTime: '12:00' },
  296. { number: 5, startTime: '14:40', endTime: '15:25' },
  297. { number: 6, startTime: '15:35', endTime: '16:20' },
  298. { number: 7, startTime: '16:30', endTime: '17:15' },
  299. { number: 8, startTime: '17:25', endTime: '18:10' },
  300. { number: 9, startTime: '19:10', endTime: '19:55' },
  301. { number: 10, startTime: '20:05', endTime: '20:50' }
  302. ];
  303. async function importPresetTimeSlots(timeSlots) {
  304. console.log('JS: 准备导入 ' + timeSlots.length + ' 个预设时间段');
  305. try {
  306. await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(timeSlots));
  307. AndroidBridge.showToast('预设时间段导入成功!');
  308. console.log('JS: 预设时间段导入成功');
  309. } catch (error) {
  310. AndroidBridge.showToast('导入时间段失败: ' + error.message);
  311. console.error('JS: Save Time Slots Error:', error);
  312. }
  313. }
  314. async function runImportFlow() {
  315. if (isLoginPage()) {
  316. AndroidBridge.showToast('导入失败:请先登录教务系统!');
  317. console.log('JS: 检测到当前在登录页面,终止导入');
  318. return;
  319. }
  320. var alertConfirmed = await promptUserToStart();
  321. if (!alertConfirmed) {
  322. AndroidBridge.showToast('用户取消了导入');
  323. console.log('JS: 用户取消了导入流程');
  324. return;
  325. }
  326. // 自动获取学年和学期
  327. var academicYear = getCurrentAcademicYear();
  328. var semesterIndex = getCurrentSemesterIndex();
  329. var semesterName = getSemesterName(semesterIndex);
  330. console.log('JS: 自动获取到学年: ' + academicYear + ', 学期: ' + semesterName);
  331. // 让用户确认
  332. var confirmResult = await confirmAcademicYear(academicYear, semesterName);
  333. if (!confirmResult) {
  334. AndroidBridge.showToast('导入已取消');
  335. console.log('JS: 用户取消确认学年学期');
  336. return;
  337. }
  338. var startDate = await getSemesterStartDate();
  339. if (startDate === null) {
  340. AndroidBridge.showToast('导入已取消');
  341. console.log('JS: 获取开学日期失败/取消,流程终止');
  342. return;
  343. }
  344. console.log('JS: 已选择开学日期: ' + startDate);
  345. var courses = await fetchAndParseCourses(academicYear, semesterIndex);
  346. if (courses === null) {
  347. console.log('JS: 课程获取或解析失败,流程终止');
  348. return;
  349. }
  350. var saveResult = await saveCourses(courses);
  351. if (!saveResult) {
  352. console.log('JS: 课程保存失败,流程终止');
  353. return;
  354. }
  355. var configResult = await saveConfig(startDate);
  356. if (!configResult) {
  357. console.log('JS: 配置保存失败,流程终止');
  358. return;
  359. }
  360. await importPresetTimeSlots(TimeSlots);
  361. AndroidBridge.showToast('课程导入成功,共导入 ' + courses.length + ' 门课程!');
  362. console.log('JS: 整个导入流程执行完毕并成功');
  363. AndroidBridge.notifyTaskCompletion();
  364. }
  365. runImportFlow();