# fstar-client
**Repository Path**: mikai39/fstar-client
## Basic Information
- **Project Name**: fstar-client
- **Description**: 繁星课程表客户端
- **Primary Language**: Dart
- **License**: BSD-3-Clause
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2021-02-20
- **Last Updated**: 2023-02-26
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# FStar 繁星课程表
## 简介
繁星课程表是一款课程表软件,采用Flutter框架开发,包含通用模式和江苏科技大学专有模式
### 关于本项目开发环境
本项目采用flutter dev分支进行开发,由于flutter不同分支api有变动以及版本升级可能导致相关api被废弃,而项目的依赖没有同步进行升级,
项目运行有可能失败,请自行做相关的调整。文末有本项目可以运行的flutter环境信息以供参考
### 关于跨平台问题
本项目理论上是支持跨平台的,采用dev分支是为了开启Windows和Web平台,由于本项目主要还是在Android和iOS上运行,
所以Windows和Web平台仅供体验,iOS部分由于我现在手上并没有Mac,所以iOS的部分一些第三方依赖所需要的配置并没有做, 需要自行配置
### 通用模式
通用模式采用内置浏览器定位到教务系统课表页执行JavaScript解析函数的方法导入课表
### JUST模式
开发者为江苏科技大学教务系统适配的专用模式,包含成绩查询、课表查询、常用工具等功能
### APP预览
## 繁星课程表适配方法
### 如何导入课表
如果你是**江苏科技大学**的学生,可以在设置中选择**JUST模式**来快捷导入课表,如果你是**其他学校的学生**, 在本页配置学校的课表解析函数(右上角),退出本页点击**课表导入**
,通过浏览器进入到学校的教务系统课表页,然后点击Appbar上的导入按钮即可完成导入
### 课表解析函数的编写(导入课表只看上面部分)
导入课表需要有该学校的课表解析函数(JavaScript),该函数可以由该学校的任意一名学生提供,只需要有一点编程基础和CSS选择器知识即可编写;编写之后把解析函数上传到服务器即可共享
### 课程数据结构规定
```json
{
"name": "MEMS封装技术",
"id": "06050010b-1",
"classroom": "12号楼-214(活动)",
"week": [
6,
7,
8
],
"row": 7,
"column": 5,
"rowSpan": 2,
"teacher": "刘瑞"
}
```
以上为标准的JSON结构
> name 课程名称 字符串
>
> id 课程号 字符串
>
> classroom 教室 字符串
>
> week 课程周数 整型数组
>
> row 课程行定位,从1开始,表示第几小节 数字
>
> column 课程列定位,从1开始,表示星期几 范围1-7 数字
>
> rowSpan 课程跨行,2表示课程横跨两行,相当于两小节 数字
>
> teacher 老师 字符串
>
> 以上字段均不能为null 跨行没有限制,课程可以任意重叠
>
### 发送课表解析结果
调用如下代码将解析结果发送给繁星课程表
```javascript
window.flutter_inappwebview.callHandler('postCourse', JSON.stringify({'course': course, 'remark': remark}));
```
> course是一个课程数组 remark为备注信息,可选,会显示到课表底部
**注意** 手动填入解析函数的时候要特别注意换行问题,如果没有换行,所有的代码会变成一行,如果代码中带有注释则会出错!
### 解析函数例子
#### 以下为江苏科技大学本科生课表解析函数
```javascript
(function () {
let course = [];
let trs = document.querySelectorAll('#kbtable tr');
let remark = trs[trs.length - 1].textContent;//备注
for (let i = 1; i < trs.length - 1; i++) {
let contents = trs[i].querySelectorAll('td .kbcontent')
contents.forEach((value, key) => {
let c = parse(value.innerHTML, i, key + 1);
course.push(...c)
});
}
//解析课表
function parse(content, row, column) {
let eachContent = content.split(/-{5,}
/);
let result = [];
eachContent.forEach(value => {
let regx = /(.*?)
(.*?)
<.*?>(.*?)<\/font>.*?">(.*?)\(周\)/
let results = regx.exec(value);
if (results != null) {
let id = results[1];
let name = results[2];
let teacher = results[3];
let week = parseWeek(results[4]);
let roomRegx = /(.*?)<\/font>/
let roomResult = roomRegx.exec(value)
let room = roomResult !== null ? roomResult[1] : '';
let course = {
'name': name,
'id': id,
'classroom': room,
'week': week,
'row': row * 2 - 1,
'column': column,
'rowSpan': 2,
'teacher': teacher,
};
result.push(course);
}
});
return result;
}
function parseWeek(rawWeek) {
let eachPart = rawWeek.split(',');
let result = [];
eachPart.forEach(part => {
if (part.indexOf('-') !== -1) {
let [begin, end] = part.split('-');
for (let i = Number(begin); i < Number(end) + 1; i++) {
result.push(i);
}
} else {
result.push(Number(part));
}
});
let setResult = new Set(result);
return Array.from(setResult);
}
window.flutter_inappwebview.callHandler('postCourse', JSON.stringify({'course': course, 'remark': remark}));
})();
```
#### 以下为江苏科技大学研究生课表解析函数
预处理函数
因为研究生系统含有多个iframe,需要用js代码跳转到课表页的iframe执行课表解析函数才能成功导入课表
```javascript
window.location = 'http://yjsinfo.just.edu.cn/pyxx/pygl/kbcx_xs.aspx';
```
#### 课表解析函数
```javascript
(function () {
let course = [];
let trs = document.querySelectorAll('#DataGrid1 tr');
for (let i = 1; i < trs.length; i++) {
let contents = trs[i].querySelectorAll('td');
let filterResult = [];
contents.forEach(value => {
if (value.innerHTML !== '上午' && value.innerHTML !== '下午' && value.innerHTML !== '晚上') {
if (isNaN(parseInt(value.innerHTML))) {
filterResult.push(value);
}
}
})
filterResult.forEach((value, key) => {
let rowSpan = value.getAttribute('rowSpan');
let c = parse(value.innerHTML, i, key + 1, parseInt(rowSpan));
course.push(...c);
});
}
function parse(content, row, column, rowSpan) {
let eachContent = content.split(/
/);
let result = [];
eachContent.forEach(value => {
let regx = /课程:(.*?)
班级:(.*?)
\((.*?)\)
第(.*?)周;
主讲教师:(.*?)/
let results = regx.exec(value);
if (results != null) {
let id = results[2].trim();
let name = results[1].trim();
let teacher = results[5].trim();
let week = parseWeek(results[4]);
let room = results[3].trim();
let course = {
'name': name,
'id': id,
'classroom': room,
'week': week,
'row': row,
'column': column,
'rowSpan': rowSpan,
'teacher': teacher,
};
result.push(course);
}
});
return result;
}
function parseWeek(rawWeek) {
let eachPart = rawWeek.split(',');
let result = [];
eachPart.forEach(part => {
if (part.indexOf('-') !== -1) {
let [begin, end] = part.split('-');
for (let i = Number(begin); i < Number(end) + 1; i++) {
result.push(i);
}
} else {
result.push(Number(part));
}
});
let setResult = new Set(result);
return Array.from(setResult);
}
window.flutter_inappwebview.callHandler('postCourse', JSON.stringify({'course': course}));
})();
```
## 开发环境
### 本项目采用的是Flutter的dev分支进行开发的,并非stable分支
```
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel dev, 2.1.0-12.1.pre, on Microsoft Windows [Version 10.0.19042.867], locale zh-CN)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
[✓] Chrome - develop for the web
[✓] Visual Studio - develop for Windows (Visual Studio Enterprise 2019 16.8.2)
[✓] Android Studio (version 4.1.0)
[✓] IntelliJ IDEA Ultimate Edition (version 2020.3)
[✓] VS Code (version 1.45.1)
[✓] Connected device (4 available)
```
## Flutter学习
### [Flutter官网](https://flutter.dev/)
### [Flutter中文网](https://flutterchina.club/)
### [Flutter实战](https://book.flutterchina.club/)