| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- /**
- * 星链课表分享导入脚本
- */
- // 工具函数
- // 输入验证
- function validateInput(input) {
- if (!input || input.trim().length === 0) return "请输入分享码!";
- return false;
- }
- // 提取分享码
- function extractShareCode(text) {
- const structuredRegex = /输入:\s*([^(\s]+)/;
- const fallbackRegex = /([a-zA-Z0-9-]{5,20})/;
- const matchA = text.match(structuredRegex);
- if (matchA && matchA[1]) return matchA[1].trim();
- const matchB = text.match(fallbackRegex);
- if (matchB) return matchB[1].trim();
- return text.trim();
- }
- // 课程合并与去重函数
- /**
- * 节次与周次合并去重函数
- * @param {Array<Object>} courses 原始解析课程数组
- * @returns {Array<Object>} 合并去重后的课程数组
- */
- function mergeAndDistinctCourses(courses) {
- if (!Array.isArray(courses) || courses.length <= 1) return courses;
- // 1. 深拷贝并规范周次数据,过滤无效项
- const list = courses.map(c => ({
- ...c,
- name: c.name || '',
- teacher: c.teacher || '',
- position: c.position || '',
- weeks: Array.isArray(c.weeks) ? [...c.weeks].sort((a, b) => a - b) : []
- }));
- // 阶段 1:合并连续节次与完全重复记录
- list.sort((a, b) => {
- return a.name.localeCompare(b.name) ||
- a.teacher.localeCompare(b.teacher) ||
- a.position.localeCompare(b.position) ||
- (a.day || 0) - (b.day || 0) ||
- a.weeks.join(',').localeCompare(b.weeks.join(',')) ||
- (a.startSection || 0) - (b.startSection || 0);
- });
- const step1Merged = [];
- let current = list[0];
- for (let i = 1; i < list.length; i++) {
- const next = list[i];
- const isSameCourseAndWeeks =
- current.name === next.name &&
- current.teacher === next.teacher &&
- current.position === next.position &&
- current.day === next.day &&
- current.weeks.join(',') === next.weeks.join(',');
- const isContinuous = current.endSection + 1 === next.startSection;
- const isDuplicate = current.startSection === next.startSection && current.endSection === next.endSection;
- if (isSameCourseAndWeeks && isContinuous) {
- // 节次连续:延长结束节次
- current.endSection = next.endSection;
- } else if (isSameCourseAndWeeks && isDuplicate) {
- // 完全重复:跳过
- continue;
- } else {
- step1Merged.push(current);
- current = next;
- }
- }
- step1Merged.push(current);
- // 阶段 2:合并同节次的周次
- step1Merged.sort((a, b) => {
- return a.name.localeCompare(b.name) ||
- a.teacher.localeCompare(b.teacher) ||
- a.position.localeCompare(b.position) ||
- (a.day || 0) - (b.day || 0) ||
- (a.startSection || 0) - (b.startSection || 0) ||
- (a.endSection || 0) - (b.endSection || 0);
- });
- const step2Merged = [];
- let cur = step1Merged[0];
- for (let i = 1; i < step1Merged.length; i++) {
- const nxt = step1Merged[i];
- const isSameCourseAndSection =
- cur.name === nxt.name &&
- cur.teacher === nxt.teacher &&
- cur.position === nxt.position &&
- cur.day === nxt.day &&
- cur.startSection === nxt.startSection &&
- cur.endSection === nxt.endSection;
- if (isSameCourseAndSection) {
- // 周次合并去重
- cur.weeks = Array.from(new Set([...cur.weeks, ...nxt.weeks])).sort((a, b) => a - b);
- } else {
- step2Merged.push(cur);
- cur = nxt;
- }
- }
- step2Merged.push(cur);
- return step2Merged;
- }
- // 时间转换函数
- // 将分钟数转换为 HH:mm 格式
- function minutesToTime(minutes) {
- const hours = Math.floor(minutes / 60);
- const mins = minutes % 60;
- return `${String(hours).padStart(2, '0')}:${String(mins).padStart(2, '0')}`;
- }
- // 将星链的 sectionMinutes 转换为标准时间段数据
- function convertTimeSlots(sectionMinutes) {
- if (!sectionMinutes) return [];
-
- const timeSlots = [];
- for (const [number, [startMin, endMin]] of Object.entries(sectionMinutes)) {
- timeSlots.push({
- number: parseInt(number),
- startTime: minutesToTime(startMin),
- endTime: minutesToTime(endMin)
- });
- }
- timeSlots.sort((a, b) => a.number - b.number);
- return timeSlots;
- }
- // 主流程
- async function runStarlinkImport() {
- try {
- // 获取用户输入
- const userInput = await window.shiguangBridgePromise.showPrompt(
- "导入星链课表",
- "请粘贴分享文案(包含分享码)",
- "",
- "validateInput"
- );
- if (!userInput) return;
- const shareCode = extractShareCode(userInput);
- const apiUrl = `https://api.starlinkkb.cn/share/curriculum/${shareCode}`;
-
- window.shiguangBridge.showToast("正在同步云端数据...");
- // 请求数据
- const response = await fetch(apiUrl);
- if (!response.ok) throw new Error("分享码已失效或网络异常");
- const resJson = await response.json();
- const data = resJson.data;
- if (!data || !data.courses || data.courses.length === 0) {
- throw new Error("未获取到课程数据");
- }
- // 数据映射 - 转换为标准格式
- const rawCourses = data.courses.map(c => ({
- name: c.name || "未命名课程",
- teacher: (c.teacher && c.teacher !== "无") ? c.teacher : "未知教师",
- position: (c.location && c.location.replace(/^@/, '').trim() !== "")
- ? c.location.replace(/^@/, '').trim()
- : "未排地点",
- day: c.weekday || 1,
- startSection: c.startSection || 1,
- endSection: c.endSection || 1,
- weeks: c.weeks || []
- }));
- // 使用官方合并去重函数处理课程数据
- const finalCourses = mergeAndDistinctCourses(rawCourses);
- // 构建标准课程数据
- const standardCourses = finalCourses.map(c => ({
- name: c.name,
- teacher: c.teacher,
- position: c.position,
- day: c.day,
- startSection: c.startSection,
- endSection: c.endSection,
- weeks: c.weeks
- }));
- // 构建配置数据
- const config = {
- semesterStartDate: data.startDate ? data.startDate.substring(0, 10) : null,
- semesterTotalWeeks: data.totalWeeks || 20
- };
- // 转换时间段数据
- const timeSlots = convertTimeSlots(data.sectionMinutes || {});
- // 保存配置
- if (config.semesterStartDate) {
- try {
- await window.shiguangBridgePromise.saveCourseConfig(JSON.stringify(config));
- window.shiguangBridge.showToast("课表配置已更新");
- } catch (e) {
- console.warn("保存配置失败:", e.message);
- }
- }
- // 保存时间段
- if (timeSlots.length > 0) {
- try {
- await window.shiguangBridgePromise.savePresetTimeSlots(JSON.stringify(timeSlots));
- window.shiguangBridge.showToast("已导入 " + timeSlots.length + " 个时间段");
- } catch (e) {
- console.warn("保存时间段失败:", e.message);
- }
- }
- // 保存课程数据
- const success = await window.shiguangBridgePromise.saveImportedCourses(
- JSON.stringify(standardCourses)
- );
- if (success) {
- window.shiguangBridge.showToast("成功导入 " + standardCourses.length + " 门课程");
- window.shiguangBridge.notifyTaskCompletion();
- }
- } catch (e) {
- await window.shiguangBridgePromise.showAlert(
- "导入失败",
- e.message || "未知错误,请检查网络或分享码",
- "确定"
- );
- }
- }
- // 启动导入
- runStarlinkImport();
|