|
@@ -0,0 +1,464 @@
|
|
|
|
|
+// 文件: school.js
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 显示导入提示
|
|
|
|
|
+ */
|
|
|
|
|
+async function promptUserToStart() {
|
|
|
|
|
+ const confirmed = await window.AndroidBridgePromise.showAlert(
|
|
|
|
|
+ "导入提示",
|
|
|
|
|
+ "导入前请确保您已进入课表页面(运行->课表查询->我的课表)并等待页面加载完成",
|
|
|
|
|
+ "开始导入"
|
|
|
|
|
+ );
|
|
|
|
|
+ if (!confirmed) {
|
|
|
|
|
+ AndroidBridge.showToast("用户取消了导入");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ AndroidBridge.showToast("开始获取课表数据...");
|
|
|
|
|
+ return true;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 获取 iframe 内容
|
|
|
|
|
+ */
|
|
|
|
|
+function getIframeDocument() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ console.log('开始获取 iframe 内容');
|
|
|
|
|
+
|
|
|
|
|
+ // 尝试多种选择器找到 iframe
|
|
|
|
|
+ const selectors = [
|
|
|
|
|
+ '.iframe___1hsk7',
|
|
|
|
|
+ '[class*="iframe"]',
|
|
|
|
|
+ 'iframe'
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ let iframe = null;
|
|
|
|
|
+ for (const selector of selectors) {
|
|
|
|
|
+ iframe = document.querySelector(selector);
|
|
|
|
|
+ if (iframe) {
|
|
|
|
|
+ console.log(`通过选择器 "${selector}" 找到 iframe`);
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!iframe) {
|
|
|
|
|
+ console.error('未找到 iframe 元素');
|
|
|
|
|
+ AndroidBridge.showToast("未找到课表框架,请确保在课表页面");
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取 iframe 的 document
|
|
|
|
|
+ const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
|
|
|
|
+
|
|
|
|
|
+ if (!iframeDoc) {
|
|
|
|
|
+ console.error('无法访问 iframe 内容');
|
|
|
|
|
+ AndroidBridge.showToast("无法访问课表内容,可能页面未加载完成");
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查是否包含课表元素
|
|
|
|
|
+ const timetable = iframeDoc.querySelector('.kbappTimetableDayColumnRoot');
|
|
|
|
|
+ if (!timetable) {
|
|
|
|
|
+ console.warn('iframe 中未找到课表元素,可能不在课表页面');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return iframeDoc;
|
|
|
|
|
+
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('获取 iframe 内容时出错:', error);
|
|
|
|
|
+ AndroidBridge.showToast(`获取课表失败: ${error.message}`);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function getSectionByPosition(element) {
|
|
|
|
|
+ const dayColumn = element.closest('.kbappTimetableDayColumnRoot');
|
|
|
|
|
+ const dayCols = Array.from(dayColumn.parentNode.children);
|
|
|
|
|
+ const day = dayCols.indexOf(dayColumn) + 1;
|
|
|
|
|
+
|
|
|
|
|
+ let slotBlock = element;
|
|
|
|
|
+ while (slotBlock.parentElement && slotBlock.parentElement !== dayColumn) {
|
|
|
|
|
+ slotBlock = slotBlock.parentElement;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const win = element.ownerDocument.defaultView || window;
|
|
|
|
|
+ const getFlex = (el) => {
|
|
|
|
|
+ const fg = win.getComputedStyle(el).flexGrow;
|
|
|
|
|
+ return Math.round(parseFloat(fg || 0));
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ let previousFlexSum = 0;
|
|
|
|
|
+ let curr = slotBlock.previousElementSibling;
|
|
|
|
|
+ while (curr) {
|
|
|
|
|
+ previousFlexSum += getFlex(curr);
|
|
|
|
|
+ curr = curr.previousElementSibling;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const currentFlex = getFlex(slotBlock);
|
|
|
|
|
+
|
|
|
|
|
+ // 换算
|
|
|
|
|
+ let start = previousFlexSum + 1;
|
|
|
|
|
+ let end = start + Math.max(1, currentFlex) - 1;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // 午餐晚餐修正节数
|
|
|
|
|
+ if (start >= 10) {
|
|
|
|
|
+ start -= 2;
|
|
|
|
|
+ end -= 2;
|
|
|
|
|
+ } else if (start > 5) {
|
|
|
|
|
+ start -= 1;
|
|
|
|
|
+ end -= 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return { day, start, end };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 解析时间段数据
|
|
|
|
|
+ */
|
|
|
|
|
+function parseTimeSlots(iframeDoc) {
|
|
|
|
|
+ const timeSlots = [];
|
|
|
|
|
+
|
|
|
|
|
+ // 查找时间段列
|
|
|
|
|
+ const timeColumn = iframeDoc.querySelector('.kbappTimetableJcColumn');
|
|
|
|
|
+
|
|
|
|
|
+ const timeItems = timeColumn.querySelectorAll('.kbappTimetableJcItem');
|
|
|
|
|
+ console.log('找到时间段数量:', timeItems.length);
|
|
|
|
|
+
|
|
|
|
|
+ timeItems.forEach((item, index) => {
|
|
|
|
|
+ const textElements = item.querySelectorAll('.kbappTimetableJcItemText');
|
|
|
|
|
+ if (textElements.length >= 2) {
|
|
|
|
|
+ const sectionName = textElements[0]?.textContent?.trim() || `第${index + 1}节`;
|
|
|
|
|
+ const timeRange = textElements[1]?.textContent?.trim() || '';
|
|
|
|
|
+
|
|
|
|
|
+ // 解析时间范围
|
|
|
|
|
+ const timeMatch = timeRange.match(/(\d{2}:\d{2})[~-](\d{2}:\d{2})/);
|
|
|
|
|
+ if (timeMatch) {
|
|
|
|
|
+ const startTime = timeMatch[1];
|
|
|
|
|
+ const endTime = timeMatch[2];
|
|
|
|
|
+
|
|
|
|
|
+ // 提取节次数字
|
|
|
|
|
+ const sectionMatch = sectionName.match(/第(\d+)节/);
|
|
|
|
|
+ const sectionNumber = sectionMatch ? parseInt(sectionMatch[1]) : index + 1;
|
|
|
|
|
+
|
|
|
|
|
+ timeSlots.push({
|
|
|
|
|
+ number: sectionNumber,
|
|
|
|
|
+ startTime: startTime,
|
|
|
|
|
+ endTime: endTime
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ console.log(`时间段 ${sectionNumber}: ${startTime} ~ ${endTime}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return timeSlots;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 解析周次信息
|
|
|
|
|
+ */
|
|
|
|
|
+function parseWeeks(text) {
|
|
|
|
|
+ const weeks = [];
|
|
|
|
|
+ // 匹配如 1-16, 1, 3, 5-7 等模式
|
|
|
|
|
+ const patterns = text.match(/(\d+)-(\d+)周|(\d+)周/g);
|
|
|
|
|
+ if (!patterns) return weeks;
|
|
|
|
|
+
|
|
|
|
|
+ const isSingle = text.includes('(单)');
|
|
|
|
|
+ const isDouble = text.includes('(双)');
|
|
|
|
|
+
|
|
|
|
|
+ patterns.forEach(p => {
|
|
|
|
|
+ const range = p.match(/(\d+)-(\d+)/);
|
|
|
|
|
+ if (range) {
|
|
|
|
|
+ const start = parseInt(range[1]);
|
|
|
|
|
+ const end = parseInt(range[2]);
|
|
|
|
|
+ for (let i = start; i <= end; i++) {
|
|
|
|
|
+ if (isSingle && i % 2 === 0) continue;
|
|
|
|
|
+ if (isDouble && i % 2 !== 0) continue;
|
|
|
|
|
+ weeks.push(i);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const single = p.match(/(\d+)/);
|
|
|
|
|
+ if (single) weeks.push(parseInt(single[1]));
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ return weeks;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 解析单个课程信息
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+// 这里源数据使用了el - popover 和el - popover__reference两种模式 一种是弹窗还要一种是课程块
|
|
|
|
|
+// 我这里解析就只用了第一种popover 因为显示的数据精简 直接可以使用
|
|
|
|
|
+
|
|
|
|
|
+function parseSingleCourse(courseElement, day, timeSlots) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const infoTexts = courseElement.querySelectorAll('.kbappTimetableCourseRenderCourseItemInfoText');
|
|
|
|
|
+ if (infoTexts.length < 2) return null;
|
|
|
|
|
+
|
|
|
|
|
+ // 课程名称
|
|
|
|
|
+ let nameElement = courseElement.querySelector('.kbappTimetableCourseRenderCourseItemName');
|
|
|
|
|
+ let rawName = nameElement ? nameElement.innerText.trim() : courseElement.innerText.split('\n')[0].trim();
|
|
|
|
|
+ let name = rawName.replace(/\[.*?\]/g, "").replace(/\s+\d+$/, "").trim();
|
|
|
|
|
+ if (name === "未知课程" || !name) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 获取持续时间
|
|
|
|
|
+ const duration = parseInt(courseElement.getAttribute('data-scales-span') || '1');
|
|
|
|
|
+
|
|
|
|
|
+ // 计算起始节次
|
|
|
|
|
+ let startSection = 1;
|
|
|
|
|
+ const parent = courseElement.closest('.kbappTimetableCourseRenderColumn');
|
|
|
|
|
+ if (parent) {
|
|
|
|
|
+ const containers = parent.querySelectorAll('.kbappTimetableCourseRenderCourseItemContainer');
|
|
|
|
|
+ for (let i = 0; i < containers.length; i++) {
|
|
|
|
|
+ const container = containers[i];
|
|
|
|
|
+ const courseInContainer = container.querySelector('.kbappTimetableCourseRenderCourseItem');
|
|
|
|
|
+ if (courseInContainer === courseElement) {
|
|
|
|
|
+ const flexMatch = container.style.flex?.match(/(\d+)/);
|
|
|
|
|
+ if (flexMatch) {
|
|
|
|
|
+ let totalPrevSpan = 0;
|
|
|
|
|
+ for (let j = 0; j < i; j++) {
|
|
|
|
|
+ const prevFlex = containers[j].style.flex?.match(/(\d+)/);
|
|
|
|
|
+ if (prevFlex) {
|
|
|
|
|
+ totalPrevSpan += parseInt(prevFlex[1]);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ startSection = totalPrevSpan + 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 计算结束节次
|
|
|
|
|
+ const endSection = startSection + duration - 1;
|
|
|
|
|
+
|
|
|
|
|
+ // 验证范围
|
|
|
|
|
+ const validStart = Math.max(1, Math.min(startSection, timeSlots?.length || 12));
|
|
|
|
|
+ const validEnd = Math.max(validStart, Math.min(endSection, timeSlots?.length || 12));
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ name: name,
|
|
|
|
|
+ teacher: '未知教师', // 暂时用默认值
|
|
|
|
|
+ position: '未知教室', // 暂时用默认值
|
|
|
|
|
+ day: day,
|
|
|
|
|
+ startSection: validStart,
|
|
|
|
|
+ endSection: validEnd,
|
|
|
|
|
+ weeks: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18], // 暂时用默认值
|
|
|
|
|
+ isCustomTime: false
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('解析出错:', error);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 解析课程数据
|
|
|
|
|
+ */
|
|
|
|
|
+function parseCourses(iframeDoc, timeSlots) {
|
|
|
|
|
+ const courses = [];
|
|
|
|
|
+
|
|
|
|
|
+ // 获取所有星期列
|
|
|
|
|
+ const dayColumns = iframeDoc.querySelectorAll('.kbappTimetableDayColumnRoot');
|
|
|
|
|
+ console.log('找到课表列数量:', dayColumns.length);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // 遍历每一天的列
|
|
|
|
|
+ for (let dayIndex = 0; dayIndex < dayColumns.length; dayIndex++) {
|
|
|
|
|
+ const dayColumn = dayColumns[dayIndex];
|
|
|
|
|
+
|
|
|
|
|
+ // 获取当天的所有课程
|
|
|
|
|
+ const dayCourses = dayColumn.querySelectorAll('.kbappTimetableCourseRenderCourseItem');
|
|
|
|
|
+
|
|
|
|
|
+ console.log(`星期${dayIndex + 1} 课程数量:`, dayCourses.length);
|
|
|
|
|
+
|
|
|
|
|
+ dayCourses.forEach(courseElement => {
|
|
|
|
|
+ const courseInfo = parseSingleCourse(courseElement, dayIndex + 1, timeSlots);
|
|
|
|
|
+ if (courseInfo) {
|
|
|
|
|
+ courses.push(courseInfo);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return courses;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 解析所有数据
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+function parseAllData(iframeDoc) {
|
|
|
|
|
+ const timeSlots = parseTimeSlots(iframeDoc);
|
|
|
|
|
+ const courses = [];
|
|
|
|
|
+ const courseElements = iframeDoc.querySelectorAll('.kbappTimetableCourseRenderCourseItem');
|
|
|
|
|
+
|
|
|
|
|
+ courseElements.forEach(element => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const popoverId = element.getAttribute('aria-describedby');
|
|
|
|
|
+ const popover = iframeDoc.getElementById(popoverId);
|
|
|
|
|
+ if (!popover) return;
|
|
|
|
|
+
|
|
|
|
|
+ const nameElement = popover.querySelector('.kbappTimetableCourseRenderCourseItemInfoPopperInfo');
|
|
|
|
|
+ const name = nameElement ? nameElement.textContent.trim().replace(/\[.*?\]/g, "") : "";
|
|
|
|
|
+ if (!name) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 获取位置信息
|
|
|
|
|
+ const sectionInfo = getSectionByPosition(element);
|
|
|
|
|
+
|
|
|
|
|
+ // --- 关键修正:获取所有信息行 (处理单双周不同行的情况) ---
|
|
|
|
|
+ const infoItems = Array.from(popover.querySelectorAll('.kbappTimetableCourseRenderCourseItemInfoPopperInfo')).slice(1);
|
|
|
|
|
+
|
|
|
|
|
+ infoItems.forEach(item => {
|
|
|
|
|
+ const detailStr = item.textContent.trim();
|
|
|
|
|
+ if (!detailStr) return;
|
|
|
|
|
+
|
|
|
|
|
+ const parts = detailStr.split(/\s+/).filter(p => p.length > 0);
|
|
|
|
|
+ let teacher = "未知教师";
|
|
|
|
|
+ let posParts = [];
|
|
|
|
|
+ let currentWeeks = parseWeeks(detailStr);
|
|
|
|
|
+
|
|
|
|
|
+ parts.forEach(p => {
|
|
|
|
|
+ if (p.includes('周')) return;
|
|
|
|
|
+ // 老师判定:2-4个字且不含地点特征词
|
|
|
|
|
+ if (/^[\u4e00-\u9fa5]{2,4}$/.test(p) && !/(楼|校区|室|场|馆|中心)/.test(p)) {
|
|
|
|
|
+ teacher = p;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ posParts.push(p);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 地点去重:选最长的描述
|
|
|
|
|
+ let position = posParts.sort((a, b) => b.length - a.length)[0] || "未知教室";
|
|
|
|
|
+
|
|
|
|
|
+ courses.push({
|
|
|
|
|
+ name: name,
|
|
|
|
|
+ teacher: teacher,
|
|
|
|
|
+ position: position,
|
|
|
|
|
+ day: sectionInfo.day,
|
|
|
|
|
+ startSection: sectionInfo.start,
|
|
|
|
|
+ endSection: sectionInfo.end,
|
|
|
|
|
+ weeks: currentWeeks
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ } catch (e) { console.error("解析单条课程失败:", e); }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return { courses: removeDuplicates(courses), timeSlots };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 课程去重 后期这里可能会出现问题
|
|
|
|
|
+ */
|
|
|
|
|
+function removeDuplicates(courses) {
|
|
|
|
|
+ const seen = new Set();
|
|
|
|
|
+ return courses.filter(course => {
|
|
|
|
|
+ // 核心唯一键:星期 + 起始节次 + 课程名 + 周次
|
|
|
|
|
+ // 这样即使老师或地点写法稍有不同,只要是同一时间同一门课且周次一致,就会被去重
|
|
|
|
|
+ const key = `${course.day}-${course.startSection}-${course.name}-${course.weeks.join(',')}`;
|
|
|
|
|
+ if (seen.has(key)) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ seen.add(key);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 保存课程数据
|
|
|
|
|
+ */
|
|
|
|
|
+async function saveCourses(parsedData) {
|
|
|
|
|
+ const { courses, timeSlots } = parsedData;
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ AndroidBridge.showToast(`准备保存 ${courses.length} 门课程...`);
|
|
|
|
|
+
|
|
|
|
|
+ // 保存课程数据
|
|
|
|
|
+ const courseSaveResult = await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(courses));
|
|
|
|
|
+ if (!courseSaveResult) {
|
|
|
|
|
+ AndroidBridge.showToast("保存课程失败");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ AndroidBridge.showToast(`成功导入 ${courses.length} 门课程`);
|
|
|
|
|
+
|
|
|
|
|
+ // 保存时间段数据
|
|
|
|
|
+ if (timeSlots && timeSlots.length > 0) {
|
|
|
|
|
+ const timeSlotSaveResult = await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(timeSlots));
|
|
|
|
|
+ if (timeSlotSaveResult) {
|
|
|
|
|
+ AndroidBridge.showToast(`成功导入 ${timeSlots.length} 个时间段`);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ AndroidBridge.showToast("时间段导入失败,课程仍可使用");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return true;
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error("保存课程数据时出错:", error);
|
|
|
|
|
+ AndroidBridge.showToast(`保存失败: ${error.message}`);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 运行主函数
|
|
|
|
|
+ */
|
|
|
|
|
+async function runImportFlow() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ AndroidBridge.showToast("课表导入工具启动...");
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 显示导入提示
|
|
|
|
|
+ const shouldProceed = await promptUserToStart();
|
|
|
|
|
+ if (!shouldProceed) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 等待一下确保页面加载
|
|
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 1000));
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 获取 iframe 内容
|
|
|
|
|
+ const iframeDoc = getIframeDocument();
|
|
|
|
|
+ if (!iframeDoc) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 解析数据
|
|
|
|
|
+ AndroidBridge.showToast("正在解析课表数据...");
|
|
|
|
|
+ const parsedData = parseAllData(iframeDoc);
|
|
|
|
|
+
|
|
|
|
|
+ if (parsedData.courses.length === 0) {
|
|
|
|
|
+ await window.AndroidBridgePromise.showAlert(
|
|
|
|
|
+ "解析失败",
|
|
|
|
|
+ "未找到任何课程数据,请确认:\n1. 已在课表查询页面\n2. 课表已完全加载\n3. 当前学期有课程",
|
|
|
|
|
+ "知道了"
|
|
|
|
|
+ );
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 5. 显示预览
|
|
|
|
|
+ const previewMsg = `找到 ${parsedData.courses.length} 门课程\n${parsedData.timeSlots.length} 个时间段\n\n是否继续导入?`;
|
|
|
|
|
+ const confirmed = await window.AndroidBridgePromise.showAlert(
|
|
|
|
|
+ "导入确认",
|
|
|
|
|
+ previewMsg,
|
|
|
|
|
+ "确认导入"
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ if (!confirmed) {
|
|
|
|
|
+ AndroidBridge.showToast("已取消导入");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 6. 保存数据
|
|
|
|
|
+ const saveSuccess = await saveCourses(parsedData);
|
|
|
|
|
+ if (!saveSuccess) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 7. 完成
|
|
|
|
|
+ AndroidBridge.showToast("课表导入完成!");
|
|
|
|
|
+ AndroidBridge.notifyTaskCompletion();
|
|
|
|
|
+
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error("导入流程出错:", error);
|
|
|
|
|
+ AndroidBridge.showToast(`导入失败: ${error.message}`);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 启动导入流程
|
|
|
|
|
+runImportFlow();
|