/**
* 青岛农业大学综合教务管理系统(强智科技)
* by ReGoMark, 2026.07.04
*
*/
// ─────────────────────────────────────────────
// 工具函数
// ─────────────────────────────────────────────
/**
* 解析周次字符串
* 例如 "1-4,6,11-13" → [1, 2, 3, 4, 6, 11, 12, 13]
*/
function parseWeeks(weekStr) {
let weeks = [];
weekStr = weekStr.replace(/\(.*?\)/g, '').trim();
let parts = weekStr.split(',');
for (let part of parts) {
part = part.trim();
if (part.includes('-')) {
let [start, end] = part.split('-');
for (let i = parseInt(start); i <= parseInt(end); i++) {
if (!weeks.includes(i)) weeks.push(i);
}
} else {
let w = parseInt(part);
if (!isNaN(w) && !weeks.includes(w)) weeks.push(w);
}
}
return weeks.sort((a, b) => a - b);
}
/**
* 解析行标题中的节次范围
* 例如 "第1,2节" → { start: 1, end: 2 }
* "第5节" → { start: 5, end: 5 }
*/
function parseSectionFromThHeader(thText) {
let match = thText.match(/第([\d,]+)节/);
if (!match) return null;
let nums = match[1].split(',').map(n => parseInt(n)).filter(n => !isNaN(n));
if (nums.length === 0) return null;
return { start: nums[0], end: nums[nums.length - 1] };
}
// ─────────────────────────────────────────────
// 核心解析
// ─────────────────────────────────────────────
/**
* 从课表页面的 Document 中提取并去重、合并课程数据
*/
function extractCoursesFromDoc(doc) {
let parsedCourses = [];
const table = doc.getElementById('kbtable');
if (!table) throw new Error("未找到课表表格(#kbtable),请确认已登录教务系统且当前学期有排课。");
const rows = table.getElementsByTagName('tr');
for (let i = 1; i < rows.length - 1; i++) {
const row = rows[i];
const th = row.querySelector('th');
if (!th) continue;
const sectionInfo = parseSectionFromThHeader(th.innerText || th.textContent);
if (!sectionInfo) continue;
const cells = row.getElementsByTagName('td');
for (let j = 0; j < cells.length; j++) {
const dayOfWeek = j + 1;
const cell = cells[j];
const detailDivs = cell.querySelectorAll('div.kbcontent');
if (detailDivs.length === 0) continue;
detailDivs.forEach(div => {
let htmlContent = div.innerHTML;
if (!htmlContent.trim() || htmlContent.trim() === ' ') return;
let courseBlocks = htmlContent.split(/-{5,}\s*
/i);
courseBlocks.forEach(block => {
if (!block.trim() || block.trim() === ' ') return;
let tempDiv = doc.createElement('div');
tempDiv.innerHTML = block;
let courseObj = {
day: dayOfWeek,
isCustomTime: false,
startSection: sectionInfo.start,
endSection: sectionInfo.end
};
// 1. 课程名
let lines = tempDiv.innerHTML.split(/
/i);
for (let line of lines) {
let cleanLine = line.replace(/<[^>]+>/g, '').trim();
if (cleanLine && cleanLine !== ' ') {
courseObj.name = cleanLine;
break;
}
}
// 2. 教师(QAU title="老师")
let teacherFont = tempDiv.querySelector('font[title="老师"]');
courseObj.teacher = teacherFont
? (teacherFont.innerText || teacherFont.textContent).trim()
: "未知";
// 3. 教室
let positionFont = tempDiv.querySelector('font[title="教室"]');
courseObj.position = positionFont
? (positionFont.innerText || positionFont.textContent).trim()
: "待定";
// 4. 周次
let timeFont = tempDiv.querySelector('font[title="周次(节次)"]');
if (timeFont) {
let timeText = (timeFont.innerText || timeFont.textContent).trim();
let weekMatch = timeText.match(/^(.+?)\(周\)/);
if (weekMatch) {
courseObj.weeks = parseWeeks(weekMatch[1]);
}
}
if (!courseObj.weeks || courseObj.weeks.length === 0) return;
if (!courseObj.name) return;
parsedCourses.push(courseObj);
});
});
}
}
// ── 去重 ──
let uniqueCourses = [];
let courseSet = new Set();
parsedCourses.forEach(course => {
let key = `${course.day}-${course.startSection}-${course.endSection}-${course.name}-${course.weeks.join(',')}`;
if (!courseSet.has(key)) {
courseSet.add(key);
uniqueCourses.push(course);
}
});
// ── 合并相邻节次(最多合并两个大节,即 1-4 节)──
// 条件:同天、同名、同教师、同教室、同周次,节次紧邻,且合并后跨度不超过 4 节
const sorted = uniqueCourses.sort((a, b) => {
if (a.day !== b.day) return a.day - b.day;
if (a.name !== b.name) return a.name.localeCompare(b.name);
const wa = a.weeks.join(','), wb = b.weeks.join(',');
if (wa !== wb) return wa.localeCompare(wb);
return a.startSection - b.startSection;
});
const merged = [];
for (const cur of sorted) {
const prev = merged[merged.length - 1];
const canMerge = prev
&& prev.day === cur.day
&& prev.name === cur.name
&& prev.teacher === cur.teacher
&& prev.position === cur.position
&& prev.weeks.join(',') === cur.weeks.join(',')
&& prev.endSection + 1 === cur.startSection
&& (cur.endSection - prev.startSection) <= 3;
if (canMerge) {
prev.endSection = cur.endSection;
} else {
merged.push({ ...cur });
}
}
return merged;
}
// ─────────────────────────────────────────────
// 学校定制数据
// ─────────────────────────────────────────────
/**
* 三个校区的作息时间表
*/
const CAMPUS_TIME_SLOTS = {
"青岛校区": [
{ "number": 1, "startTime": "08:00", "endTime": "08:45" },
{ "number": 2, "startTime": "08:55", "endTime": "09:40" },
{ "number": 3, "startTime": "09:55", "endTime": "10:40" },
{ "number": 4, "startTime": "10:50", "endTime": "11:35" },
{ "number": 5, "startTime": "11:35", "endTime": "12:00" },
{ "number": 6, "startTime": "14:00", "endTime": "14:45" },
{ "number": 7, "startTime": "14:55", "endTime": "15:40" },
{ "number": 8, "startTime": "15:55", "endTime": "16:40" },
{ "number": 9, "startTime": "16:50", "endTime": "17:35" },
{ "number": 10, "startTime": "18:50", "endTime": "19:35" },
{ "number": 11, "startTime": "19:45", "endTime": "20:30" }
],
"平度校区": [
{ "number": 1, "startTime": "08:30", "endTime": "09:15" },
{ "number": 2, "startTime": "09:25", "endTime": "10:10" },
{ "number": 3, "startTime": "10:20", "endTime": "11:05" },
{ "number": 4, "startTime": "11:15", "endTime": "12:00" },
{ "number": 5, "startTime": "12:00", "endTime": "12:25" },
{ "number": 6, "startTime": "14:00", "endTime": "14:45" },
{ "number": 7, "startTime": "14:55", "endTime": "15:40" },
{ "number": 8, "startTime": "15:50", "endTime": "16:35" },
{ "number": 9, "startTime": "16:45", "endTime": "17:30" },
{ "number": 10, "startTime": "18:50", "endTime": "19:35" },
{ "number": 11, "startTime": "19:45", "endTime": "20:30" }
],
"蓝谷校区": [
{ "number": 1, "startTime": "08:30", "endTime": "09:15" },
{ "number": 2, "startTime": "09:20", "endTime": "10:05" },
{ "number": 3, "startTime": "10:15", "endTime": "11:00" },
{ "number": 4, "startTime": "11:05", "endTime": "11:50" },
{ "number": 5, "startTime": "13:10", "endTime": "13:55" },
{ "number": 6, "startTime": "14:00", "endTime": "14:45" },
{ "number": 7, "startTime": "14:55", "endTime": "15:40" },
{ "number": 8, "startTime": "15:45", "endTime": "16:30" },
{ "number": 9, "startTime": "16:35", "endTime": "17:20" },
{ "number": 10, "startTime": "18:30", "endTime": "19:15" },
{ "number": 11, "startTime": "19:25", "endTime": "20:15" }
]
};
function getCourseConfig() {
return {
"defaultClassDuration": 45,
"defaultBreakDuration": 5
};
}
// ─────────────────────────────────────────────
// 主流程
// ─────────────────────────────────────────────
async function runImportFlow() {
try {
AndroidBridge.showToast("正在获取课表数据,请稍候...");
const response = await fetch('/jsxsd/xskb/xskb_list.do', { method: 'GET' });
const htmlText = await response.text();
const parser = new DOMParser();
let doc = parser.parseFromString(htmlText, 'text/html');
// 解析学期列表
const selectElem = doc.getElementById('xnxq01id');
let semesters = [];
let semesterValues = [];
let defaultIndex = 0;
if (selectElem) {
const options = selectElem.querySelectorAll('option');
options.forEach((opt, index) => {
semesters.push((opt.innerText || opt.textContent).trim());
semesterValues.push(opt.value);
if (opt.hasAttribute('selected')) {
defaultIndex = index;
}
});
}
// 选择学期
if (semesters.length > 0) {
let selectedIdx = await window.AndroidBridgePromise.showSingleSelection(
"请选择要导入的学期",
JSON.stringify(semesters),
defaultIndex
);
if (selectedIdx === null) {
AndroidBridge.showToast("已取消导入");
return;
}
if (selectedIdx !== defaultIndex) {
AndroidBridge.showToast(`正在获取 [${semesters[selectedIdx]}] 课表...`);
let formData = new URLSearchParams();
formData.append('xnxq01id', semesterValues[selectedIdx]);
const postResponse = await fetch('/jsxsd/xskb/xskb_list.do', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: formData.toString()
});
const postHtml = await postResponse.text();
doc = parser.parseFromString(postHtml, 'text/html');
}
}
// 选择校区
const campusNames = Object.keys(CAMPUS_TIME_SLOTS);
const campusIdx = await window.AndroidBridgePromise.showSingleSelection(
"请选择您所在的校区",
JSON.stringify(campusNames),
0
);
if (campusIdx === null) {
AndroidBridge.showToast("已取消导入");
return;
}
const selectedCampus = campusNames[campusIdx];
const timeSlots = CAMPUS_TIME_SLOTS[selectedCampus];
// 解析课程
const courses = extractCoursesFromDoc(doc);
if (courses.length === 0) {
await window.AndroidBridgePromise.showAlert(
"提示",
"未能解析到任何课程,请检查当前学期是否有排课,或尝试切换学期。",
"好的"
);
return;
}
// 保存
await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify(getCourseConfig()));
await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(timeSlots));
const saveResult = await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(courses));
if (!saveResult) {
AndroidBridge.showToast("课程保存失败,请重试!");
return;
}
AndroidBridge.showToast(`成功导入 ${courses.length} 节课程(${selectedCampus}作息)!`);
AndroidBridge.notifyTaskCompletion();
} catch (error) {
AndroidBridge.showToast("导入发生异常: " + error.message);
}
}
runImportFlow();