|
|
@@ -16,11 +16,10 @@ function findTable(win) {
|
|
|
const t = Array.from(win.document.querySelectorAll('table'))
|
|
|
.find(x => x.innerText.includes("星期一") && x.innerText.includes("["));
|
|
|
if (t) return t;
|
|
|
-
|
|
|
for (let i = 0; i < win.frames.length; i++) {
|
|
|
- try {
|
|
|
- const st = findTable(win.frames[i]);
|
|
|
- if (st) return st;
|
|
|
+ try {
|
|
|
+ const st = findTable(win.frames[i]);
|
|
|
+ if (st) return st;
|
|
|
} catch (e) {}
|
|
|
}
|
|
|
return null;
|
|
|
@@ -28,7 +27,6 @@ function findTable(win) {
|
|
|
|
|
|
async function fetchAndParseCourses() {
|
|
|
const table = findTable(window);
|
|
|
-
|
|
|
if (!table) {
|
|
|
throw new Error("未检测到课表数据,请确保已切换到显示课表的页面!");
|
|
|
}
|
|
|
@@ -37,7 +35,7 @@ async function fetchAndParseCourses() {
|
|
|
Array.from(table.rows).forEach(row => {
|
|
|
const cells = Array.from(row.cells);
|
|
|
if (cells.length < 7) return;
|
|
|
-
|
|
|
+
|
|
|
cells.forEach((cell, colIndex) => {
|
|
|
const distanceToLast = cells.length - 1 - colIndex;
|
|
|
if (distanceToLast > 6) return;
|
|
|
@@ -45,21 +43,61 @@ async function fetchAndParseCourses() {
|
|
|
const rawText = cell.innerText.trim();
|
|
|
if (!rawText.includes('[')) return;
|
|
|
|
|
|
- const lines = rawText.split('\n').map(l => l.trim()).filter(l => l);
|
|
|
- lines.forEach((line, i) => {
|
|
|
- const match = line.match(/([\d\-,]+)\[(\d+)-(\d+)\]/);
|
|
|
+ // 过滤空行、清理隐藏的乱码字符
|
|
|
+ const lines = rawText.split('\n').map(l => l.replace(/[\s\u200B-\u200D\uFEFF]/g, '').trim()).filter(l => l);
|
|
|
+
|
|
|
+ // 找出所有时间的行号(锚点)
|
|
|
+ const timeIndices = [];
|
|
|
+ lines.forEach((line, index) => {
|
|
|
+ if (/([\d\-,]+)\[(\d+)-(\d+)\]/.test(line)) {
|
|
|
+ timeIndices.push(index);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 遍历每个锚点,精准抓取
|
|
|
+ timeIndices.forEach((currTimeIdx, k) => {
|
|
|
+ const nextTimeIdx = (k + 1 < timeIndices.length) ? timeIndices[k + 1] : lines.length;
|
|
|
+ const match = lines[currTimeIdx].match(/([\d\-,]+)\[(\d+)-(\d+)\]/);
|
|
|
+
|
|
|
if (match) {
|
|
|
+ // 1. 抓取课名和老师
|
|
|
let name = "未知课程";
|
|
|
- if (i >= 2) name = lines[i-2];
|
|
|
- else if (i >= 1) name = lines[i-1];
|
|
|
+ let teacher = "未知教师";
|
|
|
+ let nameTeacherLines = (k === 0) ? lines.slice(0, currTimeIdx) : lines.slice(timeIndices[k - 1] + 1, currTimeIdx);
|
|
|
+
|
|
|
+ if (nameTeacherLines.length > 2) {
|
|
|
+ nameTeacherLines = nameTeacherLines.slice(nameTeacherLines.length - 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (nameTeacherLines.length >= 2) {
|
|
|
+ name = nameTeacherLines[0];
|
|
|
+ teacher = nameTeacherLines[1];
|
|
|
+ } else if (nameTeacherLines.length === 1) {
|
|
|
+ name = nameTeacherLines[0];
|
|
|
+ teacher = ""; // 没写老师
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 抓取地点
|
|
|
+ let position = "";
|
|
|
+ const gap = nextTimeIdx - currTimeIdx - 1; // 距离下个时间(或结尾)差几行
|
|
|
|
|
|
- let teacher = (i >= 1 && !lines[i-1].includes('[')) ? lines[i-1] : "未知教师";
|
|
|
- let position = (i < lines.length - 1) ? lines[i+1] : "未知地点";
|
|
|
+ if (k === timeIndices.length - 1) {
|
|
|
+ // 最后一门课,后面全是地点
|
|
|
+ if (gap > 0) position = lines.slice(currTimeIdx + 1).join(' ');
|
|
|
+ } else {
|
|
|
+ if (gap === 3) {
|
|
|
+ position = lines[currTimeIdx + 1]; // 刚好3行,第1行必是地点
|
|
|
+ } else if (gap > 3) {
|
|
|
+ position = lines.slice(currTimeIdx + 1, nextTimeIdx - 2).join(' '); // 地点占了多行
|
|
|
+ } else {
|
|
|
+ position = ""; // <= 2行说明全被下节课的名字占了,这节课没地点(网课)
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
rawItems.push({
|
|
|
- name: name.replace(/\s/g, ""),
|
|
|
- teacher: teacher.replace(/\s/g, ""),
|
|
|
- position: position.replace(/\s/g, ""),
|
|
|
+ name: name,
|
|
|
+ teacher: teacher,
|
|
|
+ position: position,
|
|
|
day: day,
|
|
|
startSection: parseInt(match[2]),
|
|
|
endSection: parseInt(match[3]),
|
|
|
@@ -87,12 +125,10 @@ async function fetchAndParseCourses() {
|
|
|
groupMap.forEach((weekMap, key) => {
|
|
|
const [name, teacher, position, day] = key.split('|');
|
|
|
const patternMap = new Map();
|
|
|
-
|
|
|
Object.keys(weekMap).forEach(w => {
|
|
|
const week = parseInt(w);
|
|
|
const sections = Array.from(weekMap[week]).sort((a, b) => a - b);
|
|
|
if (sections.length === 0) return;
|
|
|
-
|
|
|
let start = sections[0];
|
|
|
for (let i = 0; i < sections.length; i++) {
|
|
|
if (i === sections.length - 1 || sections[i+1] !== sections[i] + 1) {
|
|
|
@@ -103,7 +139,6 @@ async function fetchAndParseCourses() {
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
-
|
|
|
patternMap.forEach((weeks, pKey) => {
|
|
|
const [sStart, sEnd] = pKey.split('-').map(Number);
|
|
|
finalCourses.push({
|
|
|
@@ -122,13 +157,11 @@ async function runImportFlow() {
|
|
|
try {
|
|
|
AndroidBridge.showToast("泰山学院引擎启动,抓取数据中...");
|
|
|
const courses = await fetchAndParseCourses();
|
|
|
-
|
|
|
if (!courses || courses.length === 0) {
|
|
|
AndroidBridge.showToast("解析完成,但当前课表为空");
|
|
|
AndroidBridge.notifyTaskCompletion();
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
const saveResult = await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(courses));
|
|
|
if (saveResult === true) {
|
|
|
AndroidBridge.showToast(`导入大成功!合并生成 ${courses.length} 个课块`);
|