Kaynağa Gözat

Merge pull request #60 from XingHeYuZhuan/pending

Pending
星河欲转 4 hafta önce
ebeveyn
işleme
b0cdab613d

+ 6 - 1
index/root_index.yaml

@@ -106,4 +106,9 @@ schools:
   - id: "GZST"
     name: "广州松田职业学院"
     initial: "G"
-    resource_folder: "GZST"      
+    resource_folder: "GZST"      
+
+  - id: "FJCPC"
+    name: "福建船政交通职业学院"
+    initial: "F"
+    resource_folder: "FJCPC"        

+ 9 - 0
resources/FJCPC/adapters.yaml

@@ -0,0 +1,9 @@
+# resources/FJCPC/adapters.yaml
+adapters:
+  - adapter_id: "FJCPC"
+    adapter_name: "福建船政交通职业学院教务"
+    category: "BACHELOR_AND_ASSOCIATE"
+    asset_js_path: "fjcpc.js"
+    import_url: "https://webvpn.fjcpc.edu.cn/"
+    maintainer: "星河欲转"
+    description: "点击本校师生统一身份验证进行登录,登录后点击新教务系统在点击教务管理\n 已使用自动获取开学日期,免去手动设置 \n福建船政交通职业学院教务适配,非本校开发者适配如果有误建议提交issues"

+ 191 - 0
resources/FJCPC/fjcpc.js

@@ -0,0 +1,191 @@
+// 福建船政交通职业学院(fjcpc.edu.cn)拾光课程表适配脚本
+// 非该大学开发者适配,开发者无法及时发现问题
+// 出现问题请提联系开发者或者提交pr更改,这更加快速
+
+// 引导
+async function promptUserToStart() {
+    return await window.AndroidBridgePromise.showAlert(
+        "公告",
+        "即将开始导入请确保已经登录并跳转到已显示课表位置",
+        "好的,开始"
+    );
+}
+
+/**
+ * 选择作息时间(双季适配)
+ */
+async function selectTimeSchedule() {
+    const semesters = ["夏季作息", "冬季作息"];
+    
+    const selectedIndex = await window.AndroidBridgePromise.showSingleSelection(
+        "选择作息时间",
+        JSON.stringify(semesters),
+        -1
+    );
+    
+    if (selectedIndex === null) return null;
+
+    // 预设节次数据
+    const summerTime = [
+        { number: 1, startTime: "08:00", endTime: "08:45" },
+        { number: 2, startTime: "08:55", endTime: "09:40" },
+        { number: 3, startTime: "10:00", endTime: "10:45" },
+        { number: 4, startTime: "10:55", endTime: "11:40" },
+        { number: 5, startTime: "14:30", endTime: "15:15" },
+        { number: 6, startTime: "15:25", endTime: "16:10" },
+        { number: 7, startTime: "16:30", endTime: "17:15" },
+        { number: 8, startTime: "17:25", endTime: "18:10" },
+        { number: 9, startTime: "19:20", endTime: "20:05" },
+        { number: 10, startTime: "20:15", endTime: "21:00" }
+    ];
+
+    const winterTime = [
+        { number: 1, startTime: "08:00", endTime: "08:45" },
+        { number: 2, startTime: "08:55", endTime: "09:40" },
+        { number: 3, startTime: "10:00", endTime: "10:45" },
+        { number: 4, startTime: "10:55", endTime: "11:40" },
+        { number: 5, startTime: "14:00", endTime: "14:45" },
+        { number: 6, startTime: "14:55", endTime: "15:40" },
+        { number: 7, startTime: "16:00", endTime: "16:45" },
+        { number: 8, startTime: "16:55", endTime: "17:40" },
+        { number: 9, startTime: "19:00", endTime: "19:45" },
+        { number: 10, startTime: "19:55", endTime: "20:40" }
+    ];
+
+    return selectedIndex === 0 ? summerTime : winterTime;
+}
+
+/**
+ * 获取并解析网络数据
+ */
+async function fetchAndParseData() {
+    // 自动提取凭证
+    const match = document.cookie.match(/Admin-Token=([^;]+)/);
+    if (!match) {
+        AndroidBridge.showToast("未发现登录凭证,请登录后重试");
+        return null;
+    }
+    const token = decodeURIComponent(match[1]);
+
+    try {
+        const response = await fetch("https://211-80-233-108.webvpn.fjcpc.edu.cn/jwxt/manager/bjxx/kb/selectStudentLeader", {
+            "headers": { "authorization": token },
+            "method": "GET"
+        });
+        const json = await response.json();
+        const { xlxx, xskbList } = json.data;
+
+        // 计算日期
+        const date = new Date(xlxx.rq);
+        const day = date.getDay();
+        const diffToMonday = (day === 0 ? 6 : day - 1);
+        const firstMonday = new Date(date);
+        firstMonday.setDate(date.getDate() - diffToMonday - (parseInt(xlxx.zc) - 1) * 7);
+
+        // 解析课程列表
+        const days = { "Mon": 1, "Tues": 2, "Wed": 3, "Thu": 4, "Fri": 5, "Sat": 6, "Sun": 7 };
+        const slots = ["1_2", "3_4", "5_6", "7_8", "9_10", "11_12"];
+        const courses = [];
+
+        for (const [dk, dv] of Object.entries(days)) {
+            slots.forEach(sk => {
+                const raw = xskbList[`${dk}${sk}`];
+                if (raw && raw.includes('◇')) {
+                    raw.split('\n').filter(l => l.includes('◇')).forEach(line => {
+                        const p = line.split('◇');
+                        const timePart = p[1]; 
+                        const secStr = timePart.split('(')[1].replace(')', '');
+                        const sec = secStr.split(',');
+
+                        courses.push({
+                            name: p[0].split('<')[1].trim(),
+                            teacher: p[4].trim(),
+                            position: p[2].trim(),
+                            day: dv,
+                            startSection: parseInt(sec[0]),
+                            endSection: parseInt(sec[1]),
+                            weeks: ((s) => {
+                                const ws = [];
+                                s.split(',').forEach(v => {
+                                    if (v.includes('-')) {
+                                        const [b, e] = v.split('-').map(Number);
+                                        for (let i = b; i <= e; i++) ws.push(i);
+                                    } else ws.push(Number(v));
+                                });
+                                return ws;
+                            })(timePart.split('(')[0])
+                        });
+                    });
+                }
+            });
+        }
+
+        return {
+            config: {
+                semesterStartDate: firstMonday.toISOString().split('T')[0],
+                semesterTotalWeeks: 24,
+                firstDayOfWeek: 1
+            },
+            courses: courses
+        };
+    } catch (e) {
+        AndroidBridge.showToast("数据解析失败: " + e.message);
+        return null;
+    }
+}
+
+// 编排主流程
+
+async function runImportFlow() {
+    AndroidBridge.showToast("课程导入流程开始...");
+
+    // 公告和前置检查
+    const alertConfirmed = await promptUserToStart();
+    if (!alertConfirmed) {
+        AndroidBridge.showToast("用户取消了导入。");
+        return;
+    }
+
+    // 选择作息时间
+    const timeSlots = await selectTimeSchedule();
+    if (timeSlots === null) {
+        AndroidBridge.showToast("已取消选择作息。");
+        return;
+    }
+
+    // 网络请求和数据解析
+    const data = await fetchAndParseData();
+    if (data === null) {
+        return;
+    }
+
+    // 保存课表配置
+    try {
+        await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify(data.config));
+    } catch (e) {
+        AndroidBridge.showToast("保存配置失败");
+        return;
+    }
+
+    // 课程数据保存
+    try {
+        await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(data.courses));
+        AndroidBridge.showToast(`成功抓取 ${data.courses.length} 门课程!`);
+    } catch (e) {
+        AndroidBridge.showToast("保存课程数据失败");
+        return;
+    }
+
+    // 导入时间段
+    try {
+        await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(timeSlots));
+        AndroidBridge.showToast("作息时间同步成功");
+    } catch (e) {
+        console.error("作息保存失败:", e);
+    }
+
+    AndroidBridge.showToast("所有任务已完成!");
+    AndroidBridge.notifyTaskCompletion();
+}
+
+runImportFlow();

+ 57 - 5
resources/GLOBAL_TOOLS/school.js

@@ -87,11 +87,8 @@ async function demoSingleSelection() {
     }
 }
 
-// 仍然可以使用原始的 AndroidBridge 对象
-AndroidBridge.showToast("这是一个来自 JS 的 Toast 消息,会很快消失!");
-
+// 4. 导入课程数据
 async function demoSaveCourses() {
-    // ... 代码保持不变 ...
     console.log("正在准备测试课程数据...");
     const testCourses = [
     {
@@ -103,6 +100,26 @@ async function demoSaveCourses() {
         "endSection": 2,
         "weeks": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
     },
+    {
+        "name": "测试自定义课程1",
+        "teacher": "测试老师1",
+        "position": "测试教室1",
+        "day": 1,
+        "isCustomTime": true,
+        "customStartTime": "08:00",
+        "customEndTime": "09:00",
+        "weeks": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
+    },
+    {
+        "name": "测试自定义课程2",
+        "teacher": "测试老师2",
+        "position": "测试教室2",
+        "day": 3,
+        "isCustomTime": true,
+        "customStartTime": "06:00",
+        "customEndTime": "12:00",
+        "weeks": [3, 5, 7, 9, 11, 13, 15]
+    },
     {
         "name": "大学英语",
         "teacher": "李老师",
@@ -292,7 +309,7 @@ async function demoSaveCourses() {
     }
 }
 
-// 5. 导入预设时间段(保持不变)
+// 5. 导入预设时间段
 async function importPresetTimeSlots() {
     console.log("正在准备预设时间段数据...");
     const presetTimeSlots = [
@@ -330,6 +347,40 @@ async function importPresetTimeSlots() {
     }
 }
 
+// 6. 导入课表配置
+async function demoSaveConfig() {
+    console.log("正在准备配置数据...");
+    // 注意:只传入要修改的字段,其他字段(如 semesterTotalWeeks)会使用 Kotlin 模型中的默认值
+    const courseConfigData = {
+        "semesterStartDate": "2025-09-01",
+        "semesterTotalWeeks": 18,
+        "defaultClassDuration": 50,
+        "defaultBreakDuration": 5,
+        "firstDayOfWeek": 7
+    };
+
+    try {
+        console.log("正在尝试导入课表配置...");
+        const configJsonString = JSON.stringify(courseConfigData);
+
+        const result = await window.AndroidBridgePromise.saveCourseConfig(configJsonString);
+
+        if (result === true) {
+            console.log("课表配置导入成功!");
+            AndroidBridge.showToast("测试配置导入成功!开学日期: 2025-09-01");
+        } else {
+            console.log("课表配置导入未成功,结果:" + result);
+            AndroidBridge.showToast("测试配置导入失败,请查看日志。");
+        }
+    } catch (error) {
+        console.error("导入配置时发生错误:", error);
+        AndroidBridge.showToast("导入配置失败: " + error.message);
+    }
+}
+
+
+AndroidBridge.showToast("这是一个来自 JS 的 Toast 消息,会很快消失!");
+
 /**
  * 编排这些异步操作,并在用户取消时停止后续执行。
  */
@@ -363,6 +414,7 @@ async function runAllDemosSequentially() {
     // 以下是数据导入,与用户交互无关,可以继续
     await demoSaveCourses();
     await importPresetTimeSlots();
+    await demoSaveConfig();
 
     // 发送最终的生命周期完成信号
     AndroidBridge.notifyTaskCompletion();