|
|
@@ -1,156 +1,268 @@
|
|
|
// 中山大学教务系统课表导入器
|
|
|
-// 功能:获取教务系统课表 -> 转换为目标 JSON -> 自动下载
|
|
|
+// 功能:获取教务系统课表 -> 转换为目标 JSON -> 通过时光课程表 V2 Bridge 导入
|
|
|
|
|
|
const API_URL = "/jwxt/timetable-search/stuTimeTabPrint/studentQuery";
|
|
|
+const CALENDAR_API_URL = "/jwxt/base-info/school-calender";
|
|
|
let ACAD_YEAR = "2026-1";
|
|
|
|
|
|
-const AVAILABLE_YEARS = [2026, 2027];
|
|
|
-const AVAILABLE_SEMESTERS = [1, 2];
|
|
|
+function validateYearInput(input) {
|
|
|
+ return /^[0-9]{4}$/.test(String(input).trim())
|
|
|
+ ? false
|
|
|
+ : "请输入四位数字的学年!";
|
|
|
+}
|
|
|
|
|
|
-// 中山大学各学期实际开课日期。
|
|
|
-// 日期以中山大学官方校历为准;尚未公布的学期暂不填写,避免错误导入。
|
|
|
-const SEMESTER_START_DATES = {
|
|
|
- "2025-1": "2025-09-08",
|
|
|
- "2025-2": "2026-03-02",
|
|
|
- "2026-1": "2026-09-07"
|
|
|
-};
|
|
|
+async function getAcademicYear() {
|
|
|
+ const defaultYear = ACAD_YEAR.split("-")[0];
|
|
|
|
|
|
-function validateAcademicYear(input) {
|
|
|
- if (/^(2026|2027)$/.test(String(input).trim())) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- return "请输入 2026 或 2027。";
|
|
|
+ const yearSelection = await window.shiguangBridgePromise.showPrompt(
|
|
|
+ "选择学年",
|
|
|
+ "请输入要导入课程的学年(如 2026):",
|
|
|
+ defaultYear || "2026",
|
|
|
+ "validateYearInput"
|
|
|
+ );
|
|
|
+
|
|
|
+ return yearSelection;
|
|
|
}
|
|
|
|
|
|
async function selectSemester() {
|
|
|
- if (!window.AndroidBridgePromise || typeof window.AndroidBridgePromise.showPrompt !== "function") {
|
|
|
- throw new Error("AndroidBridgePromise.showPrompt 不可用,请在时光课程表 App 内运行此适配器。");
|
|
|
+ if (!window.shiguangBridgePromise) {
|
|
|
+ throw new Error(
|
|
|
+ "shiguangBridgePromise 不可用,请在时光课程表 App 内运行此适配器。"
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- const [defaultYear, defaultSemester] = ACAD_YEAR.split("-");
|
|
|
+ if (typeof window.shiguangBridgePromise.showPrompt !== "function") {
|
|
|
+ throw new Error(
|
|
|
+ "shiguangBridgePromise.showPrompt 不可用,请在时光课程表 App 内运行此适配器。"
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
- const yearInput = await window.AndroidBridgePromise.showPrompt(
|
|
|
- "选择学年",
|
|
|
- "请输入学年,例如:2026",
|
|
|
- defaultYear || "2026",
|
|
|
- "validateAcademicYear"
|
|
|
- );
|
|
|
+ if (typeof window.shiguangBridgePromise.showSingleSelection !== "function") {
|
|
|
+ throw new Error(
|
|
|
+ "shiguangBridgePromise.showSingleSelection 不可用,请在时光课程表 App 内运行此适配器。"
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 先输入学年
|
|
|
+ const yearSelection = await getAcademicYear();
|
|
|
|
|
|
- if (yearInput === null) {
|
|
|
+ if (yearSelection === null) {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
- const year = String(yearInput).trim();
|
|
|
+ const year = String(yearSelection).trim();
|
|
|
|
|
|
- if (typeof window.AndroidBridgePromise.showSingleSelection !== "function") {
|
|
|
- throw new Error("AndroidBridgePromise.showSingleSelection 不可用,请在时光课程表 App 内运行此适配器。");
|
|
|
- }
|
|
|
+ // 再选择学期
|
|
|
+ const semesters = [
|
|
|
+ "1(第一学期)",
|
|
|
+ "2(第二学期)"
|
|
|
+ ];
|
|
|
|
|
|
- const semesters = ["1(第一学期)", "2(第二学期)"];
|
|
|
- const defaultSemesterIndex = defaultSemester === "2" ? 1 : 0;
|
|
|
+ const [, defaultSemester] = ACAD_YEAR.split("-");
|
|
|
|
|
|
- const semesterIndex = await window.AndroidBridgePromise.showSingleSelection(
|
|
|
- "选择学期",
|
|
|
- JSON.stringify(semesters),
|
|
|
- defaultSemesterIndex
|
|
|
- );
|
|
|
+ const semesterIndex =
|
|
|
+ await window.shiguangBridgePromise.showSingleSelection(
|
|
|
+ "选择学期",
|
|
|
+ JSON.stringify(semesters),
|
|
|
+ defaultSemester === "2" ? 1 : 0
|
|
|
+ );
|
|
|
|
|
|
- if (semesterIndex === null || semesterIndex < 0 || semesterIndex >= semesters.length) {
|
|
|
+ if (
|
|
|
+ semesterIndex === null ||
|
|
|
+ semesterIndex < 0 ||
|
|
|
+ semesterIndex >= semesters.length
|
|
|
+ ) {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
ACAD_YEAR = `${year}-${semesterIndex + 1}`;
|
|
|
+
|
|
|
return ACAD_YEAR;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+// ========================================
|
|
|
// 目标课表时间段
|
|
|
+// ========================================
|
|
|
+
|
|
|
const TIME_SLOTS = [
|
|
|
- { number: 1, startTime: "08:00", endTime: "08:45" },
|
|
|
- { number: 2, startTime: "08:55", endTime: "09:40" },
|
|
|
- { number: 3, startTime: "10:10", endTime: "10:55" },
|
|
|
- { number: 4, startTime: "11:05", endTime: "11:50" },
|
|
|
- { number: 5, startTime: "14:20", endTime: "15:05" },
|
|
|
- { number: 6, startTime: "15:15", endTime: "16:00" },
|
|
|
- { number: 7, startTime: "16:30", endTime: "17:15" },
|
|
|
- { number: 8, startTime: "17:25", endTime: "18:10" },
|
|
|
- { number: 9, startTime: "19:00", endTime: "19:45" },
|
|
|
- { number: 10, startTime: "19:55", endTime: "20:40" },
|
|
|
- { number: 11, startTime: "20:50", endTime: "21:35" }
|
|
|
+ {
|
|
|
+ number: 1,
|
|
|
+ startTime: "08:00",
|
|
|
+ endTime: "08:45"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ number: 2,
|
|
|
+ startTime: "08:55",
|
|
|
+ endTime: "09:40"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ number: 3,
|
|
|
+ startTime: "10:10",
|
|
|
+ endTime: "10:55"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ number: 4,
|
|
|
+ startTime: "11:05",
|
|
|
+ endTime: "11:50"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ number: 5,
|
|
|
+ startTime: "14:20",
|
|
|
+ endTime: "15:05"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ number: 6,
|
|
|
+ startTime: "15:15",
|
|
|
+ endTime: "16:00"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ number: 7,
|
|
|
+ startTime: "16:30",
|
|
|
+ endTime: "17:15"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ number: 8,
|
|
|
+ startTime: "17:25",
|
|
|
+ endTime: "18:10"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ number: 9,
|
|
|
+ startTime: "19:00",
|
|
|
+ endTime: "19:45"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ number: 10,
|
|
|
+ startTime: "19:55",
|
|
|
+ endTime: "20:40"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ number: 11,
|
|
|
+ startTime: "20:50",
|
|
|
+ endTime: "21:35"
|
|
|
+ }
|
|
|
];
|
|
|
|
|
|
-// 课表配置(学期开始日期需要根据所选学期调整)
|
|
|
+
|
|
|
+// ========================================
|
|
|
+// 课表配置
|
|
|
+// ========================================
|
|
|
+
|
|
|
const CONFIG_BASE = {
|
|
|
semesterTotalWeeks: 20,
|
|
|
defaultClassDuration: 45,
|
|
|
defaultBreakDuration: 10
|
|
|
};
|
|
|
|
|
|
-function getSemesterStartDate() {
|
|
|
- const startDate = SEMESTER_START_DATES[ACAD_YEAR];
|
|
|
+
|
|
|
+// ========================================
|
|
|
+// 自动获取学期实际开学日期
|
|
|
+// ========================================
|
|
|
+
|
|
|
+async function fetchSemesterStartDate() {
|
|
|
+ const url =
|
|
|
+ `${CALENDAR_API_URL}?academicYear=${encodeURIComponent(ACAD_YEAR)}` +
|
|
|
+ `&weekly=1&_t=${Math.floor(Date.now() / 1000)}`;
|
|
|
+
|
|
|
+ const response = await fetch(url, {
|
|
|
+ method: "GET",
|
|
|
+ credentials: "include"
|
|
|
+ });
|
|
|
+
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error(
|
|
|
+ `获取学期开始日期失败:HTTP ${response.status}`
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ const json = await response.json();
|
|
|
+ const startDate = json?.data?.startTime;
|
|
|
|
|
|
if (!startDate) {
|
|
|
- throw new Error(`暂未配置 ${ACAD_YEAR} 的实际开课日期,请根据中山大学官方校历更新 SEMESTER_START_DATES。`);
|
|
|
+ throw new Error(
|
|
|
+ `教务系统未返回 ${ACAD_YEAR} 第 1 周的开始日期。`
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
return startDate;
|
|
|
}
|
|
|
|
|
|
-function buildCourseConfig() {
|
|
|
+
|
|
|
+async function buildCourseConfig() {
|
|
|
+ const semesterStartDate =
|
|
|
+ await fetchSemesterStartDate();
|
|
|
+
|
|
|
return {
|
|
|
- semesterStartDate: getSemesterStartDate(),
|
|
|
+ semesterStartDate,
|
|
|
...CONFIG_BASE
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+// 请求课程数据
|
|
|
+
|
|
|
async function fetchCourses() {
|
|
|
- console.log("========================================");
|
|
|
- console.log("开始请求中山大学教务系统 API...");
|
|
|
- console.log("API:", API_URL);
|
|
|
- console.log("学期:", ACAD_YEAR);
|
|
|
- console.log("========================================");
|
|
|
+ const url = `${API_URL}?_t=${Date.now()}`;
|
|
|
+ const payload = {
|
|
|
+ acadYear: ACAD_YEAR,
|
|
|
+ submitFlag: "1",
|
|
|
+ nothroughCourseFlag: "1"
|
|
|
+ };
|
|
|
|
|
|
- const response = await fetch(`${API_URL}?_t=${Date.now()}`, {
|
|
|
+ const response = await fetch(url, {
|
|
|
method: "POST",
|
|
|
headers: {
|
|
|
"Content-Type": "application/json"
|
|
|
},
|
|
|
credentials: "include",
|
|
|
- body: JSON.stringify({
|
|
|
- acadYear: ACAD_YEAR,
|
|
|
- submitFlag: "1",
|
|
|
- nothroughCourseFlag: "1"
|
|
|
- })
|
|
|
+ body: JSON.stringify(payload)
|
|
|
});
|
|
|
|
|
|
- console.log("HTTP 状态码:", response.status);
|
|
|
-
|
|
|
const responseText = await response.text();
|
|
|
|
|
|
if (!response.ok) {
|
|
|
- throw new Error(`API 请求失败:HTTP ${response.status}\n${responseText}`);
|
|
|
+ throw new Error(
|
|
|
+ `API 请求失败:HTTP ${response.status}\n${responseText}`
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
let json;
|
|
|
+
|
|
|
try {
|
|
|
json = JSON.parse(responseText);
|
|
|
- } catch (error) {
|
|
|
- console.error("响应不是合法 JSON:", error);
|
|
|
- console.error("原始响应:", responseText);
|
|
|
- return [];
|
|
|
+ } catch {
|
|
|
+ throw new Error("教务系统返回的课程数据不是有效的 JSON。\n");
|
|
|
}
|
|
|
|
|
|
if (json?.code !== 200) {
|
|
|
- throw new Error(`API 返回异常 code:${json?.code}`);
|
|
|
+ throw new Error(
|
|
|
+ `API 返回异常 code:${json?.code}`
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
const timetable = json?.data?.timetable;
|
|
|
|
|
|
- if (!timetable || typeof timetable !== "object" || Array.isArray(timetable)) {
|
|
|
- throw new Error("API 返回中不存在有效的 data.timetable");
|
|
|
+ if (
|
|
|
+ !timetable ||
|
|
|
+ typeof timetable !== "object" ||
|
|
|
+ Array.isArray(timetable)
|
|
|
+ ) {
|
|
|
+ throw new Error(
|
|
|
+ "API 返回中不存在有效的 data.timetable"
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
const courses = [];
|
|
|
|
|
|
+ // timetable 的 key:
|
|
|
+ // 第一位 = 星期
|
|
|
+ // 后两位 = 起始节次
|
|
|
+ //
|
|
|
+ // 例如:
|
|
|
+ // 11 -> 周一第1节
|
|
|
+ // 13 -> 周一第3节
|
|
|
+ // 21 -> 周二第1节
|
|
|
+
|
|
|
for (const entries of Object.values(timetable)) {
|
|
|
if (!Array.isArray(entries) || entries.length === 0) {
|
|
|
continue;
|
|
|
@@ -158,28 +270,28 @@ async function fetchCourses() {
|
|
|
|
|
|
for (const item of entries) {
|
|
|
const course = normalizeCourse(item);
|
|
|
+
|
|
|
if (course) {
|
|
|
courses.push(course);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 自动获取学期配置
|
|
|
+ const config = await buildCourseConfig();
|
|
|
+
|
|
|
const result = {
|
|
|
courses,
|
|
|
timeSlots: TIME_SLOTS,
|
|
|
- config: buildCourseConfig()
|
|
|
+ config
|
|
|
};
|
|
|
|
|
|
- console.log("========================================");
|
|
|
- console.log(`课程转换完成,共 ${courses.length} 条记录`);
|
|
|
- console.log("最终 JSON:");
|
|
|
- console.log(result);
|
|
|
- console.table(courses);
|
|
|
- console.log("========================================");
|
|
|
-
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+// 课程数据转换
|
|
|
+
|
|
|
function normalizeCourse(item) {
|
|
|
if (!item || typeof item !== "object") {
|
|
|
return null;
|
|
|
@@ -194,174 +306,317 @@ function normalizeCourse(item) {
|
|
|
const startWeek = toNumber(item.startWeek);
|
|
|
const weeks = parseWeeks(item.timeDetail, startWeek);
|
|
|
|
|
|
- if (!name || !day || !startSection || !endSection || weeks.length === 0) {
|
|
|
- console.warn("跳过字段不完整的课程记录:", item);
|
|
|
+ if (
|
|
|
+ !name ||
|
|
|
+ !day ||
|
|
|
+ !startSection ||
|
|
|
+ !endSection ||
|
|
|
+ weeks.length === 0
|
|
|
+ ) {
|
|
|
+ console.warn(
|
|
|
+ "跳过字段不完整的课程记录:",
|
|
|
+ item
|
|
|
+ );
|
|
|
+
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
id: crypto.randomUUID(),
|
|
|
+
|
|
|
name,
|
|
|
+
|
|
|
teacher,
|
|
|
+
|
|
|
position,
|
|
|
+
|
|
|
day,
|
|
|
+
|
|
|
startSection,
|
|
|
+
|
|
|
endSection,
|
|
|
- color: getCourseColor(name),
|
|
|
+
|
|
|
weeks
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+// 清理课程名称
|
|
|
+
|
|
|
function cleanCourseName(value) {
|
|
|
const text = cleanText(value);
|
|
|
|
|
|
- // 去掉中山大学 API 返回的课程类别前缀:
|
|
|
- // "本(专必)高等数学一(I)" -> "高等数学一(I)"
|
|
|
- // "本(公必)劳动教育" -> "劳动教育"
|
|
|
- return text.replace(/^本\([^)]*\)/, "").trim();
|
|
|
+ // 去掉中山大学 API 返回的课程类别前缀
|
|
|
+ //
|
|
|
+ // 本(专必)高等数学一(I)
|
|
|
+ // ↓
|
|
|
+ // 高等数学一(I)
|
|
|
+ //
|
|
|
+ // 本(公必)劳动教育
|
|
|
+ // ↓
|
|
|
+ // 劳动教育
|
|
|
+
|
|
|
+ return text
|
|
|
+ .replace(/^本\([^)]*\)/, "")
|
|
|
+ .trim();
|
|
|
}
|
|
|
|
|
|
-function parseWeeks(timeDetail, startWeek = 1) {
|
|
|
- const text = cleanText(timeDetail);
|
|
|
+
|
|
|
+// 解析上课周次
|
|
|
+
|
|
|
+function parseWeeks(
|
|
|
+ timeDetail,
|
|
|
+ startWeek = 1
|
|
|
+) {
|
|
|
+ const text =
|
|
|
+ cleanText(timeDetail);
|
|
|
|
|
|
if (!text) {
|
|
|
- return startWeek ? [startWeek] : [];
|
|
|
+ return startWeek
|
|
|
+ ? [startWeek]
|
|
|
+ : [];
|
|
|
}
|
|
|
|
|
|
- // 例如:"1-17每周"
|
|
|
- const rangeMatch = text.match(/(\d+)\s*-\s*(\d+)/);
|
|
|
+ // 例如:
|
|
|
+ // 1-17每周
|
|
|
+
|
|
|
+ const rangeMatch =
|
|
|
+ text.match(/(\d+)\s*-\s*(\d+)/);
|
|
|
+
|
|
|
if (rangeMatch) {
|
|
|
- const start = Number(rangeMatch[1]);
|
|
|
- const end = Number(rangeMatch[2]);
|
|
|
+ const start =
|
|
|
+ Number(rangeMatch[1]);
|
|
|
+
|
|
|
+ const end =
|
|
|
+ Number(rangeMatch[2]);
|
|
|
|
|
|
- if (Number.isFinite(start) && Number.isFinite(end) && end >= start) {
|
|
|
+ if (
|
|
|
+ Number.isFinite(start) &&
|
|
|
+ Number.isFinite(end) &&
|
|
|
+ end >= start
|
|
|
+ ) {
|
|
|
return Array.from(
|
|
|
- { length: end - start + 1 },
|
|
|
- (_, index) => start + index
|
|
|
+ {
|
|
|
+ length:
|
|
|
+ end - start + 1
|
|
|
+ },
|
|
|
+ (_, index) =>
|
|
|
+ start + index
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 兼容单双周或离散周次,例如:"1,3,5周"、"1、3、5周"
|
|
|
- const numbers = text
|
|
|
- .match(/\d+/g)
|
|
|
- ?.map(Number)
|
|
|
- .filter(Number.isFinite) || [];
|
|
|
+ // 兼容:
|
|
|
+ //
|
|
|
+ // 1,3,5周
|
|
|
+ // 1、3、5周
|
|
|
+
|
|
|
+ const numbers =
|
|
|
+ text
|
|
|
+ .match(/\d+/g)
|
|
|
+ ?.map(Number)
|
|
|
+ .filter(Number.isFinite) || [];
|
|
|
|
|
|
if (numbers.length > 0) {
|
|
|
- return [...new Set(numbers)].sort((a, b) => a - b);
|
|
|
+ return [
|
|
|
+ ...new Set(numbers)
|
|
|
+ ].sort(
|
|
|
+ (a, b) => a - b
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- return startWeek ? [startWeek] : [];
|
|
|
+ return startWeek
|
|
|
+ ? [startWeek]
|
|
|
+ : [];
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+// 清理字符串
|
|
|
+
|
|
|
function cleanText(value) {
|
|
|
if (value === null || value === undefined) {
|
|
|
return "";
|
|
|
}
|
|
|
|
|
|
- return String(value)
|
|
|
- .replace(/\/+$/g, "")
|
|
|
- .trim();
|
|
|
+ return String(value).replace(/\/+$/g, "").trim();
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+// 数字转换
|
|
|
+
|
|
|
function toNumber(value) {
|
|
|
const number = Number(value);
|
|
|
return Number.isFinite(number) ? number : null;
|
|
|
}
|
|
|
|
|
|
-// 根据课程名称生成稳定的颜色编号,避免同一课程每次导出颜色变化。
|
|
|
-function getCourseColor(name) {
|
|
|
- const colorCount = 8;
|
|
|
- let hash = 0;
|
|
|
|
|
|
- for (let i = 0; i < name.length; i++) {
|
|
|
- hash = ((hash << 5) - hash) + name.charCodeAt(i);
|
|
|
- hash |= 0;
|
|
|
- }
|
|
|
|
|
|
- return Math.abs(hash) % colorCount + 1;
|
|
|
-}
|
|
|
+
|
|
|
+
|
|
|
+// 主导入流程
|
|
|
|
|
|
async function runImportFlow() {
|
|
|
try {
|
|
|
- if (!window.AndroidBridgePromise || typeof window.AndroidBridgePromise.showAlert !== "function") {
|
|
|
- throw new Error("AndroidBridgePromise.showAlert 不可用,请在时光课程表 App 内运行此适配器。");
|
|
|
+ // 提示用户选择学期
|
|
|
+
|
|
|
+ if (
|
|
|
+ window.shiguangBridge &&
|
|
|
+ typeof window.shiguangBridge.showToast ===
|
|
|
+ "function"
|
|
|
+ ) {
|
|
|
+ window.shiguangBridge.showToast(
|
|
|
+ "请选择要导入的学期..."
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- if (!confirmed) {
|
|
|
- if (window.AndroidBridge && typeof window.AndroidBridge.showToast === "function") {
|
|
|
- window.AndroidBridge.showToast("已取消导入。");
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
+ const selectedSemester =
|
|
|
+ await selectSemester();
|
|
|
|
|
|
- const selectedSemester = await selectSemester();
|
|
|
if (!selectedSemester) {
|
|
|
- if (window.AndroidBridge && typeof window.AndroidBridge.showToast === "function") {
|
|
|
- window.AndroidBridge.showToast("已取消导入。");
|
|
|
+ if (
|
|
|
+ window.shiguangBridge &&
|
|
|
+ typeof window.shiguangBridge.showToast ===
|
|
|
+ "function"
|
|
|
+ ) {
|
|
|
+ window.shiguangBridge.showToast(
|
|
|
+ "已取消导入。"
|
|
|
+ );
|
|
|
}
|
|
|
+
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- console.log(`已选择学期:${ACAD_YEAR}`);
|
|
|
- console.log(`学期实际开课日期:${getSemesterStartDate()}`);
|
|
|
+ // 获取课程
|
|
|
|
|
|
- const data = await fetchCourses();
|
|
|
- if (!data || !Array.isArray(data.courses)) {
|
|
|
- throw new Error("课程数据为空或格式不正确。");
|
|
|
- }
|
|
|
+ const data =
|
|
|
+ await fetchCourses();
|
|
|
|
|
|
- window.__SYSU_COURSE_JSON__ = data;
|
|
|
- window.__SYSU_COURSES__ = data.courses;
|
|
|
+ if (
|
|
|
+ !data ||
|
|
|
+ !Array.isArray(data.courses)
|
|
|
+ ) {
|
|
|
+ throw new Error(
|
|
|
+ "课程数据为空或格式不正确。"
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
- console.log("window.__SYSU_COURSE_JSON__ 已更新。");
|
|
|
- console.log("准备通过 AndroidBridgePromise 向应用提交数据。");
|
|
|
+ // 检查 V2 Bridge
|
|
|
|
|
|
- if (!window.AndroidBridgePromise) {
|
|
|
- throw new Error("AndroidBridgePromise 不可用,请在时光课程表 App 内运行此适配器。");
|
|
|
+ if (
|
|
|
+ !window.shiguangBridgePromise
|
|
|
+ ) {
|
|
|
+ throw new Error(
|
|
|
+ "shiguangBridgePromise 不可用,请在时光课程表 App 内运行此适配器。"
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- if (typeof window.AndroidBridgePromise.saveImportedCourses !== "function") {
|
|
|
- throw new Error("saveImportedCourses API 不可用。");
|
|
|
+ if (
|
|
|
+ typeof window.shiguangBridgePromise
|
|
|
+ .saveImportedCourses !==
|
|
|
+ "function"
|
|
|
+ ) {
|
|
|
+ throw new Error(
|
|
|
+ "saveImportedCourses API 不可用。"
|
|
|
+ );
|
|
|
}
|
|
|
- if (typeof window.AndroidBridgePromise.savePresetTimeSlots !== "function") {
|
|
|
- throw new Error("savePresetTimeSlots API 不可用。");
|
|
|
+
|
|
|
+ if (
|
|
|
+ typeof window.shiguangBridgePromise
|
|
|
+ .savePresetTimeSlots !==
|
|
|
+ "function"
|
|
|
+ ) {
|
|
|
+ throw new Error(
|
|
|
+ "savePresetTimeSlots API 不可用。"
|
|
|
+ );
|
|
|
}
|
|
|
- if (typeof window.AndroidBridgePromise.saveCourseConfig !== "function") {
|
|
|
- throw new Error("saveCourseConfig API 不可用。");
|
|
|
+
|
|
|
+ if (
|
|
|
+ typeof window.shiguangBridgePromise
|
|
|
+ .saveCourseConfig !==
|
|
|
+ "function"
|
|
|
+ ) {
|
|
|
+ throw new Error(
|
|
|
+ "saveCourseConfig API 不可用。"
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- if (window.AndroidBridge && typeof window.AndroidBridge.showToast === "function") {
|
|
|
- window.AndroidBridge.showToast(`正在导入 ${data.courses.length} 条课程记录...`);
|
|
|
+ // 开始导入
|
|
|
+
|
|
|
+ if (
|
|
|
+ window.shiguangBridge &&
|
|
|
+ typeof window.shiguangBridge.showToast ===
|
|
|
+ "function"
|
|
|
+ ) {
|
|
|
+ window.shiguangBridge.showToast(
|
|
|
+ `正在导入 ${data.courses.length} 条课程记录...`
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(data.courses));
|
|
|
- console.log("课程数据提交成功。");
|
|
|
+ // 保存课程
|
|
|
+
|
|
|
+ await window.shiguangBridgePromise
|
|
|
+ .saveImportedCourses(
|
|
|
+ JSON.stringify(
|
|
|
+ data.courses
|
|
|
+ )
|
|
|
+ );
|
|
|
+
|
|
|
+ // 保存时间段
|
|
|
+
|
|
|
+ await window.shiguangBridgePromise
|
|
|
+ .savePresetTimeSlots(
|
|
|
+ JSON.stringify(
|
|
|
+ data.timeSlots
|
|
|
+ )
|
|
|
+ );
|
|
|
+
|
|
|
+ // 保存课表配置
|
|
|
|
|
|
- await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(data.timeSlots));
|
|
|
- console.log("时间段数据提交成功。");
|
|
|
+ await window.shiguangBridgePromise
|
|
|
+ .saveCourseConfig(
|
|
|
+ JSON.stringify(
|
|
|
+ data.config
|
|
|
+ )
|
|
|
+ );
|
|
|
|
|
|
- await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify(data.config));
|
|
|
- console.log("课表配置提交成功。");
|
|
|
+ // 导入完成
|
|
|
|
|
|
- if (window.AndroidBridge && typeof window.AndroidBridge.showToast === "function") {
|
|
|
- window.AndroidBridge.showToast(`成功导入 ${data.courses.length} 条课程记录!`);
|
|
|
+ if (
|
|
|
+ window.shiguangBridge &&
|
|
|
+ typeof window.shiguangBridge.showToast ===
|
|
|
+ "function"
|
|
|
+ ) {
|
|
|
+ window.shiguangBridge.showToast(
|
|
|
+ `成功导入 ${data.courses.length} 条课程记录!`
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- if (window.AndroidBridge && typeof window.AndroidBridge.notifyTaskCompletion === "function") {
|
|
|
- window.AndroidBridge.notifyTaskCompletion();
|
|
|
+ // 通知 App 导入流程结束
|
|
|
+
|
|
|
+ if (
|
|
|
+ window.shiguangBridge &&
|
|
|
+ typeof window.shiguangBridge
|
|
|
+ .notifyTaskCompletion ===
|
|
|
+ "function"
|
|
|
+ ) {
|
|
|
+ window.shiguangBridge
|
|
|
+ .notifyTaskCompletion();
|
|
|
}
|
|
|
- } catch (error) {
|
|
|
- console.error("========================================");
|
|
|
- console.error("中山大学课表导入失败:", error);
|
|
|
- console.error(error.stack);
|
|
|
- console.error("========================================");
|
|
|
|
|
|
- if (window.AndroidBridge && typeof window.AndroidBridge.showToast === "function") {
|
|
|
- window.AndroidBridge.showToast(`导入失败:${error.message}`);
|
|
|
+ } catch (error) {
|
|
|
+ if (
|
|
|
+ window.shiguangBridge &&
|
|
|
+ typeof window.shiguangBridge.showToast ===
|
|
|
+ "function"
|
|
|
+ ) {
|
|
|
+ window.shiguangBridge.showToast(
|
|
|
+ `导入失败:${error.message}`
|
|
|
+ );
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+// 启动
|
|
|
+
|
|
|
runImportFlow();
|