Explorar el Código

add: 添加山东轻工职业学院教务适配

localhosts-A hace 1 mes
padre
commit
91b6a2bff9
Se han modificado 3 ficheros con 273 adiciones y 0 borrados
  1. 5 0
      index/root_index.yaml
  2. 9 0
      resources/SDLIVC/adapters.yaml
  3. 259 0
      resources/SDLIVC/sdlivc.js

+ 5 - 0
index/root_index.yaml

@@ -148,6 +148,11 @@ schools:
     initial: "S"
     resource_folder: "SDDFVC"
 
+  - id: "SDLIVC"
+    name: "山东轻工职业学院"
+    initial: "S"
+    resource_folder: "SDLIVC"
+
   - id: "CQCIVC"
     name: "重庆化工职业学院"
     initial: "C"

+ 9 - 0
resources/SDLIVC/adapters.yaml

@@ -0,0 +1,9 @@
+# resources/SDLIVC/adapters.yaml
+adapters:
+  - adapter_id: "SDLIVC"
+    adapter_name: "山东轻工职业学院教务"
+    category: "BACHELOR_AND_ASSOCIATE"
+    asset_js_path: "sdlivc.js"
+    import_url: "https://crp.sdlivc.cn/xsgl/xs/login/login.aspx"
+    maintainer: "赛博丁真"
+    description: "山东轻工职业学院教务适配"

+ 259 - 0
resources/SDLIVC/sdlivc.js

@@ -0,0 +1,259 @@
+// 山东轻工职业学院(sdlivc.cn) 拾光课程表适配脚本
+// 数据来源:教务系统 /jedu/edu/core/eduScheduleInfo/getScheduleNew.do
+
+(function () {
+    const ROOT_PATH = window.__rootPath || '/jedu';
+    const WEEK_MAP = {
+        mon: 1,
+        tue: 2,
+        wed: 3,
+        thu: 4,
+        fri: 5,
+        sat: 6,
+        sun: 7
+    };
+
+    function showToast(message) {
+        if (window.AndroidBridge && typeof window.AndroidBridge.showToast === 'function') {
+            window.AndroidBridge.showToast(message);
+        } else {
+            console.log('[Toast]', message);
+        }
+    }
+
+    function getStudentId() {
+        if (typeof window.stuId === 'string' && window.stuId.trim()) {
+            return window.stuId.trim();
+        }
+
+        const html = document.body ? document.body.innerHTML : '';
+        const match = html.match(/var\s+stuId\s*=\s*["']([^"']+)["']/);
+        return match ? match[1] : '';
+    }
+
+    function getSelectedSemesterId() {
+        if (typeof mini !== 'undefined' && typeof mini.get === 'function') {
+            const picker = mini.get('semId');
+            if (picker && typeof picker.getValue === 'function') {
+                return picker.getValue() || '';
+            }
+        }
+
+        const input = document.querySelector('input[name="semId"], #semId');
+        return input ? input.value || '' : '';
+    }
+
+    function toDayNumber(week) {
+        return WEEK_MAP[String(week || '').toLowerCase()] || null;
+    }
+
+    function expandWeekRange(start, end, parity) {
+        const weeks = [];
+        const from = Math.min(start, end);
+        const to = Math.max(start, end);
+
+        for (let week = from; week <= to; week += 1) {
+            if (parity === 'odd' && week % 2 === 0) continue;
+            if (parity === 'even' && week % 2 !== 0) continue;
+            weeks.push(week);
+        }
+
+        return weeks;
+    }
+
+    function parseWeeks(weekList) {
+        if (!weekList) return [];
+
+        const weeks = new Set();
+        const normalized = String(weekList)
+            .replace(/\s+/g, '')
+            .replace(/,/g, ',')
+            .replace(/(/g, '(')
+            .replace(/)/g, ')')
+            .replace(/第/g, '')
+            .replace(/周/g, '');
+
+        for (const rawPart of normalized.split(',')) {
+            if (!rawPart) continue;
+
+            const parity = rawPart.includes('单') ? 'odd' : rawPart.includes('双') ? 'even' : null;
+            const numberPart = rawPart.replace(/\([^)]*\)/g, '');
+            const rangeMatch = numberPart.match(/^(\d+)-(\d+)$/);
+            const singleMatch = numberPart.match(/^(\d+)$/);
+
+            if (rangeMatch) {
+                expandWeekRange(Number(rangeMatch[1]), Number(rangeMatch[2]), parity)
+                    .forEach(week => weeks.add(week));
+            } else if (singleMatch) {
+                const week = Number(singleMatch[1]);
+                if (parity === 'odd' && week % 2 === 0) continue;
+                if (parity === 'even' && week % 2 !== 0) continue;
+                weeks.add(week);
+            }
+        }
+
+        return Array.from(weeks).sort((a, b) => a - b);
+    }
+
+    function cleanPlaceName(item) {
+        const place = item && item.eduPlace ? item.eduPlace : {};
+        return place.placeName || place.nameIncludeNum || '';
+    }
+
+    function convertScheduleItem(item) {
+        if (!item || item.stopCourse !== 'NO') return null;
+
+        const lesson = item.eduLesson || {};
+        const day = toDayNumber(item.week);
+        const weeks = parseWeeks(item.weekList);
+        const startSection = Number(lesson.startLesson);
+        const endSection = Number(lesson.endLesson);
+
+        if (!item.courseName || !day || !startSection || !endSection || weeks.length === 0) {
+            return null;
+        }
+
+        return {
+            name: item.courseName,
+            teacher: item.teacherName || '',
+            position: cleanPlaceName(item),
+            day,
+            startSection,
+            endSection,
+            weeks,
+            isCustomTime: false
+        };
+    }
+
+    function mergeCourses(courses) {
+        const courseMap = new Map();
+
+        courses.forEach(course => {
+            const key = [
+                course.name,
+                course.teacher,
+                course.position,
+                course.day,
+                course.startSection,
+                course.endSection
+            ].join('|');
+
+            const existing = courseMap.get(key);
+            if (existing) {
+                existing.weeks = Array.from(new Set(existing.weeks.concat(course.weeks))).sort((a, b) => a - b);
+            } else {
+                courseMap.set(key, Object.assign({}, course, {
+                    weeks: Array.from(new Set(course.weeks)).sort((a, b) => a - b)
+                }));
+            }
+        });
+
+        return Array.from(courseMap.values());
+    }
+
+    function getMaxWeek(courses) {
+        const weeks = courses.flatMap(course => Array.isArray(course.weeks) ? course.weeks : []);
+        return weeks.length > 0 ? Math.max.apply(null, weeks) : 20;
+    }
+
+    async function fetchSchedule() {
+        const stuId = getStudentId();
+        if (!stuId) {
+            throw new Error('未找到学生 ID,请先通过学生信息系统进入“学期课表”页面。');
+        }
+
+        const params = new URLSearchParams({
+            semId: getSelectedSemesterId(),
+            stuId,
+            checkType: 'student'
+        });
+
+        const response = await fetch(ROOT_PATH + '/edu/core/eduScheduleInfo/getScheduleNew.do', {
+            method: 'POST',
+            credentials: 'same-origin',
+            headers: {
+                'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
+                'X-Requested-With': 'XMLHttpRequest'
+            },
+            body: params.toString()
+        });
+
+        if (!response.ok) {
+            throw new Error('课表接口请求失败:HTTP ' + response.status);
+        }
+
+        const json = await response.json();
+        if (!json || json.success !== true) {
+            throw new Error((json && json.message) || '课表接口返回失败。');
+        }
+
+        return json.data && Array.isArray(json.data.schedule) ? json.data.schedule : [];
+    }
+
+    async function saveImportedData(courses) {
+        const courseResult = await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(courses));
+        if (courseResult !== true) {
+            throw new Error('课程保存失败:' + courseResult);
+        }
+
+        if (typeof window.AndroidBridgePromise.saveCourseConfig === 'function') {
+            const configResult = await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify({
+                semesterTotalWeeks: getMaxWeek(courses),
+                defaultClassDuration: 45,
+                defaultBreakDuration: 10,
+                firstDayOfWeek: 1
+            }));
+            if (configResult !== true) {
+                throw new Error('课表配置保存失败:' + configResult);
+            }
+        }
+    }
+
+    async function promptUserToStart() {
+        if (!window.AndroidBridgePromise || typeof window.AndroidBridgePromise.showAlert !== 'function') {
+            return true;
+        }
+
+        return await window.AndroidBridgePromise.showAlert(
+            '山东轻工职业学院课表导入',
+            '请确认已登录并进入“学期课表”页面。脚本将读取当前学期课表并导入拾光课程表。',
+            '开始导入'
+        );
+    }
+
+    async function runImportFlow() {
+        try {
+            const confirmed = await promptUserToStart();
+            if (!confirmed) return;
+
+            showToast('正在读取学期课表...');
+            const rawSchedule = await fetchSchedule();
+            const courses = mergeCourses(rawSchedule.map(convertScheduleItem).filter(Boolean));
+
+            if (courses.length === 0) {
+                showToast('未解析到可导入课程');
+                return;
+            }
+
+            await saveImportedData(courses);
+            showToast('成功导入 ' + courses.length + ' 条课程');
+            window.AndroidBridge.notifyTaskCompletion();
+        } catch (error) {
+            console.error('山东轻工职业学院课表导入失败', error);
+            showToast('课表导入失败:' + error.message);
+        }
+    }
+
+    window.__sdlivcImporter = {
+        parseWeeks,
+        toDayNumber,
+        convertScheduleItem,
+        mergeCourses,
+        getMaxWeek,
+        saveImportedData
+    };
+
+    if (window.__SDLIVC_AUTO_RUN__ !== false) {
+        runImportFlow();
+    }
+}());