| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- // 文件: school.js
- // 功能:从桂医教务系统获取课程表,通过桥接 API 导入到拾光课程表
- // ---------- 全局验证函数 ----------
- function validateYearInput(input) {
- if (/^\d{4}$/.test(input)) {
- return false; // 验证通过
- } else {
- return "请输入四位数字的年份(例如 2024)";
- }
- }
- // ---------- 工具函数 ----------
- function parseWeeks(weeksStr) {
- // 🔥 修复核心:兼容数字、数组和字符串
- if (typeof weeksStr === 'number') {
- return [weeksStr];
- }
- if (Array.isArray(weeksStr)) {
- return weeksStr.map(Number);
- }
- // 兼容字符串情况
- let str = String(weeksStr).replace('周', '');
- const parts = str.split(',');
- const weeks = [];
- for (const part of parts) {
- if (part.includes('-')) {
- const [start, end] = part.split('-').map(Number);
- for (let i = start; i <= end; i++) weeks.push(i);
- } else {
- weeks.push(Number(part));
- }
- }
- return weeks;
- }
- // 兼容旧字段 jcdm 的解析(保留作为备用)
- function parseSections(jcdm) {
- const str = String(jcdm);
- const sections = [];
- if (str.includes('-')) {
- const [start, end] = str.split('-').map(Number);
- for (let i = start; i <= end; i++) sections.push(i);
- } else if (/^\d+$/.test(str)) {
- for (let i = 0; i < str.length; i += 2) {
- const sec = parseInt(str.substring(i, i + 2), 10);
- if (!isNaN(sec)) sections.push(sec);
- }
- } else {
- sections.push(parseInt(str));
- }
- return sections;
- }
- function parseRawCourses(rawData) {
- const courseInfos = [];
- for (const course of rawData) {
- const name = course.kcmc;
- const teacher = course.teaxms;
- const position = course.jxcdmc;
- const weeks = parseWeeks(course.zc); // 这里调用已经修复
- const day = parseInt(course.xq);
- // 使用 ps 和 pe 字段来生成 sections 数组
- let sections = [];
- if (course.ps !== undefined && course.pe !== undefined && course.ps !== '' && course.pe !== '') {
- const start = parseInt(course.ps, 10);
- const end = parseInt(course.pe, 10);
- if (!isNaN(start) && !isNaN(end)) {
- for (let i = start; i <= end; i++) {
- sections.push(i);
- }
- }
- } else if (course.jcdm !== undefined) {
- sections = parseSections(course.jcdm);
- }
- if (sections.length === 0) {
- console.warn('跳过无节次信息的课程:', course.kcmc);
- continue;
- }
- courseInfos.push({ name, teacher, position, weeks, day, sections });
- }
- return courseInfos;
- }
- function convertToTargetCourses(middleCourses) {
- return middleCourses.map(c => ({
- name: c.name,
- teacher: c.teacher,
- position: c.position,
- day: c.day,
- startSection: c.sections[0],
- endSection: c.sections[c.sections.length - 1],
- weeks: c.weeks,
- isCustomTime: false
- }));
- }
- function getTimeSlots() {
- return [
- { number: 1, startTime: "08:30", endTime: "09:10" },
- { number: 2, startTime: "09:20", endTime: "10:00" },
- { number: 3, startTime: "10:10", endTime: "10:50" },
- { number: 4, startTime: "11:00", endTime: "11:40" },
- { number: 5, startTime: "11:50", endTime: "12:30" },
- { number: 6, startTime: "14:30", endTime: "15:10" },
- { number: 7, startTime: "15:20", endTime: "16:00" },
- { number: 8, startTime: "16:10", endTime: "16:50" },
- { number: 9, startTime: "17:00", endTime: "17:40" },
- { number: 10, startTime: "19:00", endTime: "19:40" },
- { number: 11, startTime: "19:50", endTime: "20:30" },
- { number: 12, startTime: "20:40", endTime: "21:20" }
- ];
- }
- // ---------- 网络请求 ----------
- async function fetchCourseData(xnxqdm) {
- const url = "https://ejwc.glmu.edu.cn/new/student/xsgrkb/getCalendarWeekDatas";
- // 🔥 核心修改:并发请求第 1 周到第 20 周
- const promises = [];
- for (let zc = 1; zc <= 20; zc++) {
- const bodyParams = `xnxqdm=${xnxqdm}&zc=${zc}`;
- promises.push(
- fetch(url, {
- headers: {
- "accept": "application/json, text/javascript, */*; q=0.01",
- "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
- "x-requested-with": "XMLHttpRequest"
- },
- body: bodyParams,
- method: "POST",
- credentials: "include"
- })
- .then(res => res.json())
- .then(ret => {
- // 深度兼容新版数据结构
- let courseData = null;
- if (Array.isArray(ret)) {
- courseData = ret;
- } else if (ret.rows && Array.isArray(ret.rows)) {
- courseData = ret.rows;
- } else if (ret.data) {
- if (Array.isArray(ret.data)) {
- courseData = ret.data;
- } else if (ret.data.rows && Array.isArray(ret.data.rows)) {
- courseData = ret.data.rows;
- } else if (ret.data.list && Array.isArray(ret.data.list)) {
- courseData = ret.data.list;
- }
- } else if (ret.result && Array.isArray(ret.result)) {
- courseData = ret.result;
- } else if (ret.list && Array.isArray(ret.list)) {
- courseData = ret.list;
- }
- if (courseData && courseData.length > 0) {
- // 安全补全:如果教务系统返回的数据里没有 zc 字段,补上当前请求的周次
- courseData.forEach(course => {
- if (!course.zc) {
- course.zc = String(zc);
- }
- });
- return courseData;
- }
- return [];
- })
- .catch(err => {
- console.error(`请求第 ${zc} 周数据出错:`, err);
- return []; // 某一周失败不影响整体
- })
- );
- }
- // 等待所有 20 个请求并发完成
- const results = await Promise.all(promises);
- // 合并结果
- let allRows = [];
- results.forEach(rows => {
- allRows = allRows.concat(rows);
- });
- return allRows;
- }
- // ---------- 用户交互 ----------
- async function promptUserToStart() {
- return await window.shiguangBridgePromise.showAlert(
- "重要提醒",
- "请确保您已登录桂医教务系统,且当前页面为教务系统内任意页面。\n\n点击确定继续。",
- "确定"
- );
- }
- async function getAcademicYear() {
- return await window.shiguangBridgePromise.showPrompt(
- "学年设置",
- "请输入本学年开始的年份\n(例如 2024,代表 2024-2025 学年)",
- "2024",
- "validateYearInput"
- );
- }
- async function selectSemester() {
- const semesterOptions = ["上学期", "下学期", "短学期"];
- const index = await window.shiguangBridgePromise.showSingleSelection(
- "选择学期",
- JSON.stringify(semesterOptions),
- 0
- );
- if (index === null || index < 0) return null;
- return index;
- }
- // ---------- 主流程 ----------
- async function run() {
- try {
- const confirmed = await promptUserToStart();
- if (!confirmed) {
- window.shiguangBridge.showToast("用户取消了导入流程。");
- return;
- }
- const yearInput = await getAcademicYear();
- if (yearInput === null) {
- window.shiguangBridge.showToast("导入已取消。");
- return;
- }
- const yearNum = parseInt(yearInput);
- if (isNaN(yearNum) || yearNum < 2000 || yearNum > 2100) {
- await window.shiguangBridgePromise.showAlert("错误", "学年输入无效,请输入2000-2100之间的数字。", "确定");
- return;
- }
- const semesterIndex = await selectSemester();
- if (semesterIndex === null) {
- window.shiguangBridge.showToast("导入已取消。");
- return;
- }
- const termCode = semesterIndex === 0 ? "01" : (semesterIndex === 1 ? "02" : "03");
- const xnxqdm = `${yearNum}${termCode}`;
- window.shiguangBridge.showToast("正在获取课表,请稍候...");
- let rawData;
- try {
- rawData = await fetchCourseData(xnxqdm);
- } catch (fetchErr) {
- await window.shiguangBridgePromise.showAlert(
- "网络请求失败",
- `请求教务系统失败:${fetchErr.message}\n\n请检查网络连接和登录状态。`,
- "确定"
- );
- return;
- }
- if (!rawData.length) {
- await window.shiguangBridgePromise.showAlert("提示", "未获取到任何课程数据。请确认已登录教务系统并选择正确的学年学期。", "确定");
- return;
- }
- const middleCourses = parseRawCourses(rawData);
- const targetCourses = convertToTargetCourses(middleCourses);
- try {
- await window.shiguangBridgePromise.saveImportedCourses(JSON.stringify(targetCourses));
- window.shiguangBridge.showToast(`课程数据已导入(共 ${targetCourses.length} 条)`);
- } catch (saveErr) {
- await window.shiguangBridgePromise.showAlert("保存课程失败", saveErr.message, "确定");
- return;
- }
- const timeSlots = getTimeSlots();
- try {
- await window.shiguangBridgePromise.savePresetTimeSlots(JSON.stringify(timeSlots));
- window.shiguangBridge.showToast("时间段数据已导入");
- } catch (slotErr) {
- window.shiguangBridge.showToast(`时间段保存失败:${slotErr.message}`);
- }
- window.shiguangBridge.showToast("导入完成!");
- window.shiguangBridge.notifyTaskCompletion();
- } catch (err) {
- console.error("run error:", err);
- await window.shiguangBridgePromise.showAlert(
- "导入失败",
- `未知错误:${err.message || err}\n\n请联系开发者。`,
- "确定"
- );
- window.shiguangBridge.notifyTaskCompletion();
- }
- }
- // 启动
- run();
|