Просмотр исходного кода

通过使用Fetch 请求流优化:将原本同步的加载方式改为异步 fetch 获取 CourseTableExport.json,大幅提升了课表预览页面的响应速度。

YuXuann 1 месяц назад
Родитель
Сommit
2f94505949
2 измененных файлов с 311 добавлено и 515 удалено
  1. 3 3
      resources/CAPU/adapters.yaml
  2. 308 512
      resources/CAPU/capadap.js

+ 3 - 3
resources/CAPU/adapters.yaml

@@ -1,9 +1,9 @@
 # resources/CUST/adapters.yaml
 adapters:
   - adapter_id: "CAPU"
-    adapter_name: "成都航空职业技术大学教务"
+    adapter_name: "成都航空职业技术大学金智新教务"
     category: "BACHELOR_AND_ASSOCIATE"
     asset_js_path: "capadap.js"
-    import_url: "https://webvpn.cap.edu.cn/"
+    import_url: "https://jwxt.cap.edu.cn/jwapp/sys/homeapp/home/index.html?av=&contextPath=/jwapp#/"
     maintainer: "Xuan-Xuann"
-    description: "适配成都航空职业技术大学。导入前请确保您已进入课表页面(运行->课表查询->我的课表)有任何问题请通过GitHub/QQ联系开发者。"
+    description: "更新:您登录后直接点击导入即可食用,无需进入课表界面。有任何问题请通过GitHub/QQ联系开发者。"

+ 308 - 512
resources/CAPU/capadap.js

@@ -1,4 +1,6 @@
 // 文件: capadap.js
+//后期可加入接口-获取校区  https://jwxt.cap.edu.cn/jwapp/sys/kbapp/api/wdkbcx/getMyScheduledCampus.do
+
 
 /**
  * 显示导入提示
@@ -6,612 +8,406 @@
 async function promptUserToStart() {
     const confirmed = await window.AndroidBridgePromise.showAlert(
         "导入确认",
-        "导入前请确保您已进入课表页面(运行->课表查询->我的课表)并等待页面加载完成",
+        "请确保您已经登录咯~",
         "开始导入"
     );
     if (!confirmed) {
         AndroidBridge.showToast("用户取消了导入");
         return false;
     }
-    AndroidBridge.showToast("开始获取课表数据...");
+    AndroidBridge.showToast("开始流程咯~");
     return true;
 }
 
 /**
- * 获取 iframe 内容
+ * 请求工具
  */
-function getIframeDocument() {
-    try { 
-        // 尝试多种选择器找到 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;
-    }
+async function api(url, options = {}) {
+    //设置默认值
+    const method = options.method || (options.data ? "POST" : "GET");
+
+    const headers = {
+        "fetch-api": "true",
+        "x-requested-with": "XMLHttpRequest",
+        "Referer": "https://jwxt.cap.edu.cn/jwapp/sys/homeapp/home/index.html",
+        ...(options.data && { "content-type": "application/x-www-form-urlencoded; charset=UTF-8" }),
+        ...options.headers // 允许传入自定义 header 覆盖上面这些
+    };
+    //发起请求
+    const res = await fetch(url, {
+        method: method,
+        headers: headers,
+        body: options.data || null,
+        credentials: "include"
+    });
+
+    return res.json();
 }
 
+//共享变量
+const AppConfig = {
+    currentSemester: null,
+    postData: null,
+};
+
 /**
- * 解析开学时间
+ * 提取上课时间   开学时间   课程周数
  */
-function extractStartDate() {
-    const iframdate = getIframeDocument();
-    if (!iframdate) return null;
-
-    try {
-        const dayElement = iframdate.querySelector('.kbappTimeZCText');  //<div class="kbappTimeZCText">第1周(3/9 ~ 3/15)</div>
-        const semesterElement = iframdate.querySelector('.kbappTimeXQText');  //<div class="kbappTimeXQText">2025-2026学年 第2学期</div>
-        if (!dayElement || !semesterElement) {
-            return null;
-        }
-        const dayText = dayElement.textContent.trim();  //  第1周(3/9 ~ 3/15)
-        const semesterText = semesterElement.textContent.trim();  //  2025-2026学年 第2学期  
-        const startDate = parseStartDate(dayText, semesterText);
-        // 要判断是第几学期来选择开学年
-
-        return {startDate};  //传入解析后数据
+async function extractCourseTime() { 
+
+    try {  //上课时间
+        const userRes = await api("https://jwxt.cap.edu.cn/jwapp/sys/homeapp/api/home/currentUser.do");
+        AppConfig.currentSemester = userRes.datas.welcomeInfo.xnxqdm; //获取学期
+        console.log("检测到当前学期:", AppConfig.currentSemester);
+
+        AppConfig.postData = `XNXQDM=${AppConfig.currentSemester}&XQDM=01`;
+        //XQDM这里暂不知道有什么用,2返回的也是一个时间 不知道是不是代表不同校区 暂时用(‘龙泉’校区)替代
+        const res = await api("https://jwxt.cap.edu.cn/jwapp/sys/kbapp/api/wdkbcx/getMySectionList.do", {
+            data: AppConfig.postData,
+        })
+        const rawSections = res.datas.getMySectionList;
+
+        const cleanSections = rawSections
+        .filter(item => item.name.includes("第")) // 只保留名字里带“第”字的,过滤掉午餐/晚餐
+        .map(item => ({
+            "number": parseInt(item.name.replace(/[^0-9]/g, "")),
+            startSection: item.startTime,
+            endSection: item.endTime
+        }))
+            .sort((a, b) => a.number - b.number);
+        
+        console.log(cleanSections)
+        //开学时间 课程周数
+        
+        const weekRes = await api("https://jwxt.cap.edu.cn/jwapp/sys/homeapp/api/home/getTermWeeks.do",
+            {
+            data: `termCode=${AppConfig.currentSemester}`
+            });
+        const finalWeeks = weekRes.datas.map(item => ({
+        "week": item.serialNumber,          // 周序 (1, 2, 3...)
+        "startTime": item.startDate.split(' ')[0], // 格式化为 YYYY-MM-DD
+        "endTime": item.endDate.split(' ')[0],   // 格式化为 YYYY-MM-DD
+        "isCurrent": item.curWeek           // 是否为当前周     
+        }));
+        const totalWeeks = finalWeeks.length;
+        const startDate = finalWeeks[0].startTime;
+        console.log(AppConfig.currentSemester, totalWeeks,startDate,cleanSections)
+        
+        return {
+            currentSemester: AppConfig.currentSemester,
+            totalWeeks,
+            startDate,
+            cleanSections
+        };
+    
     }
-
     catch (error) {
         console.error('解析开学时间时出错:', error);
         AndroidBridge.showToast(`解析开学时间失败: ${error.message}`);
         return null;
     }
-        
-}
-
-/**
- * 解析开学时间
- * @param {string} weekText - 周次文本,如 "第1周(3/9 ~ 3/15)"
- * @param {string} semesterText - 学期文本,如 "2025-2026学年 第2学期"
- * @returns {string} 开学日期 YYYY-MM-DD
- */
-function parseStartDate(weekText, semesterText) {
-    // 1. 解析学期信息,获取学年和学期
-    const semesterMatch = semesterText.match(/(\d{4})-(\d{4})学年\s*第(\d)学期/);
-    if (!semesterMatch) {
-        throw new Error('无法解析学期信息');
-    }
-    
-    const startYear = parseInt(semesterMatch[1]); // 2025
-    const endYear = parseInt(semesterMatch[2]);   // 2026
-    const semester = parseInt(semesterMatch[3]);  // 1 或 2
-    
-    // 2. 解析周次信息,获取月份和日期范围
-    const weekMatch = weekText.match(/第(\d+)周\((\d{1,2})\/(\d{1,2})\s*~\s*(\d{1,2})\/(\d{1,2})\)/);
-    if (!weekMatch) {
-        throw new Error('无法解析周次信息');
-    }
-    
-    const weekNumber = parseInt(weekMatch[1]);     // 周数
-    const startMonth = parseInt(weekMatch[2]);     // 开始月份
-    const startDay = parseInt(weekMatch[3]);       // 开始日期
-    const endMonth = parseInt(weekMatch[4]);       // 结束月份
-    const endDay = parseInt(weekMatch[5]);         // 结束日期
-    
-    console.log(`解析结果: 第${weekNumber}周, ${startMonth}/${startDay} ~ ${endMonth}/${endDay}`);
-    
-    // 3. 根据学期判断开学年份
-    let startYearForDate;
-    
-    if (semester === 1) {
-        // 第一学期:开学在 startYear 年
-        startYearForDate = startYear;
-    } else {
-        // 第二学期:开学在 endYear 年(通常跨年)
-        startYearForDate = endYear;
-    }
-    
-    // 特殊情况处理:如果开始月份小于当前月份,可能需要调整年份
-    // 比如 1月开学应该是 endYear 年
-    const currentMonth = new Date().getMonth() + 1;
-    if (startMonth < 6 && semester === 2) {
-        // 第二学期如果在1-6月开学,应该用 endYear
-        startYearForDate = endYear;
-    }
-    
-    // 4. 构建开学日期(假设是第1周的周一,或者就用开始日期)
-    // 这里用开始日期作为参考
-    const startDateStr = `${startYearForDate}-${String(startMonth).padStart(2, '0')}-${String(startDay).padStart(2, '0')}`;
-    
-    // 5. 如果是第1周,直接返回开始日期
-    if (weekNumber === 1) {
-        console.log(`开学日期: ${startDateStr}`);
-        return startDateStr;
-    }
-    
-    // 6. 如果不是第1周,需要往前推算
-    // 计算第1周的日期
-    const startDate = new Date(startYearForDate, startMonth - 1, startDay);
-    const daysToSubtract = (weekNumber - 1) * 7;
-    startDate.setDate(startDate.getDate() - daysToSubtract);
-    
-    const firstWeekStartDate = formatDate(startDate);
 
-    return firstWeekStartDate;
-}
+}//返回 学期时间 课程周数 开始时间 时间表
+//      2025-2026-2 19 2026-03-09 Array
 
 /**
- * 格式化日期为 YYYY-MM-DD
+ * 获取课表数据 返回的是原始数据
  */
-function formatDate(date) {
-    const year = date.getFullYear();
-    const month = String(date.getMonth() + 1).padStart(2, '0');
-    const day = String(date.getDate()).padStart(2, '0');
-    return `${year}-${month}-${day}`;
-}
-
-
-
-/** 
- * 计算每天课程节数
- **/
-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;
-
-
-    // 这里的的start和end都加了午餐晚餐 午餐晚餐修正节数
-    if (start >= 10) { 
-        start -= 2;
-        end -= 2;
-    } else if (start > 5) {
-        start -= 1;
-        end -= 1;
-    }
-
-    return { day, start, end };
+async function getCourseData() {
+    const courseRes = await api("https://jwxt.cap.edu.cn/jwapp/sys/kbapp/api/wdkbcx/getMyScheduleDetail.do", {
+        data: AppConfig.postData,
+    })
+    const rawCourses = courseRes?.datas?.getMyScheduleDetail?.arrangedList || [];
+    // console.log("获取到课程数据:", rawCourses);
+    return rawCourses;
 }
 
-/**
- * 解析时间段数据
- */
-function parseTimeSlots(iframeDoc) {
-    const timeSlots = [];
-    
-    // 查找时间段列
-    const timeColumn = iframeDoc.querySelector('.kbappTimetableJcColumn');
-    
-    const timeItems = timeColumn.querySelectorAll('.kbappTimetableJcItem');
-    
-    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}`);
-
+function parseWeeks(weekStr) {
+    if (!weekStr) return [];
+    const weeks = [];
+    // 1. 处理逗号分隔的多个区间
+    const segments = weekStr.replace(/周/g, "").split(",");
+
+    segments.forEach(seg => {
+        // 处理单双周逻辑
+        const isOnlyOdd = seg.includes("(单)");
+        const isOnlyEven = seg.includes("(双)");
+        const cleanSeg = seg.replace(/\(单\)|\(双\)/g, "");
+
+        if (cleanSeg.includes("-")) {
+            // 处理范围型:1-4
+            const [start, end] = cleanSeg.split("-").map(Number);
+            for (let i = start; i <= end; i++) {
+                if (isOnlyOdd && i % 2 === 0) continue;
+                if (isOnlyEven && i % 2 !== 0) continue;
+                weeks.push(i);
             }
+        } else {
+            // 处理单个数字
+            const num = Number(cleanSeg);
+            if (!isNaN(num)) weeks.push(num);
         }
     });
-    
-    return timeSlots;
-}
 
+    return [...new Set(weeks)].sort((a, b) => a - b);
+}
 
 /**
- * 解析周次信息
+ * 1. 展开周次函数:支持 1-3周(单), 7-17周(单) 等
  */
-function parseWeeks(text) {
+function expandWeeks(rawStr) {
     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]);
+    if (!rawStr) return weeks;
+
+    const cleanStr = rawStr.replace(/\s+/g, '').replace(/,/g, ',').replace(/周/g, '');
+    const isOdd = cleanStr.includes('(单)');
+    const isEven = cleanStr.includes('(双)');
+    const rangePart = cleanStr.replace(/\([单双]\)/g, '');
+    
+    rangePart.split(',').forEach(segment => {
+        if (segment.includes('-')) {
+            const [start, end] = segment.split('-').map(Number);
             for (let i = start; i <= end; i++) {
-                if (isSingle && i % 2 === 0) continue;
-                if (isDouble && i % 2 !== 0) continue;
+                if (isOdd && i % 2 === 0) continue;
+                if (isEven && i % 2 !== 0) continue;
                 weeks.push(i);
             }
         } else {
-            const single = p.match(/(\d+)/);
-            if (single) weeks.push(parseInt(single[1]));
+            const num = parseInt(segment);
+            if (!isNaN(num)) {
+                if (isOdd && num % 2 === 0) return;
+                if (isEven && num % 2 !== 0) return;
+                weeks.push(num);
+            }
         }
     });
     return weeks;
 }
 
 /**
- * 解析单个课程信息 
+ * 2. 单行解析函数:提取核心信息
  */
-
-// 这里源数据使用了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 parseDetailLine(line) {
+    // 移除 HTML 标签
+    const cleanLine = line.replace(/<[^>]+>/g, "").trim();
+    const parts = cleanLine.split(/\s+/);
+
+    // 假设格式为:[周次] [老师] [建筑/校区] [具体地点]
+    const rawWeek = parts[0] || "";
+    const teacher = parts[1] || "未知教师";
+    const building = parts[2] || "";
+    const location = parts[3] || "";
+
+    return {
+        rawWeek,
+        teacher,
+        building,
+        location,
+        weeks: parseWeeks(rawWeek) // 假设你有这个解析 1-4,6周 到数组的函数
+    };
 }
 
 /**
- * 解析课程数据
+ * 3. 智能汇总函数:处理地点变动逻辑
  */
-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);
+function extractAndMergeCourse(titleDetail) {
+    if (!titleDetail || titleDetail.length === 0) return null;
+
+    const courseName = titleDetail[0];
+    // 过滤掉第一行课程名,解析后面所有的详情行
+    const rawSlots = titleDetail.slice(1).map(line => parseDetailLine(line));
+
+    const mergedMap = new Map();
+
+    rawSlots.forEach(slot => {
+        // 连堂课如果地点老师一样,就合并周次;如果不一样(比如一半在教室一半在实验室),会拆分成两个 segment
+        const identifier = `${slot.teacher}|${slot.building}|${slot.location}`;
         
-        dayCourses.forEach(courseElement => {
-            const courseInfo = parseSingleCourse(courseElement, dayIndex + 1, timeSlots);
-            if (courseInfo) {
-                courses.push(courseInfo);
-            }
-        });
-    }
+        if (mergedMap.has(identifier)) {
+            const existing = mergedMap.get(identifier);
+            // 合并周次并去重排序
+            existing.weeks = [...new Set([...existing.weeks, ...slot.weeks])].sort((a, b) => a - b);
+            existing.rawWeeksDesc += `, ${slot.rawWeek}`;
+        } else {
+            mergedMap.set(identifier, {
+                teacher: slot.teacher,
+                building: slot.building,
+                location: slot.location,
+                weeks: slot.weeks,
+                rawWeeksDesc: slot.rawWeek
+            });
+        }
+    });
+
+    const segments = Array.from(mergedMap.values());
+
+    // --- 修复点:先计算,再打印和返回 ---
+    const allActiveWeeks = [...new Set(segments.flatMap(s => s.weeks))].sort((a, b) => a - b);
     
-    return courses;
+    console.log("解析课程:", courseName, "总周次:", allActiveWeeks);
+
+    return {
+        courseName,
+        allActiveWeeks,
+        segments
+    };
 }
 
+
 /**
- * 解析所有数据
+ * 解析函数  Gemini所写  
  */
-
-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
-                });
+function parseAllCourses(rawArrangedList) {
+    const finalCourses = [];
+    if (!rawArrangedList || !Array.isArray(rawArrangedList)) return [];
+    
+    rawArrangedList.forEach(item => {
+        if (item.titleDetail && item.titleDetail.length > 0) {
+            const mergedResult = extractAndMergeCourse(item.titleDetail);
+            if (!mergedResult) return;
+
+            // 优先使用数据里的数字字段,因为 titleDetail 有时会被截断
+            const sSection = parseInt(item.beginSection || item.startSection);
+            const eSection = parseInt(item.endSection);
+            const day = parseInt(item.dayOfWeek || item.day);
+
+            mergedResult.segments.forEach(seg => {
+                if (!isNaN(sSection) && !isNaN(eSection)) {
+                    finalCourses.push({
+                        name: mergedResult.courseName,
+                        teacher: seg.teacher,
+                        position: (seg.building + " " + seg.location).trim(),
+                        day: day, 
+                        startSection: sSection,
+                        endSection: eSection,
+                        weeks: seg.weeks,
+                        startTime: item.beginTime, // 记录开始时间防止后续需要
+                        endTime: item.endTime
+                    });
+                }
             });
-        } catch (e) { console.error("解析单条课程失败:", e); }
+        }
     });
 
-    return { courses: removeDuplicates(courses), timeSlots };
+    return finalCourses;
 }
-
 /**
- * 课程去重   后期这里可能会出现问题
+ * 获取所有课程信息
  */
+async function fetchAllRawData() {
+    try {
+        // 获取基础环境信息 (学期、开学日期、时间表)
+        const baseInfo = await extractCourseTime();
+        if (!baseInfo) return null;
 
-function removeDuplicates(courses) {
-    const courseMap = new Map();
-    
-    courses.forEach(course => {
-        // 生成唯一键(不包括周次)
-        // 可以根据需要调整组合字段
-        const key = `${course.day}-${course.startSection}-${course.endSection}-${course.name}-${course.position}`;
+        const rawArrangedList = await getCourseData();
         
-        if (courseMap.has(key)) {
-            // 已存在:合并周次
-            const existing = courseMap.get(key);
-            // 合并并去重
-            const combinedWeeks = [...existing.weeks, ...course.weeks];
-            const uniqueWeeks = [...new Set(combinedWeeks)];
-            // 排序
-            existing.weeks = uniqueWeeks.sort((a, b) => a - b);
-            
-            // 如果需要,可以保留最早出现的教师(如果教师不同的话)
-            // 但这里保持原有逻辑,不更新教师
-        } else {
-            // 不存在:添加新记录
-            courseMap.set(key, {...course, weeks: [...course.weeks]});
+        if (!rawArrangedList || rawArrangedList.length === 0) {
+            AndroidBridge.showToast("未检测到当前学期的课程数据");
+            return null;
         }
-    });
-    
-    // 转换回数组
-    return Array.from(courseMap.values());
+
+        return { baseInfo, rawArrangedList };
+    } catch (e) {
+        console.error("抓取数据失败:", e);
+        return null;
+    }
 }
 
 /**
- * 保存课程数据
+ * 保存
  */
-async function saveCourses(parsedData) {
-    const { courses, timeSlots } = parsedData;
-    
-            // 解析开学时间
+   async function executeSaveSequence(finalCourses, baseInfo) {
     try {
-        const startDateInfo = extractStartDate();
-        if (!startDateInfo) {
-            AndroidBridge.showToast("获取开学时间失败");
-        }
-        
+        // 1. 保存基础配置 (开学日期、总周数)
         const configData = {
-            semesterStartDate: startDateInfo?.startDate || null,  // 如果获取失败就传 null
-        }
-
-        AndroidBridge.showToast(`准备保存开学时间 ${startDateInfo.startDate}`);
+            semesterStartDate: baseInfo.startDate,
+            semesterTotalWeeks: baseInfo.totalWeeks || 20,
+        };
 
-        let courseSaveResult = await window.AndroidBridgePromise.saveCourseConfig (
-            JSON.stringify(configData)  
-        );
+        const configSuccess = await AndroidBridge.saveCourseConfig(JSON.stringify(configData));
         
-        if (!courseSaveResult) {
-            AndroidBridge.showToast("保存开学时间失败,请自行设定");
+        if (!configSuccess) {
+            AndroidBridge.showToast("学期保存失败");
+            return false;
         }
 
+        // 2. 保存时间段 (节次时间表
+        const slotSuccess = await AndroidBridge.savePresetTimeSlots(JSON.stringify(baseInfo.cleanSections));
+        if (!slotSuccess) return false;
 
-        AndroidBridge.showToast(`准备保存 ${courses.length} 门课程...`);
+        // 3. 保存课程数据
+        const saveResult = await AndroidBridge.saveImportedCourses(JSON.stringify(finalCourses));
         
-        // 保存课程数据
-        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 saveResult;
+
+    } catch (e) {
+        console.error("保存流程崩溃:", e);
+        AndroidBridge.showToast("导入过程发生意外");
+        return false;
+    }
+}
+
+
+/**
+ * 保存配置 (日期和周数)
+ */
+async function saveConfig(baseInfo) {
+    const configData = {
+        semesterStartDate: baseInfo.startDate,
+        semesterTotalWeeks: baseInfo.totalWeeks || 20,
+    };
+    try {
+        const configSuccess = await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify(configData));
+        if (configSuccess) {
+            return true;
         }
-        
-        return true;
+        return false;
     } catch (error) {
-        console.error("保存课程数据时出错:", error);
-        AndroidBridge.showToast(`保存失败: ${error.message}`);
+        AndroidBridge.showToast("保存配置失败: " + error.message);
         return false;
     }
 }
-async function fitTimes() {
-    
-}
+
 
 /**
- * 运行主函数
+ * 主导入流
  */
 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. 当前学期有课程",
-                "知道了"
-            );
+        // 1. 前置确认
+        const isReady = await promptUserToStart();
+        if (!isReady) return;
+
+        // 2. 抓取所有必要数据
+        const dataBundle = await fetchAllRawData();
+        if (!dataBundle) return;
+
+        // 3. 解析原始数据
+        const finalCourses = parseAllCourses(dataBundle.rawArrangedList);
+        if (finalCourses.length === 0) {
+            AndroidBridge.showToast("解析失败:未能提取到有效课程");
             return;
         }
 
- 
-        
-        // 5. 显示预览
-        const previewMsg = `找到 ${parsedData.courses.length} 门课程\n${parsedData.timeSlots.length} 个时间段\n\n是否继续导入?`;
-        const confirmed = await window.AndroidBridgePromise.showAlert(
-            "导入确认",
-            previewMsg,
-            "确认导入"
-        );
-        
-        if (!confirmed) {
-            AndroidBridge.showToast("已取消导入");
+        // 4. 保存配置数据 (存日期、周数)
+        const configSaveResult = await saveConfig(dataBundle.baseInfo);
+        if (!configSaveResult) return;
+
+        // 5. 课程数据保存
+        const saveResult = await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(finalCourses));
+        if (!saveResult) {
+            AndroidBridge.showToast("课程数据保存失败");
             return;
         }
-        
-        // 6. 保存数据
-        const saveSuccess = await saveCourses(parsedData);
-        if (!saveSuccess) return;
-        
-        // 7. 完成
-        AndroidBridge.showToast("课表导入完成!");
+
+        // 6. 流程成功结束
+        AndroidBridge.showToast("Hi ~  课表导入成功!");
         AndroidBridge.notifyTaskCompletion();
-        
+
     } catch (error) {
-        console.error("导入流程出错:", error);
-        AndroidBridge.showToast(`导入失败: ${error.message}`);
+        console.error("主流程异常:", error);
+        AndroidBridge.showToast("意外错误: " + error.message);
     }
 }