|
@@ -0,0 +1,180 @@
|
|
|
|
|
+/**
|
|
|
|
|
+ * 拾光课程表适配脚本 - 山东石油化工学院 (sdipct.edu.cn)
|
|
|
|
|
+ * 非该大学开发者适配,开发者无法及时发现问题
|
|
|
|
|
+ * 出现问题请提联系开发者或者提交pr更改,这更加快速
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 解析周次字符串,例如 "1,2,3,4,5" -> [1, 2, 3, 4, 5]
|
|
|
|
|
+ */
|
|
|
|
|
+function parseWeeks(weekStr) {
|
|
|
|
|
+ if (!weekStr) return [];
|
|
|
|
|
+ const weeks = weekStr.split(',')
|
|
|
|
|
+ .map(w => Number(w.trim()))
|
|
|
|
|
+ .filter(w => !isNaN(w) && w > 0);
|
|
|
|
|
+ return [...new Set(weeks)].sort((a, b) => a - b);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 将教务系统返回的 JSON 转换为拾光标准的 CourseJsonModel 数组
|
|
|
|
|
+ */
|
|
|
|
|
+function parseJsonData(jsonData) {
|
|
|
|
|
+ console.log("JS: 开始解析课程 JSON...");
|
|
|
|
|
+ if (!jsonData || jsonData.code !== 0 || !Array.isArray(jsonData.data)) {
|
|
|
|
|
+ return [];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return jsonData.data.filter(raw => {
|
|
|
|
|
+ return raw.kcmc && raw.xq && raw.ps && raw.pe && raw.zc;
|
|
|
|
|
+ }).map(raw => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ name: raw.kcmc.trim(),
|
|
|
|
|
+ teacher: (raw.teaxms || "未知教师").trim(),
|
|
|
|
|
+ position: (raw.jxcdmc || "未知地点").trim(),
|
|
|
|
|
+ day: Number(raw.xq),
|
|
|
|
|
+ startSection: Number(raw.ps),
|
|
|
|
|
+ endSection: Number(raw.pe),
|
|
|
|
|
+ weeks: parseWeeks(raw.zc)
|
|
|
|
|
+ };
|
|
|
|
|
+ }).filter(course => {
|
|
|
|
|
+ return course.weeks.length > 0 && course.startSection <= course.endSection;
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 校验函数:验证用户输入的学年格式
|
|
|
|
|
+ */
|
|
|
|
|
+function validateYearInput(input) {
|
|
|
|
|
+ return /^[0-9]{4}$/.test(input) ? false : "请输入四位数字的起始学年(如 2025)";
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 步骤 A: 显示引导公告
|
|
|
|
|
+ */
|
|
|
|
|
+async function promptUserToStart() {
|
|
|
|
|
+ return await window.AndroidBridgePromise.showAlert(
|
|
|
|
|
+ "教务导入说明",
|
|
|
|
|
+ "1. 请确保已在浏览器中成功登录教务系统\n2. 导入过程中请勿关闭页面",
|
|
|
|
|
+ "好的,开始导入"
|
|
|
|
|
+ );
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 步骤 B: 获取用户输入的学年
|
|
|
|
|
+ */
|
|
|
|
|
+async function getAcademicYear() {
|
|
|
|
|
+ const currentYear = new Date().getFullYear().toString();
|
|
|
|
|
+ return await window.AndroidBridgePromise.showPrompt(
|
|
|
|
|
+ "选择学年",
|
|
|
|
|
+ "请输入要导入的起始学年(例如 2025-2026 应输入2025):",
|
|
|
|
|
+ currentYear,
|
|
|
|
|
+ "validateYearInput"
|
|
|
|
|
+ );
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 步骤 C: 选择学期
|
|
|
|
|
+ */
|
|
|
|
|
+async function selectSemester() {
|
|
|
|
|
+ const semesters = ["第一学期 (秋季)", "第二学期 (春季)"];
|
|
|
|
|
+ return await window.AndroidBridgePromise.showSingleSelection(
|
|
|
|
|
+ "选择学期",
|
|
|
|
|
+ JSON.stringify(semesters),
|
|
|
|
|
+ 0
|
|
|
|
|
+ );
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 发起网络请求并获取数据
|
|
|
|
|
+ */
|
|
|
|
|
+async function fetchCourses(academicYear, semesterIndex) {
|
|
|
|
|
+ AndroidBridge.showToast("正在请求教务数据...");
|
|
|
|
|
+
|
|
|
|
|
+ const semesterCode = semesterIndex === 0 ? "01" : "02";
|
|
|
|
|
+ const body = `xnxqdm=${academicYear}${semesterCode}`;
|
|
|
|
|
+ const url = "https://webvpn.sdipct.edu.cn/https/77726476706e69737468656265737421fae0598869236c596e0b9de29d51367bd943/new/student/xsgrkb/getCalendarWeekDatas?vpn-12-o2-jwxt.sdipct.edu.cn";
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await fetch(url, {
|
|
|
|
|
+ method: "POST",
|
|
|
|
|
+ headers: { "content-type": "application/x-www-form-urlencoded; charset=UTF-8" },
|
|
|
|
|
+ body: body,
|
|
|
|
|
+ credentials: "include"
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (!response.ok) throw new Error(`HTTP Error: ${response.status}`);
|
|
|
|
|
+
|
|
|
|
|
+ const jsonData = await response.json();
|
|
|
|
|
+ return parseJsonData(jsonData);
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error("Fetch Error:", e);
|
|
|
|
|
+ AndroidBridge.showToast("网络请求失败,请检查登录状态");
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 预设时间段数据
|
|
|
|
|
+ */
|
|
|
|
|
+const StandardTimeSlots = [
|
|
|
|
|
+ { number: 1, startTime: "08:00", endTime: "08:50" },
|
|
|
|
|
+ { number: 2, startTime: "09:00", endTime: "09:50" },
|
|
|
|
|
+ { number: 3, startTime: "10:10", endTime: "11:00" },
|
|
|
|
|
+ { number: 4, startTime: "11:10", endTime: "12:00" },
|
|
|
|
|
+ { number: 5, startTime: "14:00", endTime: "14:50" },
|
|
|
|
|
+ { number: 6, startTime: "15:00", endTime: "15:50" },
|
|
|
|
|
+ { number: 7, startTime: "16:10", endTime: "17:00" },
|
|
|
|
|
+ { number: 8, startTime: "17:10", endTime: "18:00" },
|
|
|
|
|
+ { number: 9, startTime: "19:00", endTime: "19:50" },
|
|
|
|
|
+ { number: 10, startTime: "20:00", endTime: "20:50" }
|
|
|
|
|
+];
|
|
|
|
|
+
|
|
|
|
|
+// 主流程编排
|
|
|
|
|
+async function runImportFlow() {
|
|
|
|
|
+ // 1. 前置检查
|
|
|
|
|
+ const isReady = await promptUserToStart();
|
|
|
|
|
+ if (!isReady) return; // 用户点击取消,静默退出
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 获取参数(学年、学期)
|
|
|
|
|
+ const year = await getAcademicYear();
|
|
|
|
|
+ if (!year) {
|
|
|
|
|
+ AndroidBridge.showToast("导入已取消");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const semesterIdx = await selectSemester();
|
|
|
|
|
+ if (semesterIdx === null) {
|
|
|
|
|
+ AndroidBridge.showToast("导入已取消");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 执行获取与解析
|
|
|
|
|
+ const courses = await fetchCourses(year, semesterIdx);
|
|
|
|
|
+ if (!courses || courses.length === 0) {
|
|
|
|
|
+ if (courses && courses.length === 0) AndroidBridge.showToast("该学期暂无课程数据");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 数据保存
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 保存课程
|
|
|
|
|
+ await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(courses));
|
|
|
|
|
+
|
|
|
|
|
+ // 保存预设时间表
|
|
|
|
|
+ await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(StandardTimeSlots));
|
|
|
|
|
+
|
|
|
|
|
+ AndroidBridge.showToast(`成功导入 ${courses.length} 门课程!`);
|
|
|
|
|
+
|
|
|
|
|
+ // 5. 流程收尾:通知原生端任务完成
|
|
|
|
|
+ AndroidBridge.notifyTaskCompletion();
|
|
|
|
|
+ console.log("JS: 流程成功完成");
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ AndroidBridge.showToast("保存失败: " + e.message);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 启动入口
|
|
|
|
|
+runImportFlow();
|