├── miniprogram ├── custom-tab-bar │ ├── index.json │ ├── index.js │ ├── index.wxml │ └── index.wxss ├── box.png ├── ec-canvas │ ├── ec-canvas.json │ ├── ec-canvas.wxss │ ├── ec-canvas.wxml │ ├── wx-canvas.js │ └── ec-canvas.js ├── icon │ ├── check-exam.png │ ├── check-grade.png │ ├── check-courses.png │ ├── check-schedule.png │ ├── check-exam-selected.png │ ├── check-grade-selected.png │ ├── check-courses-selected.png │ └── check-schedule-selected.png ├── pages │ ├── previousScore │ │ ├── previousScore.json │ │ ├── previousScore.wxss │ │ ├── previousScore.ts │ │ └── previousScore.wxml │ ├── CET │ │ ├── CET.json │ │ ├── CET.ts │ │ ├── CET.wxss │ │ └── CET.wxml │ ├── exam │ │ ├── exam.json │ │ ├── exam.wxml │ │ ├── exam.wxss │ │ └── exam.ts │ ├── index │ │ ├── index.json │ │ ├── index.wxss │ │ ├── index.wxml │ │ └── index.ts │ ├── login │ │ ├── login.json │ │ ├── login.wxss │ │ ├── login.wxml │ │ └── login.ts │ ├── entireClass │ │ ├── entireClass.json │ │ ├── entireClass.wxss │ │ ├── entireClass.wxml │ │ └── entireClass.ts │ ├── scoreList │ │ ├── scoreList.json │ │ ├── scoreList.wxss │ │ └── scoreList.wxml │ ├── more │ │ ├── more.json │ │ ├── more.wxss │ │ ├── more.wxml │ │ └── more.ts │ ├── scoreDetailPage │ │ ├── scoreDetailPage.json │ │ ├── scoreDetailPage.wxss │ │ ├── scoreDetailPage.ts │ │ └── scoreDetailPage.wxml │ └── overAllPage │ │ ├── overAllPage.json │ │ ├── overAllPage.wxss │ │ ├── overAllPage.wxml │ │ └── overAllPage.ts ├── sitemap.json ├── theme.json ├── utils │ ├── util.js │ └── filter.wxs └── app.json ├── typings ├── index.d.ts ├── declaration.d.ts └── bjut │ └── index.d.ts ├── .gitmodules ├── tsconfig.json ├── .gitignore ├── .idea ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── misc.xml ├── .gitignore ├── vcs.xml ├── compiler.xml ├── dictionaries │ └── ze3kr.xml ├── BjutHelper-MiniProgram.iml ├── modules.xml └── inspectionProfiles │ └── Project_Default.xml ├── CHANGELOG.md ├── package.json ├── project.config.json ├── README.md └── tslint.json /miniprogram/custom-tab-bar/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true 3 | } -------------------------------------------------------------------------------- /miniprogram/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildHelper/MiniProgram/HEAD/miniprogram/box.png -------------------------------------------------------------------------------- /miniprogram/ec-canvas/ec-canvas.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /miniprogram/ec-canvas/ec-canvas.wxss: -------------------------------------------------------------------------------- 1 | .ec-canvas { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /typings/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /miniprogram/icon/check-exam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildHelper/MiniProgram/HEAD/miniprogram/icon/check-exam.png -------------------------------------------------------------------------------- /miniprogram/icon/check-grade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildHelper/MiniProgram/HEAD/miniprogram/icon/check-grade.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "typings/wx"] 2 | path = typings/wx 3 | url = https://github.com/wechat-miniprogram/api-typings 4 | -------------------------------------------------------------------------------- /miniprogram/icon/check-courses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildHelper/MiniProgram/HEAD/miniprogram/icon/check-courses.png -------------------------------------------------------------------------------- /miniprogram/icon/check-schedule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildHelper/MiniProgram/HEAD/miniprogram/icon/check-schedule.png -------------------------------------------------------------------------------- /miniprogram/pages/previousScore/previousScore.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {}, 3 | "navigationBarTitleText": "各学期成绩" 4 | } -------------------------------------------------------------------------------- /miniprogram/ec-canvas/ec-canvas.wxml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /miniprogram/icon/check-exam-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildHelper/MiniProgram/HEAD/miniprogram/icon/check-exam-selected.png -------------------------------------------------------------------------------- /miniprogram/icon/check-grade-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildHelper/MiniProgram/HEAD/miniprogram/icon/check-grade-selected.png -------------------------------------------------------------------------------- /miniprogram/pages/CET/CET.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {}, 3 | "navigationBarTitleText": "四六级成绩", 4 | "enablePullDownRefresh": true 5 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "es2015", 6 | "dom" 7 | ] 8 | } 9 | } -------------------------------------------------------------------------------- /miniprogram/icon/check-courses-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildHelper/MiniProgram/HEAD/miniprogram/icon/check-courses-selected.png -------------------------------------------------------------------------------- /miniprogram/icon/check-schedule-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WildHelper/MiniProgram/HEAD/miniprogram/icon/check-schedule-selected.png -------------------------------------------------------------------------------- /miniprogram/pages/exam/exam.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {}, 3 | "navigationBarTitleText": "考试时间", 4 | "enablePullDownRefresh": true 5 | } -------------------------------------------------------------------------------- /miniprogram/pages/index/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {}, 3 | "navigationBarTitleText": "课表 - 野生助手", 4 | "enablePullDownRefresh": true 5 | } -------------------------------------------------------------------------------- /typings/declaration.d.ts: -------------------------------------------------------------------------------- 1 | declare function requirePlugin(pluginName: string): any; 2 | declare function require(pluginName: string): any; 3 | type TODO = any; 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | /miniprogram/miniprogram_npm 4 | /miniprogram/pages/*/*.js 5 | /miniprogram/app.js 6 | /miniprogram/ec-canvas/echarts.js 7 | package-lock.json 8 | -------------------------------------------------------------------------------- /miniprogram/pages/login/login.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "mp-loading": "/miniprogram_npm/weui-miniprogram/loading/loading" 4 | }, 5 | "navigationBarTitleText": "野生助手" 6 | } -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /miniprogram/pages/entireClass/entireClass.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "mp-msg": "/miniprogram_npm/weui-miniprogram/msg/msg" 4 | }, 5 | "navigationBarTitleText": "选课指导", 6 | "enablePullDownRefresh": true 7 | } -------------------------------------------------------------------------------- /miniprogram/pages/scoreList/scoreList.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "mp-halfScreenDialog": "/miniprogram_npm/weui-miniprogram/half-screen-dialog/half-screen-dialog" 4 | }, 5 | "navigationBarTitleText": "成绩详情 - 野生助手", 6 | "enablePullDownRefresh": true 7 | } -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /miniprogram/pages/more/more.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "mp-actionSheet": "/miniprogram_npm/weui-miniprogram/actionsheet/actionsheet", 4 | "mp-msg": "/miniprogram_npm/weui-miniprogram/msg/msg" 5 | }, 6 | "navigationBarTitleText": "关于" 7 | } 8 | -------------------------------------------------------------------------------- /miniprogram/pages/scoreDetailPage/scoreDetailPage.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "ec-canvas": "../../ec-canvas/ec-canvas", 4 | "mp-halfScreenDialog": "/miniprogram_npm/weui-miniprogram/half-screen-dialog/half-screen-dialog" 5 | }, 6 | "navigationBarTitleText": "课程详情" 7 | } -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /miniprogram/sitemap.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", 3 | "rules": [{ 4 | "action": "allow", 5 | "page": "pages/login/login" 6 | }, { 7 | "action": "disallow", 8 | "page": "*" 9 | }] 10 | } -------------------------------------------------------------------------------- /miniprogram/pages/overAllPage/overAllPage.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "mp-msg": "/miniprogram_npm/weui-miniprogram/msg/msg", 4 | "mp-loading": "/miniprogram_npm/weui-miniprogram/loading/loading" 5 | }, 6 | "navigationBarTitleText": "野生助手", 7 | "enablePullDownRefresh": true 8 | } -------------------------------------------------------------------------------- /.idea/dictionaries/ze3kr.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | affero 5 | bjut 6 | echarts 7 | pako 8 | weixin 9 | weui 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/BjutHelper-MiniProgram.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /miniprogram/theme.json: -------------------------------------------------------------------------------- 1 | { 2 | "light": { 3 | "navBgColor": "#FFF", 4 | "navTxtStyle": "black", 5 | "bgColor": "#e5e9eb", 6 | "bgTxtStyle": "dark" 7 | }, 8 | "dark": { 9 | "navBgColor": "#121212", 10 | "navTxtStyle": "white", 11 | "bgColor": "#000", 12 | "bgTxtStyle": "light" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 小程序版本更新日志 2 | 3 | ## v4.2.9 4 | 5 | + 第一个开源版本,改进了广告加载,上线了 Banner Ad,重写了部分重复逻辑 6 | 7 | ## v4.2.10 8 | 9 | + `A` 数据预拉取支持数据加密和数据压缩 10 | + `M` 修复了几个数据预拉取的 Bug 11 | 12 | ## v4.2.11 13 | 14 | + `A` 出分提醒消息跳转,支持高亮显示教师 15 | + `F` 修复单页模式白屏 16 | + `F` 修复预拉取在页面加载完成前拉取到数据则不会使用预拉取数据的问题 17 | + `F` 修复无法重置订阅的问题 18 | + `F` 修复成绩页无法二次分享的问题 19 | + `F` 修复无法清除预拉取数据的问题 20 | -------------------------------------------------------------------------------- /miniprogram/custom-tab-bar/index.js: -------------------------------------------------------------------------------- 1 | Component({ 2 | data: { 3 | selected: 0, 4 | color: "#595959", 5 | selectedColor: "#0197c2", 6 | list: wx.$initTab(undefined) 7 | }, 8 | methods: { 9 | switchTab(e) { 10 | const data = e.currentTarget.dataset 11 | const url = data.path 12 | console.log(data, this) 13 | wx.$initTab(this); 14 | wx.switchTab({url}) 15 | }, 16 | attached() { 17 | }, 18 | } 19 | }) -------------------------------------------------------------------------------- /miniprogram/custom-tab-bar/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{item.text}} 7 | 8 | 9 | -------------------------------------------------------------------------------- /miniprogram/utils/util.js: -------------------------------------------------------------------------------- 1 | const formatTime = date => { 2 | const year = date.getFullYear() 3 | const month = date.getMonth() + 1 4 | const day = date.getDate() 5 | const hour = date.getHours() 6 | const minute = date.getMinutes() 7 | const second = date.getSeconds() 8 | 9 | return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':') 10 | } 11 | 12 | const formatNumber = n => { 13 | n = n.toString() 14 | return n[1] ? n : '0' + n 15 | } 16 | 17 | module.exports = { 18 | formatTime: formatTime 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wild-helper-mini-program", 3 | "version": "1.0.0", 4 | "description": "野生助手小程序", 5 | "main": "app.js", 6 | "dependencies": { 7 | "pako": "^1.0.11", 8 | "crypto-js": "^4.0.0", 9 | "weui-miniprogram": "^1.0.4" 10 | }, 11 | "devDependencies": { 12 | "tslint": "^6.1.2", 13 | "typescript": "^3.9.5" 14 | }, 15 | "scripts": { 16 | "test": "echo \"Error: no test specified\" && exit 1" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/WildHelper/MiniProgram.git" 21 | }, 22 | "author": "", 23 | "license": "ISC", 24 | "bugs": { 25 | "url": "https://github.com/WildHelper/MiniProgram/issues" 26 | }, 27 | "homepage": "https://github.com/WildHelper/MiniProgram#readme" 28 | } 29 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | -------------------------------------------------------------------------------- /miniprogram/pages/login/login.wxss: -------------------------------------------------------------------------------- 1 | /*! 2 | * WeUI v2.3.0 (https://github.com/weui/weui-wxss) 3 | * Copyright 2020 Tencent, Inc. 4 | * Licensed under the MIT license 5 | */ 6 | 7 | .page{ 8 | height:100% 9 | } 10 | 11 | .weui-label{ 12 | width: 2.1em 13 | } 14 | 15 | .imgArea { 16 | height: 90px; 17 | } 18 | 19 | .img { 20 | height: 75px; 21 | width: 75px; 22 | margin: auto; 23 | } 24 | 25 | .weui-form__control-area { 26 | margin-top: 50rpx; 27 | margin-bottom: 30rpx; 28 | margin-left: 50rpx; 29 | margin-right: 50rpx; 30 | overflow: hidden; 31 | border-radius: 20rpx; 32 | box-shadow: 0rpx 0rpx 15rpx #CCC; 33 | } 34 | 35 | .weui-cell__bd { 36 | padding: 0 16px; 37 | } 38 | 39 | .weui-cells__title { 40 | padding: 0 32px; 41 | } 42 | 43 | .weui-cell:before { 44 | left: 32px; 45 | right: 32px; 46 | } 47 | 48 | .weui-cell_select .weui-cell__bd:after { 49 | right: 32px; 50 | } 51 | 52 | .weui-cells { 53 | margin-top: 0; 54 | background-color: #FFF; 55 | } 56 | -------------------------------------------------------------------------------- /miniprogram/pages/exam/exam.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 暂无考试信息 5 | 6 | {{item}} 7 | 8 | 9 | 10 | 11 | 12 | {{item.courseName}} 13 | 已结束 14 | 15 | 16 | 17 | 考试日期:{{item.time}} 18 | 考试地点:{{item.room}} 19 | 考场校区:{{item.campus}} 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /miniprogram/custom-tab-bar/index.wxss: -------------------------------------------------------------------------------- 1 | .tab-bar { 2 | position: fixed; 3 | bottom: 0; 4 | left: 0; 5 | right: 0; 6 | height: 55px; 7 | background: white; 8 | display: flex; 9 | padding-bottom: env(safe-area-inset-bottom); 10 | z-index: 100; 11 | } 12 | 13 | @media (prefers-color-scheme: dark) { 14 | .tab-bar { 15 | background: #121212; 16 | } 17 | .tab-bar-item-selected-false { 18 | -webkit-filter: invert(100%); 19 | filter: invert(100%); 20 | } 21 | } 22 | 23 | .tab-bar-border { 24 | background-color: rgba(0, 0, 0, 0.33); 25 | position: absolute; 26 | left: 0; 27 | top: 0; 28 | width: 100%; 29 | height: 1px; 30 | transform: scaleY(0.5); 31 | } 32 | 33 | .tab-bar-item { 34 | flex: 1; 35 | text-align: center; 36 | display: flex; 37 | justify-content: center; 38 | align-items: center; 39 | flex-direction: column; 40 | } 41 | 42 | .tab-bar-item image { 43 | width: 20px; 44 | height: 20px; 45 | margin: 8px 0 5px; 46 | } 47 | 48 | .tab-bar-item[data-path="/pages/exam/exam"] image { 49 | width: 19px; 50 | height: 19px; 51 | padding-top: 1px; 52 | } 53 | 54 | .tab-bar-item[data-path="/pages/entireClass/entireClass"] image { 55 | width: 21px; 56 | height: 21px; 57 | margin-top: 7px; 58 | } 59 | 60 | .tab-bar-item { 61 | font-size: 10px; 62 | } 63 | -------------------------------------------------------------------------------- /miniprogram/pages/more/more.wxss: -------------------------------------------------------------------------------- 1 | .previousAndCETScore { 2 | margin: 30rpx 40rpx!important; 3 | padding: 30rpx 30rpx; 4 | border-radius: 20rpx; 5 | box-shadow: 0rpx 0rpx 15rpx #CCC; 6 | font-size: 40rpx; 7 | font-weight:bold; 8 | display: flex; 9 | background-color: #FFF; 10 | width: initial!important; 11 | text-align: left!important; 12 | } 13 | 14 | .title { 15 | flex: 9; 16 | } 17 | 18 | .message { 19 | margin: 30rpx 40rpx; 20 | color: #888888; 21 | font-size: 25rpx; 22 | } 23 | 24 | .message icon, .message text { 25 | margin-top: auto; 26 | margin-bottom: auto; 27 | } 28 | 29 | .h1 { 30 | font-size: 49rpx; 31 | } 32 | 33 | .h2 { 34 | font-size: 45rpx; 35 | } 36 | 37 | .h3 { 38 | font-size: 41rpx; 39 | } 40 | 41 | .h4 { 42 | font-size: 37rpx; 43 | } 44 | 45 | .h5 { 46 | font-size: 33rpx; 47 | } 48 | 49 | .h6 { 50 | font-size: 29rpx; 51 | } 52 | 53 | .list { 54 | margin: 0 40rpx 0 80rpx; 55 | list-style-type: disc; 56 | display: list-item; 57 | } 58 | 59 | .numbered-list { 60 | margin: 0 40rpx 0 80rpx; 61 | list-style-type: decimal; 62 | display: list-item; 63 | } 64 | 65 | .weui-msg__icon-area { 66 | margin-bottom: 16px!important; 67 | } 68 | .weui-msg__text-area { 69 | margin-bottom: 0!important; 70 | } 71 | .weui-msg__tips-area, .weui-msg__extra-area { 72 | display: none!important; 73 | } 74 | -------------------------------------------------------------------------------- /miniprogram/pages/exam/exam.wxss: -------------------------------------------------------------------------------- 1 | .info { 2 | color: #888888; 3 | text-align: center; 4 | margin: 30rpx 40rpx; 5 | background-color: #FFF; 6 | border-radius: 20rpx; 7 | box-shadow: 0rpx 0rpx 15rpx #CCC; 8 | } 9 | 10 | .classes { 11 | margin: 30rpx 40rpx; 12 | background-color: #FFF; 13 | border-radius: 20rpx; 14 | box-shadow: 0rpx 0rpx 15rpx #CCC; 15 | } 16 | .classes .line1{ 17 | display: flex; 18 | margin-bottom: 30rpx; 19 | } 20 | .classes .line1 .name{ 21 | font-size: 35rpx; 22 | display: inline-block; 23 | white-space: nowrap; 24 | overflow: hidden; 25 | text-overflow: ellipsis; 26 | width: 60vw; 27 | } 28 | .classes .line1 .isOver{ 29 | margin-top: auto; 30 | font-size: 25rpx; 31 | color: red; 32 | } 33 | .classes .line2{ 34 | display: flex; 35 | flex-direction: column; 36 | margin-bottom: 20rpx; 37 | } 38 | .classes .line2 .item { 39 | flex: 1; 40 | color: #888888; 41 | } 42 | view.page { 43 | padding-bottom: 80px; 44 | } 45 | 46 | @supports (padding-bottom: calc(55px + 50rpx)) { 47 | view.page { 48 | padding-bottom: calc(55px + 50rpx); 49 | } 50 | } 51 | 52 | @supports (padding-bottom: calc(55px + 50rpx + env(safe-area-inset-bottom))) { 53 | view.page { 54 | padding-bottom: calc(55px + 50rpx + env(safe-area-inset-bottom)); 55 | } 56 | } 57 | 58 | .message { 59 | margin: 30rpx 40rpx; 60 | color: #888888; 61 | font-size: 25rpx; 62 | text-align: center; 63 | } 64 | -------------------------------------------------------------------------------- /miniprogram/pages/previousScore/previousScore.wxss: -------------------------------------------------------------------------------- 1 | .line{ 2 | width: 100%; /* 分隔线的长度 */ 3 | margin: auto; 4 | height: 1px; /* 分隔线粗细 */ 5 | text-align: center; 6 | font-size: 24rpx; 7 | color: #aaa; 8 | background: #ccc; 9 | margin-top: 30rpx; 10 | margin-bottom: 5rpx; 11 | flex: 1; 12 | } 13 | .arrow{ 14 | width: 10px; 15 | height: 10px; 16 | border-top: 2px solid #999; 17 | border-right: 2px solid #999; 18 | 19 | right: 20rpx; 20 | transform: rotate(45deg); 21 | margin-top:20rpx; 22 | } 23 | .title { 24 | flex: 9; 25 | } 26 | 27 | 28 | .totalScore { 29 | margin: 30rpx 40rpx; 30 | padding: 10rpx 30rpx 20rpx 30rpx; 31 | border-radius: 20rpx; 32 | box-shadow: 0rpx 0rpx 15rpx #CCC; 33 | background-color: #FFF; 34 | } 35 | .totalScore .line1 { 36 | font-size: 40rpx; 37 | font-weight:bold; 38 | margin: 10rpx 0; 39 | display: flex; 40 | } 41 | .totalScore .line2 { 42 | margin-bottom: 30rpx; 43 | } 44 | .totalScore .line3 { 45 | display: flex; 46 | } 47 | .totalScore .line3 .item { 48 | flex: 1; 49 | margin-right: 20rpx; 50 | border-right: 1rpx solid rgba(204, 204, 204, 0.4); 51 | } 52 | .totalScore .line3 .item_1 { 53 | flex: 1; 54 | margin-right: 20rpx; 55 | } 56 | .totalScore .line3 .title { 57 | font-size: 25rpx; 58 | color: #999999; 59 | } 60 | .totalScore .line3 .value { 61 | font-size: 50rpx; 62 | } 63 | .totalScore .time { 64 | font-size: 25rpx; 65 | text-align: right; 66 | color: #c4c4c4; 67 | } -------------------------------------------------------------------------------- /project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "项目配置文件", 3 | "packOptions": { 4 | "ignore": [] 5 | }, 6 | "setting": { 7 | "urlCheck": false, 8 | "es6": true, 9 | "enhance": true, 10 | "postcss": true, 11 | "preloadBackgroundData": true, 12 | "minified": true, 13 | "newFeature": true, 14 | "coverView": true, 15 | "nodeModules": true, 16 | "autoAudits": false, 17 | "showShadowRootInWxmlPanel": true, 18 | "scopeDataCheck": false, 19 | "uglifyFileName": true, 20 | "checkInvalidKey": true, 21 | "checkSiteMap": true, 22 | "uploadWithSourceMap": true, 23 | "compileHotReLoad": false, 24 | "babelSetting": { 25 | "ignore": [], 26 | "disablePlugins": [], 27 | "outputPath": "" 28 | }, 29 | "bundle": true, 30 | "useIsolateContext": true, 31 | "useCompilerModule": true, 32 | "userConfirmedUseCompilerModuleSwitch": true, 33 | "packNpmManually": true, 34 | "packNpmRelationList": [ 35 | { 36 | "packageJsonPath": "./package.json", 37 | "miniprogramNpmDistDir": "./miniprogram" 38 | } 39 | ] 40 | }, 41 | "compileType": "miniprogram", 42 | "libVersion": "2.12.0", 43 | "appid": "wx07fc056677624f68", 44 | "projectname": "%E5%B7%A5%E5%A4%A7%E5%8A%A9%E6%89%8B", 45 | "debugOptions": { 46 | "hidedInDevtools": [] 47 | }, 48 | "isGameTourist": false, 49 | "simulatorType": "wechat", 50 | "simulatorPluginLibVersion": {}, 51 | "miniprogramRoot": "miniprogram/", 52 | "condition": { 53 | "search": { 54 | "current": -1, 55 | "list": [] 56 | }, 57 | "conversation": { 58 | "current": -1, 59 | "list": [] 60 | }, 61 | "game": { 62 | "currentL": -1, 63 | "list": [] 64 | }, 65 | "miniprogram": { 66 | "current": -1, 67 | "list": [] 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /miniprogram/pages/CET/CET.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | Page({ 4 | data: { 5 | exams: [], 6 | time: null, 7 | timestamp: null, 8 | }, 9 | 10 | onLoad: function() { 11 | wx.showToast({ 12 | title: '加载中', 13 | icon: 'loading', 14 | duration: 6000, 15 | }) 16 | wx.$request({ 17 | path: 'scores/cet', 18 | actions: true, 19 | methods: 'GET', 20 | type: 'cets', 21 | success: (result: ICets, messages) => { 22 | const exams = [] 23 | for (const i in result.results) { 24 | const exam = result.results[i] 25 | let PorF = '未通过' 26 | if (parseFloat(exam.total) >= 425) { 27 | PorF = '已通过' 28 | } 29 | const date = exam.year + ' 第' + exam.term + '学期' 30 | const e = { 31 | name: exam.name, 32 | PorF: PorF, 33 | date: date, 34 | total: exam.total, 35 | listening: exam.listening, 36 | reading: exam.reading, 37 | comprehensive: exam.comprehensive, 38 | } 39 | exams.push(e) 40 | } 41 | this.setData({ 42 | exams: exams, 43 | messages: messages, 44 | time: wx.$formatDate(result.time), 45 | timestamp: result.time, 46 | }) 47 | wx.showToast({ 48 | title: '加载成功', 49 | icon: 'success', 50 | duration: 500, 51 | }) 52 | }, 53 | failed: () => { 54 | wx.navigateBack() 55 | }, 56 | }) 57 | }, 58 | 59 | onShow: function() { 60 | this.setData({time: wx.$formatDate(this.data.timestamp)}) 61 | }, 62 | 63 | onPullDownRefresh: function() { 64 | wx.stopPullDownRefresh() 65 | this.onLoad() 66 | }, 67 | }) 68 | 69 | export {} 70 | -------------------------------------------------------------------------------- /miniprogram/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages":[ 3 | "pages/login/login", 4 | "pages/entireClass/entireClass", 5 | "pages/exam/exam", 6 | "pages/scoreDetailPage/scoreDetailPage", 7 | "pages/overAllPage/overAllPage", 8 | "pages/previousScore/previousScore", 9 | "pages/scoreList/scoreList", 10 | "pages/index/index", 11 | "pages/CET/CET", 12 | "pages/more/more" 13 | ], 14 | "window":{ 15 | "backgroundTextStyle":"light", 16 | "navigationBarBackgroundColor": "@navBgColor", 17 | "navigationBarTitleText": "野生助手", 18 | "navigationBarTextStyle":"@navTxtStyle", 19 | "backgroundColor": "@bgColor", 20 | "backgroundTextStyle": "@bgTxtStyle" 21 | }, 22 | "tabBar": { 23 | "custom": true, 24 | "color": "#0197c2", 25 | "selectedColor": "#0197c2", 26 | "borderStyle": "black" , 27 | "list": [{ 28 | "iconPath":"icon/check-grade.png", 29 | "selectedIconPath":"icon/check-grade-selected.png", 30 | "pagePath": "pages/overAllPage/overAllPage", 31 | "text": "成绩" 32 | }, { 33 | "iconPath":"icon/check-schedule.png", 34 | "selectedIconPath":"icon/check-schedule-selected.png", 35 | "pagePath": "pages/index/index", 36 | "text": "课表" 37 | }, { 38 | "iconPath":"icon/check-exam.png", 39 | "selectedIconPath":"icon/check-exam-selected.png", 40 | "pagePath": "pages/exam/exam", 41 | "text": "考试时间" 42 | }, { 43 | "iconPath":"icon/check-courses.png", 44 | "selectedIconPath":"icon/check-courses-selected.png", 45 | "pagePath": "pages/entireClass/entireClass", 46 | "text": "选课指导" 47 | } 48 | ] 49 | }, 50 | "style": "v2", 51 | "sitemapLocation": "sitemap.json", 52 | "cloud": true, 53 | "darkmode": true, 54 | "themeLocation": "theme.json" 55 | } 56 | -------------------------------------------------------------------------------- /miniprogram/pages/CET/CET.wxss: -------------------------------------------------------------------------------- 1 | .totalScore { 2 | margin: 30rpx 40rpx; 3 | padding: 10rpx 30rpx 20rpx 30rpx; 4 | border-radius: 20rpx; 5 | box-shadow: 0rpx 0rpx 15rpx #CCC; 6 | background-color: #FFF; 7 | } 8 | .totalScore .line1 { 9 | font-size: 40rpx; 10 | font-weight:bold; 11 | margin: 10rpx 0; 12 | display: flex; 13 | } 14 | .totalScore .line3 .subTitle{ 15 | margin-right: 20rpx; 16 | padding: 15rpx; 17 | font-size: 20rpx; 18 | border-radius: 25px; 19 | background-color: rgba(204, 204, 204, 0.2); 20 | } 21 | .totalScore .line3 { 22 | margin-bottom: 30rpx; 23 | } 24 | .totalScore .line1 .time { 25 | color: #888888; 26 | font-size: 30rpx; 27 | font-weight: 200; 28 | margin-left: auto; 29 | margin-top: auto; 30 | } 31 | 32 | .totalScore .update-time { 33 | font-size: 25rpx; 34 | text-align: right; 35 | color: var(--weui-FG-2); 36 | } 37 | 38 | .totalScore .line2 { 39 | display: flex; 40 | } 41 | .totalScore .line2 .item { 42 | flex: 1; 43 | margin-right: 20rpx; 44 | border-right: 1rpx solid rgba(204, 204, 204, 0.4); 45 | } 46 | .totalScore .line2 .item_1 { 47 | flex: 1; 48 | margin-right: 20rpx; 49 | } 50 | .totalScore .line2 .item_2 { 51 | margin-left: auto; 52 | margin-right: auto; 53 | } 54 | .totalScore .line2 .title { 55 | font-size: 25rpx; 56 | color: #999999; 57 | } 58 | .totalScore .line2 .value { 59 | font-size: 50rpx; 60 | } 61 | 62 | .totalScore .line3 { 63 | font-size: 25rpx; 64 | } 65 | .totalScore .line4 { 66 | display: flex; 67 | margin-bottom: 10rpx; 68 | } 69 | .totalScore .line4 .item { 70 | flex: 1; 71 | margin-right: 20rpx; 72 | border-right: 1px solid #EFEFEF ; 73 | } 74 | .totalScore .line4 .item_1 { 75 | flex: 1; 76 | margin-right: 20rpx; 77 | } 78 | .totalScore .line4 .value { 79 | font-size: 50rpx; 80 | } 81 | .message { 82 | margin: 30rpx 40rpx; 83 | color: #888888; 84 | font-size: 25rpx; 85 | text-align: center; 86 | } 87 | -------------------------------------------------------------------------------- /miniprogram/pages/previousScore/previousScore.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | const app: IMyApp = getApp() 4 | 5 | Page({ 6 | data: { 7 | term: [], 8 | time: null, 9 | unread: false, 10 | share_score: false, 11 | }, 12 | 13 | onLoad: function() { 14 | if ( 15 | typeof app.globalData.scoreData === 'undefined' || !app.globalData.scoreData || 16 | !wx.$validateType(app.globalData.scoreData.result, 'term') 17 | ) { 18 | wx.reLaunch({ 19 | url: '/pages/login/login', 20 | }) 21 | return 22 | } 23 | 24 | const t = [] 25 | this.setData({time: wx.$formatDate(app.globalData.scoreData.result.time)}) 26 | for (const year in app.globalData.scoreData.result.terms) { 27 | for (const term in app.globalData.scoreData.result.terms[year]) { 28 | // 计算通过率与总学分 29 | const courses = app.globalData.scoreData.result.terms[year][term] 30 | const {unreadCount, ungraded, unsubscribed} = wx.$forCourses(courses.courses, app.globalData.unread) 31 | const {credits, passCredits, gradedCredits} = wx.$forCourses(courses.courses, app.globalData.unread, false, false) 32 | 33 | 34 | const passRate = passCredits / gradedCredits * 100 35 | 36 | t.push({ 37 | name: year + ' 第 ' + term + ' 学期', 38 | detail: { 39 | gpa: app.globalData.scoreData.result.terms[year][term].average_GPA_term, 40 | score: app.globalData.scoreData.result.terms[year][term].average_score_term, 41 | count: app.globalData.scoreData.result.terms[year][term].term_lesson_count, 42 | passRate, 43 | passCredits, 44 | totalCredit: credits, 45 | }, 46 | unread: unreadCount, 47 | ungraded, 48 | unsubscribed, 49 | year, 50 | term, 51 | }) 52 | } 53 | } 54 | this.setData({ 55 | term: t, 56 | unread: this.data.unread, 57 | share_score: app.globalData.scoreData.result.share_score, 58 | first_semester: app.globalData.scoreData.result.first_semester, 59 | }) 60 | }, 61 | 62 | onShow: function() { 63 | this.onLoad() 64 | }, 65 | 66 | bindtapHandeler: function(e) { 67 | const {year, term} = e.currentTarget.dataset 68 | wx.navigateTo({ 69 | url: '/pages/scoreList/scoreList?year=' + year + '&term=' + term, 70 | }) 71 | }, 72 | }) 73 | 74 | export {} 75 | -------------------------------------------------------------------------------- /miniprogram/pages/CET/CET.wxml: -------------------------------------------------------------------------------- 1 | 2 | {{item}} 3 | 4 | 5 | 6 | 大学英语四级 大学英语六级 {{item.name}} 7 | 8 | 9 | 10 | {{item.date}} 11 | {{item.PorF}} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 总分 19 | 20 | {{item.total}} 21 | 22 | 23 | 24 | 25 | 听力 26 | 27 | 28 | {{item.listening}} 29 | 30 | 31 | 32 | 33 | 34 | 阅读 35 | 36 | {{item.reading}} 37 | 38 | 39 | 40 | 41 | 42 | 写作 43 | 44 | {{item.comprehensive}} 45 | 46 | 47 | 48 | 49 | 更新时间:{{time}} 50 | 51 | -------------------------------------------------------------------------------- /miniprogram/pages/login/login.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | {{message}} 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /miniprogram/pages/previousScore/previousScore.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{filter.startYear(item.year, item.term, first_semester)}} 9 | 10 | 11 | 12 | 13 | 通过率:{{filter.toFix(item.detail.passRate, 0)}}% 14 | 学分:{{item.detail.passCredits}}/{{item.detail.totalCredit}} 15 | 16 | 17 | 18 | 19 | 20 | 21 | GPA 22 | 23 | {{filter.toFix(item.detail.gpa, 2)}} 24 | 25 | 26 | 27 | 28 | 29 | 加权平均分 30 | 31 | {{filter.toFix(item.detail.score, 2)}} 32 | 33 | 34 | 35 | 36 | 37 | 已出课程数 38 | 39 | {{item.detail.count}}/{{item.detail.count+item.ungraded}} 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 野生助手前端 - 小程序版 2 | 3 | 本程序在 [GNU Affero GPL v3.0](LICENSE) 下开源。若修改本程序并在网络上提供服务,必须使用相同协议公开修改后的完整源代码。 4 | 5 | Copyright (C) WildHelper 6 | 7 | [版本更新日志](CHANGELOG.md) 8 | 9 | ![小程序码](https://user-images.githubusercontent.com/6601455/87315927-a5a37100-c557-11ea-88a6-bc897e460752.jpg) 10 | 11 | ## 安装 12 | 13 | 开发时使用了 TypeScript,需要将其将其编译成 JavaScript 才可使用 14 | 15 | 先运行 `npm install`;然后进入微信开发者工具,选择 `工具 - 构建 npm` 16 | 17 | 下载 `echart.js` 到 `./miniprogram/ec-canvas` 目录中 18 | 19 | 该项目有意将 `.idea` 提交。使用 WebStorm/PhpStorm 编辑文件时会原地将 TypeScript 文件编译为 JavaScript。 20 | 21 | ## 配置 22 | 23 | 修改 `./miniprogram/app.ts` 中 `App()` 函数的 `globalData.url_api` 和 `globalData.url_logo` 以自定义 API 地址和首页图标。 24 | 25 | ## 截图 26 | 27 | 28 | 29 | ## 核心技术 30 | 31 | + 通用 RESTful API 32 | + **爬虫**与学校官方对接实现数据**实时自动获取** 33 | + 第四版开始支持**校友认证**,学校网关账户绑定微信OpenID实现认证,毕业后可使用免密OpenID授权,100%保证用户真实,比学信网更简单易用 34 | + 使用了**端到端加密 (AES-256-GCM)**,中间人(包括微信)不可拿到任何用户数据;非选课周用户只能看到自己选择的课程;服务器**永不存储用户密码**;**撤销授权机制**,不想用了可以彻底删除所有用户数据,保证用户安全 35 | + DDOS防火墙、异地多活、WAF防火墙限频、等等…… 36 | 37 | ## 开软软件使用 38 | 39 | ### GNU Affero GPL v3.0 40 | 41 | + [WildHelper/MiniProgram](https://github.com/WildHelper/MiniProgram) 42 | 43 | ### MIT License 44 | 45 | + [leoleoasd/zf_spider](https://github.com/leoleoasd/zf_spider) 46 | + [slimphp/Slim](https://github.com/slimphp/Slim) 47 | + [slimphp/Slim-Psr7](https://github.com/slimphp/Slim-Psr7) 48 | + [Tencent/weui](https://github.com/Tencent/weui) 49 | + [brix/crypto-js](https://github.com/brix/crypto-js) 50 | + [nodeca/pako](https://github.com/nodeca/pako) 51 | 52 | ### Apache License v2.0 53 | 54 | + [apache/incubator-echarts](https://github.com/apache/incubator-echarts) 55 | 56 | ### BSD 3-Clause License 57 | 58 | + [ecomfe/echarts-for-weixin](https://github.com/ecomfe/echarts-for-weixin) 59 | -------------------------------------------------------------------------------- /miniprogram/ec-canvas/wx-canvas.js: -------------------------------------------------------------------------------- 1 | export default class WxCanvas { 2 | constructor(ctx, canvasId, isNew, canvasNode) { 3 | this.ctx = ctx; 4 | this.canvasId = canvasId; 5 | this.chart = null; 6 | this.isNew = isNew 7 | if (isNew) { 8 | this.canvasNode = canvasNode; 9 | } 10 | else { 11 | this._initStyle(ctx); 12 | } 13 | 14 | // this._initCanvas(zrender, ctx); 15 | 16 | this._initEvent(); 17 | } 18 | 19 | getContext(contextType) { 20 | if (contextType === '2d') { 21 | return this.ctx; 22 | } 23 | } 24 | 25 | // canvasToTempFilePath(opt) { 26 | // if (!opt.canvasId) { 27 | // opt.canvasId = this.canvasId; 28 | // } 29 | // return wx.canvasToTempFilePath(opt, this); 30 | // } 31 | 32 | setChart(chart) { 33 | this.chart = chart; 34 | } 35 | 36 | attachEvent() { 37 | // noop 38 | } 39 | 40 | detachEvent() { 41 | // noop 42 | } 43 | 44 | _initCanvas(zrender, ctx) { 45 | zrender.util.getContext = function () { 46 | return ctx; 47 | }; 48 | 49 | zrender.util.$override('measureText', function (text, font) { 50 | ctx.font = font || '12px sans-serif'; 51 | return ctx.measureText(text); 52 | }); 53 | } 54 | 55 | _initStyle(ctx) { 56 | var styles = ['fillStyle', 'strokeStyle', 'globalAlpha', 57 | 'textAlign', 'textBaseAlign', 'shadow', 'lineWidth', 58 | 'lineCap', 'lineJoin', 'lineDash', 'miterLimit', 'fontSize']; 59 | 60 | styles.forEach(style => { 61 | Object.defineProperty(ctx, style, { 62 | set: value => { 63 | if (style !== 'fillStyle' && style !== 'strokeStyle' 64 | || value !== 'none' && value !== null 65 | ) { 66 | ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value); 67 | } 68 | } 69 | }); 70 | }); 71 | 72 | ctx.createRadialGradient = () => { 73 | return ctx.createCircularGradient(arguments); 74 | }; 75 | } 76 | 77 | _initEvent() { 78 | this.event = {}; 79 | const eventNames = [{ 80 | wxName: 'touchStart', 81 | ecName: 'mousedown' 82 | }, { 83 | wxName: 'touchMove', 84 | ecName: 'mousemove' 85 | }, { 86 | wxName: 'touchEnd', 87 | ecName: 'mouseup' 88 | }, { 89 | wxName: 'touchEnd', 90 | ecName: 'click' 91 | }]; 92 | 93 | eventNames.forEach(name => { 94 | this.event[name.wxName] = e => { 95 | const touch = e.touches[0]; 96 | this.chart.getZr().handler.dispatch(name.ecName, { 97 | zrX: name.wxName === 'tap' ? touch.clientX : touch.x, 98 | zrY: name.wxName === 'tap' ? touch.clientY : touch.y 99 | }); 100 | }; 101 | }); 102 | } 103 | 104 | set width(w) { 105 | if (this.canvasNode) this.canvasNode.width = w 106 | } 107 | set height(h) { 108 | if (this.canvasNode) this.canvasNode.height = h 109 | } 110 | 111 | get width() { 112 | if (this.canvasNode) 113 | return this.canvasNode.width 114 | return 0 115 | } 116 | get height() { 117 | if (this.canvasNode) 118 | return this.canvasNode.height 119 | return 0 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /miniprogram/pages/entireClass/entireClass.wxss: -------------------------------------------------------------------------------- 1 | .search{ 2 | margin: 30rpx 40rpx; 3 | padding: 20rpx; 4 | border-radius: 20rpx; 5 | box-shadow: 0rpx 0rpx 15rpx #CCC; 6 | background-color: #FFF; 7 | display: flex; 8 | } 9 | .search .input { 10 | flex: 7; 11 | margin: auto 0; 12 | } 13 | .search .searchConfirm { 14 | flex: 1; 15 | margin-left: 20rpx; 16 | } 17 | 18 | .sort { 19 | margin: 0 40rpx; 20 | display: flex; 21 | font-size: 25rpx; 22 | } 23 | .sort view { 24 | color: #5e5e5e; 25 | } 26 | .sort .item { 27 | text-align: center; 28 | margin: 0 30rpx; 29 | } 30 | 31 | .classes { 32 | margin: 30rpx 40rpx; 33 | background-color: #FFF; 34 | border-radius: 20rpx; 35 | box-shadow: 0rpx 0rpx 15rpx #CCC; 36 | overflow: hidden; 37 | } 38 | .classes .line1{ 39 | display: flex; 40 | } 41 | .classes .line1 .name{ 42 | font-size: 30rpx; 43 | display: inline-block; 44 | white-space: nowrap; 45 | overflow: hidden; 46 | text-overflow: ellipsis; 47 | width: calc(100vw - 330rpx); 48 | } 49 | .classes .line1 .sampleSize{ 50 | margin-top: auto; 51 | margin-bottom: auto; 52 | font-size: 20rpx; 53 | } 54 | .classes .line2{ 55 | display: flex; 56 | margin-bottom: 10rpx; 57 | } 58 | .classes .line2 .item { 59 | color: #888888; 60 | } 61 | .classes .line3{ 62 | display: flex; 63 | font-size: 20rpx; 64 | color: #888888; 65 | } 66 | .classes .line4 .content { 67 | flex: 1 68 | } 69 | .classes .line4 { 70 | color: #888888; 71 | display: flex; 72 | font-size: 20rpx; 73 | } 74 | .weui-msg__icon-area { 75 | margin-bottom: 16px!important; 76 | } 77 | .weui-msg__text-area { 78 | margin-bottom: 0!important; 79 | } 80 | .weui-msg__opr-area, .weui-msg__tips-area, .weui-msg__extra-area { 81 | display: none!important; 82 | } 83 | .count, .message { 84 | margin: 30rpx 40rpx; 85 | color: #888888; 86 | font-size: 25rpx; 87 | text-align: center; 88 | } 89 | 90 | .paging { 91 | margin: 0 40rpx; 92 | display: flex; 93 | } 94 | .paging .up{ 95 | flex: 2; 96 | margin: 20rpx 20rpx; 97 | font-size: 25rpx; 98 | } 99 | .paging .select{ 100 | flex:4; 101 | margin: 20rpx; 102 | font-size: 25rpx; 103 | padding: 8px 12px; 104 | } 105 | .paging .select view{ 106 | text-overflow: ellipsis; 107 | width: 100%; 108 | display: inline-block; 109 | white-space: nowrap; 110 | overflow: hidden; 111 | } 112 | .paging .down{ 113 | flex:2; 114 | margin: 20rpx 20rpx; 115 | font-size: 25rpx; 116 | } 117 | .weui-select { 118 | background-color: #5e5e5e; 119 | width: 100%; 120 | /* font-size: inherit; */ 121 | height: 0; 122 | line-height: 0; 123 | position: relative; 124 | z-index: 300; 125 | /* padding-left: 0; */ 126 | color: var(--weui-FG-0); 127 | } 128 | .arrow { 129 | position: absolute; 130 | right: 30rpx; 131 | top: 16rpx; 132 | } 133 | view.page { 134 | padding-bottom: 80px; 135 | } 136 | 137 | @supports (padding-bottom: calc(55px + 50rpx)) { 138 | view.page { 139 | padding-bottom: calc(55px + 50rpx); 140 | } 141 | } 142 | 143 | @supports (padding-bottom: calc(55px + 50rpx + env(safe-area-inset-bottom))) { 144 | view.page { 145 | padding-bottom: calc(55px + 50rpx + env(safe-area-inset-bottom)); 146 | } 147 | } 148 | .weui-cell { 149 | padding: 24rpx 30rpx; 150 | } 151 | -------------------------------------------------------------------------------- /miniprogram/pages/exam/exam.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | const app: IMyApp = getApp() 4 | 5 | Page({ 6 | data: { 7 | examList: [], 8 | isNull: null, 9 | isOver: [], 10 | messages: [], 11 | }, 12 | time: 0, 13 | 14 | onLoad: function() { 15 | const exam: IExams = wx.getStorageSync('exam2') 16 | if (exam && wx.$validateType(exam, 'exams')) { 17 | this.time = exam.updated_time 18 | this.setData({ 19 | examList: exam.list, 20 | isNull: false, 21 | }) 22 | this.parseTime() 23 | } 24 | if (this.time + 21600 < +new Date() / 1000) { 25 | this.fetch() 26 | } 27 | }, 28 | 29 | fetch: function() { 30 | this.time = +new Date() / 1000 31 | wx.showToast({ 32 | title: '更新考试时间中', 33 | icon: 'loading', 34 | duration: 6000, 35 | }) 36 | wx.$request({ 37 | path: 'courses/exams', 38 | actions: true, 39 | methods: 'GET', 40 | type: 'exams', 41 | success: (result: IExams, messages) => { 42 | this.setData({messages: messages}) 43 | const isNull = false 44 | if (result.list.length === 0) { 45 | if (this.data.isNull === null) { 46 | this.setData({ 47 | examList: [], 48 | isNull: true, 49 | }) 50 | } 51 | wx.showToast({ 52 | title: '考试为空', 53 | icon: 'none', 54 | duration: 500, 55 | }) 56 | } else { 57 | wx.setStorageSync('exam2', result) 58 | this.setData({ 59 | examList: result.list, 60 | isNull: isNull, 61 | }) 62 | this.parseTime() 63 | wx.showToast({ 64 | title: '更新成功', 65 | icon: 'success', 66 | duration: 500, 67 | }) 68 | } 69 | }, 70 | complete: () => { 71 | wx.stopPullDownRefresh() 72 | }, 73 | }) 74 | }, 75 | 76 | parseTime: function() { 77 | // TODO: undefined is not an object (evaluating 's.split("年")[1].split') 78 | // const isOver = [] 79 | // for (const i in this.data.examList) { 80 | // let examEndTime = this.data.examList[i].time 81 | // if (typeof this.data.examList[i].time.split === 'function') { 82 | // examEndTime = examEndTime.split('年')[0] + '-' + examEndTime.split('年')[1].split('月')[0] + '-' + examEndTime.split('月')[1].split('日')[0] + ' ' + examEndTime.split('-')[1].split(')')[0] 83 | // } 84 | // const date = new Date() 85 | // const curTime = date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + ' ' + date.getHours() + ':' + date.getMinutes() 86 | // if (examEndTime > curTime) { 87 | // isOver.push(true) 88 | // } else { 89 | // isOver.push(false) 90 | // } 91 | // } 92 | // 93 | // this.setData({isOver: isOver}) 94 | }, 95 | 96 | onShow: function() { 97 | if (typeof this.getTabBar === 'function' && this.getTabBar()) { 98 | wx.$initTab(this.getTabBar()) 99 | this.getTabBar().setData({ 100 | selected: app.globalData.exam_tab, 101 | }) 102 | } 103 | }, 104 | 105 | onPullDownRefresh: function() { 106 | if (this.time + 60 > +new Date() / 1000) { 107 | wx.stopPullDownRefresh() 108 | wx.showToast({ 109 | title: '手速太快了,等一会儿再刷新试试吧', 110 | icon: 'none', 111 | duration: 3000, 112 | }) 113 | return 114 | } 115 | this.fetch() 116 | }, 117 | }) 118 | 119 | export {} 120 | -------------------------------------------------------------------------------- /miniprogram/pages/scoreList/scoreList.wxss: -------------------------------------------------------------------------------- 1 | .line{ 2 | width: 100%; /* 分隔线的长度 */ 3 | margin: auto; 4 | height: 1px; /* 分隔线粗细 */ 5 | text-align: center; 6 | font-size: 24rpx; 7 | color: #aaa; 8 | background: #ccc; 9 | margin-top: 30rpx; 10 | margin-bottom: 5rpx; 11 | flex: 1; 12 | } 13 | .arrow{ 14 | width: 10px; 15 | height: 10px; 16 | border-top: 2px solid #999; 17 | border-right: 2px solid #999; 18 | 19 | right: 20rpx; 20 | transform: rotate(45deg); 21 | margin-top:20rpx; 22 | } 23 | .title { 24 | flex: 9; 25 | } 26 | 27 | .search{ 28 | margin: 30rpx 40rpx; 29 | padding: 20rpx; 30 | border-radius: 20rpx; 31 | box-shadow: 0rpx 0rpx 15rpx #CCC; 32 | background-color: #FFF; 33 | display: flex; 34 | } 35 | .search .input { 36 | flex: 7; 37 | margin: auto 0; 38 | } 39 | .search .searchConfirm { 40 | flex: 1; 41 | margin-left: 20rpx; 42 | } 43 | 44 | .title { 45 | display: flex; 46 | } 47 | .sort { 48 | font-size: 25rpx; 49 | } 50 | 51 | .totalScore { 52 | overflow: hidden; 53 | margin: 30rpx 40rpx; 54 | padding-bottom: 10rpx; 55 | border-radius: 20rpx; 56 | box-shadow: 0rpx 0rpx 15rpx #CCC; 57 | background-color: #FFF; 58 | } 59 | 60 | .totalScore .line1 { 61 | font-size: 40rpx; 62 | padding: 30rpx 30rpx 10rpx 30rpx; 63 | background-color: #0197C2; 64 | color: #FFF; 65 | display: flex; 66 | } 67 | .totalScore .line2 { 68 | margin-bottom: 30rpx; 69 | } 70 | .totalScore .line3 { 71 | display: flex; 72 | } 73 | .totalScore .line3 .item { 74 | flex: 1; 75 | margin-right: 20rpx; 76 | border-right: 1rpx solid rgba(204, 204, 204, 0.4); 77 | } 78 | .totalScore .line3 .item_1 { 79 | flex: 1; 80 | margin-right: 20rpx; 81 | } 82 | .totalScore .line3 .title { 83 | font-size: 25rpx; 84 | color: #999999; 85 | } 86 | .totalScore .line3 .value { 87 | font-size: 50rpx; 88 | } 89 | .totalScore .time { 90 | font-size: 25rpx; 91 | text-align: right; 92 | color: var(--weui-FG-2); 93 | flex: 1; 94 | } 95 | 96 | .search{ 97 | margin: 30rpx 40rpx; 98 | padding: 20rpx; 99 | border-radius: 20rpx; 100 | box-shadow: 0rpx 0rpx 15rpx #CCC; 101 | background-color: #FFF; 102 | } 103 | 104 | .sortDisplay { 105 | display: flex; 106 | margin: 30rpx 40rpx; 107 | } 108 | .sortDisplay view { 109 | color: #5e5e5e; 110 | } 111 | .typeSet { 112 | margin: 30rpx 40rpx; 113 | } 114 | .typeSet .title { 115 | font-size: 25rpx; 116 | color: #B2B2B2; 117 | } 118 | .scores { 119 | box-shadow: 0rpx 0rpx 15rpx #CCC; 120 | background-color: #FFF; 121 | padding: 0 30rpx; 122 | margin-top: 5rpx; 123 | border-radius: 10rpx; 124 | } 125 | 126 | .weui-cell { 127 | padding: 18rpx 0; 128 | position: relative; 129 | display: -webkit-box; 130 | display: -webkit-flex; 131 | display: flex; 132 | align-items: center; 133 | } 134 | .weui-cell:before { 135 | content: " "; 136 | position: absolute; 137 | left: 0; 138 | top: 0; 139 | right: 0; 140 | height: 1px; 141 | border-top: 1px solid var(--weui-FG-3); 142 | color: var(--weui-FG-3); 143 | transform-origin: 0 0; 144 | transform: scaleY(.5); 145 | z-index: 2; 146 | } 147 | .course-title { 148 | font-size: 90%; 149 | display: block; 150 | overflow: hidden; 151 | text-overflow: ellipsis; 152 | white-space: nowrap; 153 | width: calc(100vw - 360rpx); 154 | } 155 | .course-desc { 156 | font-size: 60%; 157 | display: block; 158 | color: #999999; 159 | } 160 | @media (prefers-color-scheme: dark) { 161 | .totalScore .line1 { 162 | background-color: #143F54 !important; 163 | } 164 | } 165 | .message { 166 | margin: 30rpx 40rpx; 167 | color: #888888; 168 | font-size: 25rpx; 169 | text-align: center; 170 | } 171 | -------------------------------------------------------------------------------- /miniprogram/pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | /**index.wxss**/ 2 | .container { 3 | height: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: space-between; 8 | padding: 0 0; 9 | box-sizing: border-box; 10 | 11 | } 12 | .page page { 13 | background-color: #FFF; 14 | } 15 | .date { 16 | background-color: var(--weui-BG-0); 17 | display: flex; 18 | color: #4c4c4c; 19 | } 20 | .date .dateItem { 21 | font-size: 30rpx; 22 | flex: 1; 23 | text-align: center; 24 | padding: 0 5rpx; 25 | box-sizing: border-box; 26 | border: 5rpx solid var(--weui-BG-0); 27 | } 28 | .date .s { 29 | flex: 1; 30 | background-color: #888; 31 | font-size: 20rpx; 32 | text-align: center; 33 | padding-top: 10rpx; 34 | color: #fff; 35 | } 36 | 37 | .classCol { 38 | background-color: #FFF; 39 | } 40 | 41 | .time { 42 | display: flex; 43 | } 44 | .classItem1,.classItem2,.classItem3,.classItem4,.classItem5,.classItem6,.classItem7 { 45 | flex: 1; 46 | height: 250rpx; 47 | font-size: 25rpx; 48 | border: 5rpx solid #FFF; 49 | box-sizing: border-box; 50 | border-radius: 15rpx; 51 | padding: 0 5rpx; 52 | color: #4c4c4c; 53 | overflow: hidden; 54 | } 55 | .opacity-true { 56 | opacity: 0.2; 57 | } 58 | .classItem0 { 59 | flex: 1; 60 | height: 250rpx; 61 | text-align: center; 62 | color: #4c4c4c; 63 | } 64 | .classItem0 .content { 65 | display: block; 66 | font-size: 30rpx; 67 | margin-top: 60rpx; 68 | } 69 | view.page { 70 | padding-bottom: 55px; 71 | } 72 | view.page.is-single-true { 73 | padding-bottom: 30rpx; 74 | } 75 | 76 | @supports (padding-bottom: calc(55px + env(safe-area-inset-bottom))) { 77 | view.page { 78 | padding-bottom: calc(55px + env(safe-area-inset-bottom)); 79 | } 80 | } 81 | 82 | @media (prefers-color-scheme: dark) { 83 | .page page { 84 | background-color: #000; 85 | } 86 | .date { 87 | color: #b3b3b3; 88 | } 89 | .date .dateItem { 90 | border: 5rpx solid var(--weui-BG-0); 91 | } 92 | .date .s { 93 | color: #000; 94 | } 95 | view { 96 | color: #FFF; 97 | } 98 | .classCol { 99 | background-color: #000; 100 | } 101 | .classItem1,.classItem2,.classItem3,.classItem4,.classItem5,.classItem6,.classItem7 { 102 | border: 5rpx solid #000; 103 | color: #b3b3b3; 104 | } 105 | .opacity-true { 106 | opacity: 0.2; 107 | } 108 | .classItem0 { 109 | flex: 1; 110 | height: 250rpx; 111 | text-align: center; 112 | color: #b3b3b3; 113 | } 114 | } 115 | .message { 116 | margin: 30rpx 40rpx; 117 | color: #888888; 118 | font-size: 25rpx; 119 | text-align: center; 120 | } 121 | .weui-cell { 122 | padding: 18rpx 0; 123 | position: relative; 124 | display: -webkit-box; 125 | display: -webkit-flex; 126 | display: flex; 127 | align-items: center; 128 | } 129 | .weui-cell:before { 130 | content: " "; 131 | position: absolute; 132 | left: 0; 133 | top: 0; 134 | right: 0; 135 | height: 1px; 136 | border-top: 1px solid var(--weui-FG-3); 137 | color: var(--weui-FG-3); 138 | transform-origin: 0 0; 139 | transform: scaleY(.5); 140 | z-index: 2; 141 | } 142 | .typeSet { 143 | margin: 30rpx 40rpx; 144 | } 145 | .typeSet .title { 146 | font-size: 25rpx; 147 | color: #B2B2B2; 148 | } 149 | .scores { 150 | box-shadow: 0rpx 0rpx 15rpx #CCC; 151 | background-color: #FFF; 152 | padding: 0 30rpx; 153 | margin-top: 5rpx; 154 | border-radius: 10rpx; 155 | } 156 | .course-title { 157 | font-size: 90%; 158 | display: block; 159 | overflow: hidden; 160 | text-overflow: ellipsis; 161 | white-space: nowrap; 162 | width: calc(100vw - 360rpx); 163 | } 164 | .course-desc { 165 | font-size: 60%; 166 | display: block; 167 | color: #999999; 168 | } 169 | -------------------------------------------------------------------------------- /miniprogram/pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{comment}} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 周一 15 | 周二 16 | 周三 17 | 周四 18 | 周五 19 | 周六 20 | 周日 21 | 22 | 23 | 24 | 25 | 26 | 27 | 上午\n 28 | 下午\n 29 | 晚上\n 30 | {{item.text}} 31 | {{item}} 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | {{message}} 40 | 41 | 42 | 43 | 无上课时间课程 44 | 45 | 46 | {{item.name}}{{item.credit}} | {{item.type}} | {{item.instructor}} 47 | {{item.hours}} 48 | 49 | 50 | 51 | 52 | {{message}} 53 | 54 | 59 | 点击右上角 … 进行分享后好友可看到您当前学期课表 60 | {{item}} 61 | 62 | -------------------------------------------------------------------------------- /miniprogram/pages/overAllPage/overAllPage.wxss: -------------------------------------------------------------------------------- 1 | .line{ 2 | width: 100%; /* 分隔线的长度 */ 3 | margin: auto; 4 | height: 1px; /* 分隔线粗细 */ 5 | text-align: center; 6 | font-size: 24rpx; 7 | color: #aaa; 8 | background: #ccc; 9 | margin-top: 30rpx; 10 | margin-bottom: 5rpx; 11 | flex: 1; 12 | } 13 | 14 | .title { 15 | flex: 9; 16 | } 17 | 18 | .view-box-btn-hover{ 19 | background-color: #4D4D4D; 20 | } 21 | 22 | .card { 23 | background-color: #0197C2; 24 | } 25 | .card view { 26 | color: #FFF; 27 | } 28 | .page .userInfo { 29 | padding: 40rpx 60rpx; 30 | } 31 | .card2 { 32 | position: relative; 33 | height: 60vw; 34 | top: -35vw; 35 | width: 140vw; 36 | background-color: #0197C2; 37 | -moz-border-radius: 100vw / 60vw; 38 | -webkit-border-radius: 100vw / 60vw; 39 | border-radius: 100vw / 60vw; 40 | z-index: -1; 41 | left: -20vw; 42 | } 43 | .page .userInfo .nameAndPic { 44 | display: flex; 45 | } 46 | .page .userInfo .nameAndPic .name { 47 | flex:2; 48 | white-space: nowrap; 49 | overflow: hidden; 50 | text-overflow: ellipsis; 51 | } 52 | .page .userInfo .nameAndPic .name .welcome { 53 | font-size: 24rpx; 54 | display: block; 55 | } 56 | .page .userInfo .nameAndPic .name .userName { 57 | font-size: 50rpx; 58 | font-weight:bold; 59 | display: inline-block; 60 | } 61 | .page .userInfo .nameAndPic .name .expression { 62 | margin-top: 20rpx; 63 | font-size: 26rpx; 64 | display: inline-block; 65 | } 66 | .page .userInfo .nameAndPic .pic { 67 | height: 120rpx; 68 | width: 120rpx; 69 | border-radius: 60rpx; 70 | overflow: hidden; 71 | } 72 | 73 | .page .userInfo .detailInfo .line1 { 74 | display: flex; 75 | } 76 | .page .userInfo .detailInfo .line2 { 77 | display: flex; 78 | } 79 | .page .userInfo .detailInfo .infoLeft{ 80 | font-size: 24rpx; 81 | flex: 1; 82 | } 83 | .page .userInfo .detailInfo .infoRight{ 84 | font-size: 24rpx; 85 | flex: 1; 86 | } 87 | 88 | 89 | 90 | .totalScore { 91 | margin: 30rpx 40rpx; 92 | padding: 10rpx 30rpx 20rpx 30rpx; 93 | border-radius: 20rpx; 94 | box-shadow: 0rpx 0rpx 15rpx #CCC; 95 | background-color: #FFF; 96 | } 97 | .totalScore .line1 { 98 | font-size: 40rpx; 99 | font-weight:bold; 100 | margin: 10rpx 0; 101 | display: flex; 102 | } 103 | .totalScore .line2 { 104 | margin-bottom: 30rpx; 105 | } 106 | .totalScore .line3 { 107 | display: flex; 108 | } 109 | .totalScore .line3 .item { 110 | flex: 1; 111 | margin-right: 20rpx; 112 | border-right: 1rpx solid rgba(204, 204, 204, 0.4); 113 | } 114 | .totalScore .line3 .item_1 { 115 | flex: 1; 116 | margin-right: 20rpx; 117 | } 118 | .totalScore .line3 .title { 119 | font-size: 25rpx; 120 | color: #999999; 121 | } 122 | .totalScore .line3 .value { 123 | font-size: 50rpx; 124 | } 125 | .totalScore .time { 126 | font-size: 25rpx; 127 | text-align: right; 128 | color: var(--weui-FG-2); 129 | } 130 | 131 | .previousAndCETScore { 132 | margin: 30rpx 40rpx; 133 | padding: 30rpx 30rpx; 134 | padding-bottom: 30rpx; 135 | border-radius: 20rpx; 136 | box-shadow: 0rpx 0rpx 15rpx #CCC; 137 | font-size: 40rpx; 138 | font-weight:bold; 139 | display: flex; 140 | background-color: #FFF; 141 | } 142 | 143 | .message { 144 | margin: 30rpx 40rpx; 145 | color: #888888; 146 | font-size: 25rpx; 147 | text-align: center; 148 | } 149 | 150 | view.page { 151 | padding-bottom: 80px; 152 | } 153 | view.page.is-single-true { 154 | padding-bottom: 30rpx; 155 | } 156 | 157 | @supports (padding-bottom: calc(55px + 50rpx)) { 158 | view.page { 159 | padding-bottom: calc(55px + 50rpx); 160 | } 161 | } 162 | 163 | @supports (padding-bottom: calc(55px + 50rpx + env(safe-area-inset-bottom))) { 164 | view.page { 165 | padding-bottom: calc(55px + 50rpx + env(safe-area-inset-bottom)); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /miniprogram/pages/scoreDetailPage/scoreDetailPage.wxss: -------------------------------------------------------------------------------- 1 | .head { 2 | margin: 30rpx 40rpx; 3 | font-size: 25rpx; 4 | color: #888888; 5 | } 6 | .totalScore { 7 | margin: 0 40rpx 30rpx 40rpx; 8 | padding-bottom: 10rpx; 9 | border-radius: 20rpx; 10 | box-shadow: 0rpx 0rpx 15rpx #CCC; 11 | background-color: #FFF; 12 | overflow: hidden; 13 | } 14 | .totalScore .line1 { 15 | font-size: 40rpx; 16 | padding: 30rpx 30rpx 10rpx 30rpx; 17 | background-color: #0197C2; 18 | color: #FFF; 19 | display: block; 20 | overflow: hidden; 21 | text-overflow: ellipsis; 22 | white-space: nowrap; 23 | } 24 | .totalScore .line2 { 25 | display: flex; 26 | } 27 | .totalScore .line2 .item { 28 | flex: 1; 29 | margin-right: 20rpx; 30 | border-right: 1rpx solid rgba(204, 204, 204, 0.4); 31 | } 32 | .totalScore .line2 .item_1 { 33 | flex: 1; 34 | margin-right: 20rpx; 35 | } 36 | .totalScore .line2 .title { 37 | font-size: 25rpx; 38 | color: #999999; 39 | } 40 | .totalScore .line2 .value { 41 | font-size: 50rpx; 42 | } 43 | .totalScore .time { 44 | font-size: 25rpx; 45 | text-align: right; 46 | color: var(--weui-FG-2); 47 | } 48 | 49 | 50 | .distribution { 51 | margin: 0rpx 40rpx 30rpx; 52 | padding: 40rpx 30rpx 30rpx; 53 | border-radius: 20rpx; 54 | box-shadow: 0rpx 0rpx 15rpx #CCC; 55 | background-color: #FFF; 56 | } 57 | .distribution .line1 { 58 | display: flex; 59 | margin: 20rpx 0; 60 | } 61 | .distribution .line1 .item { 62 | flex: 1; 63 | margin-right: 20rpx; 64 | border-right: 1rpx solid rgba(204, 204, 204, 0.4); 65 | } 66 | .distribution .line1 .item_1 { 67 | flex: 1; 68 | margin-right: 20rpx; 69 | } 70 | .distribution .line1 .title { 71 | font-size: 25rpx; 72 | color: #999999; 73 | } 74 | .distribution .line1 .value { 75 | font-size: 50rpx; 76 | } 77 | .distribution .course-type { 78 | font-size: 20rpx; 79 | text-align: left; 80 | color: #999999; 81 | display: flex; 82 | } 83 | .distribution .course-type .course-type-flex { 84 | flex: 1; 85 | } 86 | .distribution .time { 87 | font-size: 25rpx; 88 | text-align: right; 89 | color: #999999; 90 | } 91 | .distribution .line2 { 92 | font-size: 40rpx; 93 | font-weight:bold; 94 | margin-top: 30rpx; 95 | display: flex; 96 | flex-direction: column; 97 | } 98 | .message { 99 | margin: 30rpx 40rpx; 100 | color: #888888; 101 | font-size: 25rpx; 102 | text-align: center; 103 | } 104 | .distribution .line2 .statement { 105 | color: red; 106 | font-size: 25rpx; 107 | } 108 | .distributionChart { 109 | width: 100%; 110 | height: 1000rpx; 111 | margin: auto; 112 | flex-direction: column; 113 | } 114 | .distribution .line3 { 115 | display: flex; 116 | margin-bottom: 10rpx; 117 | } 118 | .distribution .line3 .item { 119 | flex: 1; 120 | margin-right: 20rpx; 121 | border-right: 1rpx solid rgba(204, 204, 204, 0.4); 122 | } 123 | 124 | .distribution .line3 .item_1 { 125 | flex: 1; 126 | margin-right: 20rpx; 127 | } 128 | 129 | .distribution .line3 .title { 130 | font-size: 25rpx; 131 | color: #999999; 132 | margin-bottom: 10rpx; 133 | } 134 | .distribution .line3 .value { 135 | font-size: 40rpx; 136 | } 137 | .distributionChart_notSshow { 138 | margin-top: 0rpx; 139 | padding-bottom: 30rpx; 140 | color: #FF0000; 141 | font-weight: 200; 142 | } 143 | 144 | .teacher { 145 | margin: 0rpx 40rpx 30rpx; 146 | padding: 0; 147 | border-radius: 20rpx; 148 | box-shadow: 0rpx 0rpx 15rpx #CCC; 149 | background-color: #FFF; 150 | overflow: hidden; 151 | } 152 | .diff-green { 153 | color: #09BB07; 154 | } 155 | .diff-red { 156 | color: #E64340; 157 | } 158 | .highlight-true { 159 | background-color: #576B95; 160 | } 161 | .highlight-true .weui-cell__bd { 162 | color: #fff; 163 | } 164 | .highlight-true .weui-cell__ft { 165 | color: #cecece; 166 | } 167 | .weui-cell { 168 | padding: 20rpx 30rpx 169 | } 170 | @media (prefers-color-scheme: dark) { 171 | .totalScore .line1 { 172 | background-color: #143F54 !important; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /miniprogram/utils/filter.wxs: -------------------------------------------------------------------------------- 1 | var filter = { 2 | toFix: function (value, n) { 3 | if(value == null) { 4 | return ''; 5 | } else if ( isNaN(value) ) { 6 | return value; 7 | } else if (typeof value === "string") { 8 | return parseInt(value, 10).toFixed(n) 9 | } else { 10 | return value.toFixed(n) 11 | } 12 | }, 13 | toStr: function (value) { 14 | return value.toString() 15 | }, 16 | toNum: function (value) { 17 | return value.toNumber() 18 | }, 19 | isNum: function (value) { 20 | return !isNaN(value); 21 | }, 22 | join: function (value, separator) { 23 | if(typeof value.join === "function") { 24 | return value.join(separator); 25 | } 26 | return value; 27 | }, 28 | diff: function (value) { 29 | if(typeof value !== "number") { 30 | return value; 31 | } 32 | var prefix = ''; 33 | var number = value.toFixed(2); 34 | if (value === 0 || number === '0.00' || number === '-0.00'){ 35 | return '-' 36 | } 37 | if (value > 0) { 38 | prefix = '+'; 39 | if (number.substring(0, 2) === '0.') { 40 | number = number.substring(1); 41 | } 42 | } else if (number.substring(0, 3) === '-0.') { 43 | prefix = '-'; 44 | number = number.substring(2); 45 | } 46 | return prefix + number; 47 | }, 48 | diffClass: function (value, cmp) { 49 | if (value > cmp) { 50 | return 'green'; 51 | } else if (value < -cmp) { 52 | return 'red'; 53 | } 54 | return 'default'; 55 | }, 56 | addHeading: function (value, choice) { 57 | var ret = {className: '', text: value} 58 | if (typeof value === 'string') { 59 | if (value.substring(0, 7) === '###### ') { 60 | ret = {className: 'h6', text: value.substring(7)}; 61 | } 62 | if (value.substring(0, 6) === '##### ') { 63 | ret = {className: 'h5', text: value.substring(6)}; 64 | } 65 | if (value.substring(0, 5) === '#### ') { 66 | ret = {className: 'h4', text: value.substring(5)}; 67 | } 68 | if (value.substring(0, 4) === '### ') { 69 | ret = {className: 'h3', text: value.substring(4)}; 70 | } 71 | if (value.substring(0, 3) === '## ') { 72 | ret = {className: 'h2', text: value.substring(3)}; 73 | } 74 | if (value.substring(0, 2) === '# ') { 75 | ret = {className: 'h1', text: value.substring(2)}; 76 | } 77 | if (value.substring(0, 2) === '+ ' || value.substring(0, 2) === '- ') { 78 | ret = {className: 'list', text: value.substring(2)}; 79 | } 80 | if (value.substring(0, 3) === '1. ') { 81 | ret = {className: 'numbered-list', text: value.substring(3)}; 82 | } 83 | } 84 | switch (choice) { 85 | case 'class': 86 | return ret.className; 87 | case 'text': 88 | return ret.text; 89 | default: 90 | return value; 91 | } 92 | }, 93 | shortYear: function (year) { 94 | if (typeof year !== 'string') { 95 | return year 96 | } 97 | return year.substring(2, 4) + '-' + year.substring(7, 9) 98 | }, 99 | startYear: function (year, semester, first) { 100 | var append = ' 第 ' + semester + ' 学期' 101 | if (typeof year !== 'string' || typeof first !== 'string') { 102 | return year + append 103 | } 104 | var firstYear = parseInt(first.substring(0, 4), 10) 105 | var currentYear = parseInt(year.substring(0, 4), 10) 106 | switch (semester) { 107 | case 1: 108 | case '1': 109 | append = '上' 110 | break; 111 | case 2: 112 | case '2': 113 | append = '下' 114 | break; 115 | case 3: 116 | case '3': 117 | append = '小学期' 118 | break; 119 | } 120 | switch (currentYear - firstYear) { 121 | case 0: 122 | return '大一' + append 123 | case 1: 124 | return '大二' + append 125 | case 2: 126 | return '大三' + append 127 | case 3: 128 | return '大四' + append 129 | case 4: 130 | return '大五' + append 131 | case 5: 132 | return '大六' + append 133 | default: 134 | return year + append 135 | } 136 | } 137 | } 138 | 139 | module.exports = { 140 | toFix: filter.toFix, 141 | toStr: filter.toStr, 142 | toNum: filter.toNum, 143 | isNum: filter.isNum, 144 | join: filter.join, 145 | diff: filter.diff, 146 | diffClass: filter.diffClass, 147 | addHeading: filter.addHeading, 148 | shortYear: filter.shortYear, 149 | startYear: filter.startYear 150 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [], 4 | "rules": { 5 | "adjacent-overload-signatures": true, 6 | "align": { 7 | "options": [ 8 | "parameters", 9 | "statements" 10 | ] 11 | }, 12 | "arrow-return-shorthand": true, 13 | "ban-types": { 14 | "options": [ 15 | ["Object", "Avoid using the `Object` type. Did you mean `object`?"], 16 | ["Function", "Avoid using the `Function` type. Prefer a specific function type, like `() => void`."], 17 | ["Boolean", "Avoid using the `Boolean` type. Did you mean `boolean`?"], 18 | ["Number", "Avoid using the `Number` type. Did you mean `number`?"], 19 | ["String", "Avoid using the `String` type. Did you mean `string`?"], 20 | ["Symbol", "Avoid using the `Symbol` type. Did you mean `symbol`?"] 21 | ] 22 | }, 23 | "comment-format": { 24 | "options": ["check-space"] 25 | }, 26 | "curly": { 27 | "options": ["ignore-same-line"] 28 | }, 29 | "cyclomatic-complexity": false, 30 | "import-spacing": true, 31 | "indent": { 32 | "options": ["spaces"] 33 | }, 34 | "interface-name": true, 35 | "interface-over-type-literal": true, 36 | "no-angle-bracket-type-assertion": true, 37 | "no-arg": true, 38 | "no-conditional-assignment": true, 39 | "no-debugger": true, 40 | "no-duplicate-super": true, 41 | "no-eval": true, 42 | "no-internal-module": true, 43 | "no-misused-new": true, 44 | "no-reference-import": true, 45 | "no-string-literal": true, 46 | "no-string-throw": true, 47 | "no-unnecessary-initializer": true, 48 | "no-unsafe-finally": true, 49 | "no-unused-expression": true, 50 | "no-use-before-declare": false, 51 | "no-var-keyword": true, 52 | "no-var-requires": true, 53 | "one-line": { 54 | "options": [ 55 | "check-catch", 56 | "check-else", 57 | "check-finally", 58 | "check-open-brace", 59 | "check-whitespace" 60 | ] 61 | }, 62 | "one-variable-per-declaration": { 63 | "options": ["ignore-for-loop"] 64 | }, 65 | "prefer-const": true, 66 | "prefer-for-of": true, 67 | "quotemark": { 68 | "options": [ 69 | "single", 70 | "avoid-escape", 71 | "jsx-double" 72 | ] 73 | }, 74 | "radix": true, 75 | "semicolon": { 76 | "options": ["never"] 77 | }, 78 | "space-before-function-paren": { 79 | "options": { 80 | "anonymous": "never", 81 | "asyncArrow": "always", 82 | "constructor": "never", 83 | "method": "never", 84 | "named": "never" 85 | } 86 | }, 87 | "trailing-comma": { 88 | "options": { 89 | "esSpecCompliant": true, 90 | "multiline": "always", 91 | "singleline": "never" 92 | } 93 | }, 94 | "triple-equals": { 95 | "options": ["allow-null-check"] 96 | }, 97 | "typedef": false, 98 | "typedef-whitespace": { 99 | "options": [{ 100 | "call-signature": "nospace", 101 | "index-signature": "nospace", 102 | "parameter": "nospace", 103 | "property-declaration": "nospace", 104 | "variable-declaration": "nospace" 105 | }, 106 | { 107 | "call-signature": "onespace", 108 | "index-signature": "onespace", 109 | "parameter": "onespace", 110 | "property-declaration": "onespace", 111 | "variable-declaration": "onespace" 112 | } 113 | ] 114 | }, 115 | "typeof-compare": false, 116 | "unified-signatures": true, 117 | "use-isnan": true, 118 | "whitespace": { 119 | "options": [ 120 | "check-branch", 121 | "check-decl", 122 | "check-operator", 123 | "check-separator", 124 | "check-type", 125 | "check-typecast" 126 | ] 127 | } 128 | }, 129 | "jsRules": {}, 130 | "rulesDirectory": [], 131 | "compilerOptions": { 132 | "lib": [ "es2015" ] 133 | } 134 | } -------------------------------------------------------------------------------- /miniprogram/pages/login/login.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | const app: IMyApp = getApp() 4 | 5 | Page({ 6 | data: { 7 | count: '', 8 | password: '', 9 | showClearBtn: false, 10 | isWaring: false, 11 | isLoggedIn: true, 12 | url_logo: app.globalData.url_logo, 13 | message: '', 14 | }, 15 | 16 | // 登陆表单数据处理-用户名输入框变更 17 | onInputCount(evt) { 18 | const count = evt.detail.value 19 | this.data.count = count 20 | this.setData({ 21 | showClearBtnCount: !!count.length, 22 | isWaring: false, 23 | }) 24 | 25 | }, 26 | // 登陆表单数据处理-密码输入框变更 27 | onInputPassword(evt) { 28 | const password = evt.detail.value 29 | this.data.password = password 30 | this.setData({ 31 | showClearBtnPassword: !!password.length, 32 | isWaring: false, 33 | }) 34 | 35 | }, 36 | // 登陆表单数据处理-用户名输入框清空 37 | onClearCount() { 38 | this.setData({ 39 | count: '', 40 | showClearBtnCount: false, 41 | isWaring: false, 42 | }) 43 | }, 44 | // 登陆表单数据处理-密码输入框清空 45 | onClearPassword() { 46 | this.setData({ 47 | password: '', 48 | showClearBtnPassword: false, 49 | isWaring: false, 50 | }) 51 | }, 52 | 53 | // 确定按钮处理 54 | onConfirm() { 55 | // 用户名格式不正确时,warning效果的判定 56 | if (this.data.count.length !== 8) { 57 | this.setData({ 58 | isWaring: true, 59 | }) 60 | } 61 | 62 | wx.showLoading({ 63 | title: '登录中', 64 | }) 65 | wx.login({ 66 | success: (res) => { 67 | wx.$request({ 68 | actions: false, 69 | methods: 'POST', 70 | data: { 71 | 'no': this.data.count, 72 | 'pass': this.data.password, 73 | 'code': res.code, 74 | }, 75 | path: 'login', 76 | success: wx.$loginSuccess, 77 | type: 'any', 78 | }) 79 | }, 80 | }) 81 | }, 82 | 83 | onLoad: function(options) { 84 | if (typeof options !== 'undefined' && options.year && options.term && options.courseId) { 85 | app.globalData.redirect = { 86 | year: options.year, 87 | term: options.term, 88 | courseId: options.courseId, 89 | } 90 | } 91 | 92 | if (!app.globalData.authorization || !app.globalData.open || !app.globalData.student_id) { 93 | app.globalData.authorization = '' 94 | app.globalData.open = '' 95 | app.globalData.student_id = '' 96 | } 97 | 98 | let version = '-1' 99 | if (options.version) { 100 | version = options.version 101 | wx.setStorageSync('version', version) 102 | } else { 103 | const getVersion = wx.getStorageSync('version') 104 | if (getVersion) { 105 | version = getVersion 106 | } 107 | } 108 | 109 | wx.$request({ 110 | actions: false, 111 | methods: 'GET', 112 | path: 'endpoint/' + version, 113 | type: 'any', 114 | success: (resp) => { 115 | app.globalData.url_api = resp.default.url 116 | const complete = () => { 117 | this.setData({message: resp.default.message}) 118 | if (app.globalData.student_id && app.globalData.authorization && app.globalData.open) { 119 | wx.getBackgroundFetchToken({ 120 | complete: (res: any) => { 121 | if (res.errMsg !== 'getBackgroundFetchToken:ok' || res.token !== app.globalData.authorization) { 122 | wx.setBackgroundFetchToken({ 123 | token: app.globalData.authorization, 124 | complete: () => { 125 | wx.switchTab({ 126 | url: '/pages/overAllPage/overAllPage', 127 | }) 128 | }, 129 | }) 130 | } else { 131 | wx.switchTab({ 132 | url: '/pages/overAllPage/overAllPage', 133 | }) 134 | } 135 | }, 136 | }) 137 | } else { 138 | app.globalData.authorization = '' 139 | app.globalData.open = '' 140 | app.globalData.student_id = '' 141 | this.setData( { isLoggedIn: false } ) 142 | } 143 | } 144 | if (resp.default.trusted) { 145 | wx.setEnableDebug({ 146 | enableDebug: false, 147 | complete, 148 | }) 149 | } else { 150 | wx.setEnableDebug({ 151 | enableDebug: true, 152 | complete, 153 | }) 154 | } 155 | }, 156 | }) 157 | }, 158 | 159 | onShareAppMessage: function() { 160 | return { 161 | title: '快来查分啦', 162 | path: '/pages/login/login', 163 | } 164 | }, 165 | 166 | morePage: function() { 167 | wx.navigateTo({ 168 | url: '/pages/more/more', 169 | }) 170 | }, 171 | }) 172 | 173 | export {} 174 | -------------------------------------------------------------------------------- /miniprogram/pages/more/more.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{status_messages['is_no_password']['desc']}} 7 | 8 | 9 | 10 | {{status_messages['share']['desc']}} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {{status_messages['1']['desc']}} 19 | 20 | 21 | 22 | 23 | {{status_messages['2']['desc']}} 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | {{status_messages['0']['desc']}} 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | {{status_messages['default']['desc']}} 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 四六级成绩 50 | 51 | 52 | 53 | 54 | 55 | 选课指导 56 | 57 | 58 | 59 | 60 | 考试时间 61 | 62 | 63 | 64 | 65 | 66 | 67 | {{adMessage}} 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | {{filter.addHeading(item, 'text')}} 81 | 82 | 86 | 87 | 91 | 92 | {{filter.addHeading(item, 'text')}} 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /miniprogram/pages/entireClass/entireClass.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 28 | 29 | 30 | 31 | 32 | 35 | 38 | 39 | 40 | 41 | 46 | 51 | 52 | 53 | 54 | 76 | 77 | 78 | {{item}} 79 | 80 | 81 | 82 | 83 | 84 | 85 | {{item.name}} 86 | 样本量:{{item.count}} 87 | 88 | 89 | 90 | 91 | {{filter.toFix((item.A*4+item.B*3+item.C*2)/(item.count), 2)}} | {{filter.toFix(item.avg, 2)}} 92 | {{item.A}} | {{filter.toFix(item.avg, 2)}} 93 | 94 | 95 | {{filter.toFix(item.A/(item.count)*100, 1)}}% 96 | {{item.A}} 97 | 98 | 99 | {{filter.toFix((item.A+item.B+item.C)/(item.count)*100, 1)}}% 100 | {{item.F}} 101 | 102 | 103 | 104 | 105 | 106 | 课程性质:{{filter.join(item.type_full, ', ')}} 107 | 108 | 109 | 110 | 开设学院:{{filter.join(item.academy_full, '\n ')}} 111 | 课程代码:{{filter.join(item.id_full, ', ')}} 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 请尝试其他匹配条件 127 | 128 | 129 | 130 | 131 | 显示第{{startIndex}}至{{endIndex}}项结果 共{{count}}项 132 | 133 | 134 | 135 | 136 | 137 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /miniprogram/pages/scoreList/scoreList.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{comment}} 5 | 6 | 7 | 8 | {{filter.startYear(year, term, first_semester)}} 9 | {{title}} 10 | 11 | 12 | 13 | 14 | 通过率:{{filter.toFix(passRate, 0)}}% 15 | 学分:{{passCredit}}/{{totalCredit}} 16 | 17 | 18 | 19 | 20 | 21 | 22 | GPA 23 | 24 | {{filter.toFix(termGPA, 2)}} 25 | 26 | 27 | 28 | 29 | 30 | 加权平均分 31 | 32 | {{filter.toFix(termScore, 2)}} 33 | 34 | 35 | 36 | 37 | 38 | 已出课程数 39 | 40 | {{termNums}}/{{termNums + ungraded}} 41 | 42 | 43 | 44 | 选课更新时间:{{course_time}} 45 | 分数更新时间:{{time}} 46 | 47 | 48 | 49 | 50 | 51 | 70 | 71 | 72 | 73 | 74 | 75 | 成绩排序 76 | 77 | 78 | 79 | 80 | 81 | {{value.type}} (共 {{value.credits}} 学分) 82 | 83 | 84 | 87 | {{item.name}}{{item.credit}} | {{item.id}} | {{item.instructor}} | {{item.comment}} 88 | 点击查看 89 | {{filter.toFix(item.gpa, 2)}} | {{item.score}} 90 | 失效需授权 91 | 授权后订阅 92 | 已订阅 93 | {{item.comment}} 94 | 无分数 95 | 点击订阅 96 | 无法订阅 97 | 98 | 99 | 100 | 101 | 102 | 点击右上角 … 进行分享后好友可看到您当前页成绩 103 | 115 | -------------------------------------------------------------------------------- /miniprogram/pages/more/more.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | const app: IMyApp = getApp() 4 | 5 | Page({ 6 | data: { 7 | logged_in: false, 8 | share_score: true, 9 | show_exam: true, 10 | show_courses: true, 11 | switches: {}, 12 | messages: undefined, 13 | open_status: undefined, 14 | status_messages: undefined, 15 | adMessage: undefined, 16 | showAd: false, 17 | 18 | showActionsheet: false, 19 | groups: [], 20 | actionSheetTitle: undefined, 21 | is_no_password: false, 22 | finished: false, 23 | }, 24 | 25 | onLoad: function(options) { 26 | wx.showToast({ 27 | title: '加载中', 28 | icon: 'loading', 29 | duration: 6000, 30 | }) 31 | wx.$request({ 32 | path: 'more', 33 | actions: false, 34 | methods: 'GET', 35 | type: 'any', 36 | success: (result: any) => { 37 | this.setData({ 38 | messages: result.more_messages, 39 | footer: result.footer_messages, 40 | open_status: result.open_status, 41 | status_messages: result.status_messages, 42 | finished: true, 43 | }) 44 | wx.hideToast() 45 | }, 46 | failed: () => { 47 | wx.navigateBack() 48 | }, 49 | }) 50 | if (options.logged_in) { 51 | this.setData({logged_in: true}) 52 | } else { 53 | this.setData({logged_in: false}) 54 | } 55 | }, 56 | 57 | onShow: function() { 58 | if ( 59 | app.globalData.scoreData && typeof app.globalData.scoreData.result !== 'undefined' && wx.$validateType(app.globalData.scoreData.result, 'term') 60 | ) { 61 | this.setData({ 62 | share_score: app.globalData.scoreData.result.share_score, 63 | switches: app.globalData.scoreData.result.switches, 64 | is_no_password: app.globalData.scoreData.result.is_no_password, 65 | }) 66 | wx.$createVideoAd(this) 67 | } 68 | this.setData({ 69 | show_exam: app.globalData.show_exam, 70 | show_courses: app.globalData.show_courses, 71 | }) 72 | }, 73 | 74 | bindtapHandeler: function(e) { 75 | switch (e.currentTarget.dataset.from) { 76 | case 'CET': 77 | wx.navigateTo({ 78 | url: '/pages/CET/CET', 79 | }) 80 | break 81 | 82 | case 'exam': 83 | wx.switchTab({ 84 | url: '/pages/exam/exam', 85 | }) 86 | break 87 | 88 | case 'entireClass': 89 | wx.switchTab({ 90 | url: '/pages/entireClass/entireClass', 91 | }) 92 | break 93 | 94 | default: 95 | break 96 | } 97 | }, 98 | 99 | cancel: function() { 100 | this.setData({ 101 | showActionsheet: true, 102 | actionSheetTitle: '此操作会删除本地所有数据,但不会撤销授权。你将需要重新登录', 103 | groups: [ 104 | { text: '确认退出登录', type: 'warn', value: 1 }, 105 | ], 106 | }) 107 | }, 108 | 109 | demo: function() { 110 | wx.showLoading({ 111 | title: '登录中', 112 | }) 113 | wx.login({ 114 | success: (res) => { 115 | wx.$request({ 116 | actions: false, 117 | methods: 'POST', 118 | data: { 119 | 'no': this.data.status_messages.demo.id, 120 | 'pass': '', 121 | 'code': res.code, 122 | }, 123 | path: 'login', 124 | success: wx.$loginSuccess, 125 | type: 'any', 126 | }) 127 | }, 128 | }) 129 | }, 130 | 131 | close: function() { 132 | this.setData({ 133 | showActionsheet: false, 134 | }) 135 | }, 136 | 137 | btnClick: function(e) { 138 | switch (e.detail.value) { 139 | case 1: 140 | wx.$logout() 141 | break 142 | case 2: 143 | wx.showToast({ 144 | title: '取消中', 145 | icon: 'loading', 146 | duration: 6000, 147 | }) 148 | wx.$request({ 149 | path: 'share_score', 150 | actions: false, 151 | methods: 'DELETE', 152 | type: 'any', 153 | success: (resp: null, messages) => { 154 | this.onLoad() 155 | let title = '已经取消共享' 156 | let content = '你的分数已经被删除,分数订阅已经取消' 157 | if (messages.length > 0) { 158 | title = messages[0] 159 | } 160 | if (messages.length > 1) { 161 | content = messages[1] 162 | } 163 | wx.showModal({ 164 | title: title, 165 | content: content, 166 | showCancel: false, 167 | success: () => { 168 | if ( 169 | typeof app.globalData.scoreData !== 'undefined' && typeof app.globalData.scoreData.result !== 'undefined' && 170 | typeof app.globalData.scoreData.result.share_score !== 'undefined' 171 | ) { 172 | app.globalData.scoreData.result.share_score = false 173 | } 174 | wx.setStorageSync('share_score', false) 175 | this.setData({share_score: false}) 176 | app.globalData.refreshCourses = true 177 | wx.pageScrollTo({ 178 | scrollTop: 0, 179 | duration: 0, 180 | }) 181 | wx.hideToast() 182 | }, 183 | }) 184 | }, 185 | }) 186 | break 187 | case 3: 188 | wx.showToast({ 189 | title: '重置中', 190 | icon: 'loading', 191 | duration: 6000, 192 | }) 193 | const success = () => { 194 | wx.showToast({ 195 | title: '重置成功', 196 | icon: 'success', 197 | duration: 500, 198 | }) 199 | } 200 | wx.$request({ 201 | actions: false, 202 | path: 'subscribe/RESET', 203 | success, 204 | noneData: success, 205 | type: 'any', 206 | methods: 'POST', 207 | }) 208 | break 209 | default: 210 | } 211 | this.close() 212 | }, 213 | 214 | optIn: function() { 215 | wx.navigateTo({ 216 | url: '/pages/scoreDetailPage/scoreDetailPage?id=0007929', 217 | }) 218 | }, 219 | 220 | optOut: function() { 221 | this.setData({ 222 | showActionsheet: true, 223 | actionSheetTitle: '此操作会删除你的分数和微信绑定。毕业后教务账号会被学校回收,撤销将导致毕业后再也无法使用此程序', 224 | groups: [ 225 | { text: '确认撤销授权', type: 'warn', value: 2 }, 226 | ], 227 | }) 228 | }, 229 | 230 | resetSubscribe: function() { 231 | this.setData({ 232 | showActionsheet: true, 233 | actionSheetTitle: '此操作会重置所有订阅并重新绑定微信号', 234 | groups: [ 235 | { text: '确认重置订阅', type: 'warn', value: 3 }, 236 | ], 237 | }) 238 | }, 239 | connectAccount: function() { 240 | this.btnClick({ 241 | detail: { 242 | value: 3, 243 | }, 244 | }) 245 | }, 246 | cancelNoWarning: function() { 247 | this.btnClick({ 248 | detail: { 249 | value: 1, 250 | }, 251 | }) 252 | }, 253 | ad: function() { 254 | wx.showToast({ 255 | title: '加载广告中', 256 | icon: 'loading', 257 | duration: 3000, 258 | }) 259 | wx.$loadVideoAd() 260 | }, 261 | }) 262 | 263 | export {} 264 | -------------------------------------------------------------------------------- /typings/bjut/index.d.ts: -------------------------------------------------------------------------------- 1 | import GeneralCallbackResult = WechatMiniprogram.GeneralCallbackResult 2 | type ApiMethods = 'GET'|'POST'|'DELETE' 3 | type ApiRespTypes = 'term'|'schedule'|'exams'|'overview'|'detail'|'cets'|'any' 4 | 5 | interface IExtendedWx extends WechatMiniprogram.Wx { 6 | $formatDate(arg0: any), 7 | $base64(arg0: any), 8 | $initTab(arg0: any), 9 | $waterMark(arg0: any), 10 | $forCourses(c: ICourse[], unread: { [courseId: string]: ICourse }, minor?: boolean, all?: boolean): {unreadCount, credits, pass, ungraded, unsubscribed, passCredits, gradedCredits, nums}, 11 | $request(options: { 12 | success: (resp: T, messages: string[], errors?: IApiError[]) => void, 13 | noneData?: () => void, 14 | failed?: (res?) => void, 15 | complete?: (res?) => void, 16 | actions: boolean, 17 | path: string, 18 | methods: ApiMethods, 19 | type: ApiRespTypes, 20 | data?: object, 21 | }), 22 | $validateType(arg: any, type: ApiRespTypes): boolean, 23 | $getTypeOrder(arg: string): number, 24 | $validateTypeResp(arg: IApi, type: ApiRespTypes, callbacks?: {noneData?: () => void, failed?: (res?) => void, success: (resp: T, messages: string[]) => void}): boolean, 25 | $loginSuccess(resp: any): void, 26 | $createVideoAd(arg: any): void, 27 | $loadVideoAd(): void, 28 | $logout(): void, 29 | } 30 | 31 | // tslint:disable-next-line:interface-name 32 | interface Wx extends WechatMiniprogram.Wx { 33 | [functionName: string]: any 34 | } 35 | 36 | // @ts-ignore 37 | declare const wx: IExtendedWx 38 | 39 | interface IMyApp { 40 | // @ts-ignore 41 | getCurrentPage(): wx.Page, 42 | globalData: IGlobalData 43 | } 44 | 45 | interface IGlobalData { 46 | unread?: { 47 | [courseId: string]: ICourse, 48 | }, 49 | scoreData?: IApi, 50 | student_id?: string, 51 | authorization?: string, 52 | show_exam?: boolean, 53 | show_courses?: boolean, 54 | refreshCourses?: boolean, 55 | map?: { 56 | [courseId: string]: boolean, 57 | }, 58 | exam_tab?: number, 59 | courses_tab?: number, 60 | background?: string, 61 | schedule: ISchedule, 62 | backgroundFetchTimestamp: number, 63 | redirect?: { 64 | year?: string, 65 | term?: string, 66 | courseId?: string, 67 | }, 68 | open?: string, 69 | videoAd?: WechatMiniprogram.RewardedVideoAd, 70 | sceneId: number, 71 | url_api: string, 72 | url_logo: string, 73 | } 74 | 75 | interface IResp { 76 | /** 回调函数返回的内容 */ 77 | data: T, 78 | /** 开发者服务器返回的 HTTP 状态码 */ 79 | statusCode: number, 80 | /** 开发者服务器返回的 HTTP Response Header */ 81 | header: object, 82 | } 83 | 84 | interface IApi { 85 | errors: IApiError[], 86 | messages: string[], 87 | result: T, 88 | success: boolean, 89 | result_info?: { 90 | count: number, 91 | page: number, 92 | per_page: number, 93 | total_count: number, 94 | }, 95 | encrypted?: { 96 | iv: string, 97 | data: string, 98 | } 99 | } 100 | 101 | interface IApiError { 102 | code: number, 103 | message: string, 104 | } 105 | 106 | interface IRespWithTime { 107 | time: number, 108 | updated_time?: number 109 | } 110 | 111 | interface IRespWithList { 112 | list: T[] 113 | } 114 | 115 | interface ITerms extends IRespWithTime { 116 | ad_messages: string[], // TODO: 解释每个string的含义 117 | ad_times: number, 118 | current_year_term: string, 119 | exam_week: { 120 | [current_year_term: string]: [number, number], // [0] 为开始周,[1] 为结束周,[1] 可能大于 [0] 121 | }, 122 | registration_week: { 123 | [current_year_term: string]: [number, number], 124 | }, 125 | share_message: { 126 | imageUrl: string, 127 | imageUrlId: string, 128 | title: string, 129 | }, 130 | switches: { 131 | cet: boolean, 132 | }, 133 | share_score: boolean, 134 | has_open: boolean, 135 | is_no_password: boolean, 136 | course_time: number, // 课表更新时间 137 | subscribe_ids: string[], // 订阅成绩的模版 id 列表,至少一个 138 | first_semester?: string, // 入学时间 139 | average_GPA_minor?: number, // 辅修 GPA 140 | average_GPA_term?: number, // 在校期间总 GPA 141 | average_score_minor?: number, // 辅修加权 142 | average_score_term?: number, // 总加权 143 | term_lesson_count?: number, // 已出分的课程数 144 | term_lesson_minor?: number, // 辅修课程数 145 | sid?: string, // 学号 146 | class?: string, // 班级号 147 | institute?: string, // 学院 148 | major?: string, 149 | name?: string, 150 | terms?: { 151 | [year: string]: { 152 | [term: string]: ITerm, 153 | }, 154 | }, 155 | shared?: string, 156 | ad_id?: string, 157 | ad_id_banner?: string, 158 | } 159 | 160 | interface ITerm { 161 | course_time?: number, 162 | average_GPA_minor: number, 163 | average_GPA_term: number, 164 | average_score_minor: number, 165 | average_score_term: number, 166 | term_lesson_count: number, 167 | courses: (IGradedCourse|IUngradedCourse)[] 168 | } 169 | 170 | interface ICourse { 171 | academy: string, 172 | belong: string, 173 | comment: string, 174 | credit: string, 175 | gpa: string|number, 176 | id: string, 177 | makeup_score: string, 178 | minor_maker: string, 179 | name: string, 180 | retake_maker: string, 181 | retake_score: string, 182 | score: string|number, 183 | type: string, 184 | unread?: boolean, 185 | term: string, 186 | year: string, 187 | } 188 | 189 | interface IGradedCourse extends ICourse { 190 | gpa: string, 191 | score: string, 192 | } 193 | 194 | interface IUngradedCourse extends ICourse { 195 | courseSelectId: string, 196 | gpa: number, 197 | score: number, 198 | hours: string, 199 | instructor: string, 200 | room: string, 201 | selected: boolean, // 选修课 202 | textbook: boolean, // 需要教材 203 | time: string, // 上课时间 204 | } 205 | 206 | interface IFetchCallback extends GeneralCallbackResult { 207 | fetchedData: string, 208 | timeStamp: number, 209 | } 210 | 211 | interface IOverview { 212 | A: number, 213 | B: number, 214 | C: number, 215 | F: number, 216 | academy: string, 217 | academy_full: string[], 218 | avg: number, 219 | belong: string, 220 | count: number, 221 | credit: string, 222 | id: string, 223 | id_full: string[], 224 | max: number, 225 | name: string, 226 | std: number, 227 | type: string, 228 | type_full: string[], 229 | majors?: string[], 230 | is_elective?: boolean 231 | } 232 | 233 | interface IDetail extends IOverview, IRespWithTime { 234 | year_term?: { 235 | [name: string]: { 236 | score: number, 237 | gpa: number, 238 | count: number, 239 | }, 240 | }, 241 | instructors?: { 242 | [name: string]: { 243 | score: number, 244 | gpa: number, 245 | count: number, 246 | }, 247 | }, 248 | scores?: { 249 | [score: string]: number, 250 | }, 251 | } 252 | 253 | interface ISchedule extends IRespWithTime, IRespWithList { 254 | term: string, 255 | week: { 256 | [year: string]: { 257 | 1: number, 258 | 2: number, 259 | }, 260 | }, 261 | year: string, 262 | shared?: string, 263 | major?: string, 264 | name?: string, 265 | id?: string, 266 | semesters?: string[], 267 | } 268 | 269 | interface IExam { 270 | campus: string, 271 | courseName: string, 272 | id: string, 273 | name: string, 274 | room: string, 275 | seat: string, 276 | time: string, 277 | type: string, 278 | } 279 | 280 | interface IExams extends IRespWithTime, IRespWithList {} 281 | 282 | interface ICet { 283 | comprehensive: string, 284 | date: string, 285 | id: string, 286 | listening: string, 287 | name: string, 288 | reading: string, 289 | term: string, 290 | total: string, 291 | year: string, 292 | } 293 | 294 | interface ICets extends IRespWithTime { 295 | results: ICet[], 296 | } 297 | -------------------------------------------------------------------------------- /miniprogram/ec-canvas/ec-canvas.js: -------------------------------------------------------------------------------- 1 | import WxCanvas from './wx-canvas'; 2 | import * as echarts from './echarts'; 3 | 4 | let ctx; 5 | 6 | function compareVersion(v1, v2) { 7 | v1 = v1.split('.') 8 | v2 = v2.split('.') 9 | const len = Math.max(v1.length, v2.length) 10 | 11 | while (v1.length < len) { 12 | v1.push('0') 13 | } 14 | while (v2.length < len) { 15 | v2.push('0') 16 | } 17 | 18 | for (let i = 0; i < len; i++) { 19 | const num1 = parseInt(v1[i]) 20 | const num2 = parseInt(v2[i]) 21 | 22 | if (num1 > num2) { 23 | return 1 24 | } else if (num1 < num2) { 25 | return -1 26 | } 27 | } 28 | return 0 29 | } 30 | 31 | Component({ 32 | properties: { 33 | canvasId: { 34 | type: String, 35 | value: 'ec-canvas' 36 | }, 37 | 38 | ec: { 39 | type: Object 40 | }, 41 | 42 | forceUseOldCanvas: { 43 | type: Boolean, 44 | value: false 45 | } 46 | }, 47 | 48 | data: { 49 | isUseNewCanvas: false 50 | }, 51 | 52 | ready: function () { 53 | // Disable prograssive because drawImage doesn't support DOM as parameter 54 | // See https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.drawImage.html 55 | echarts.registerPreprocessor(option => { 56 | if (option && option.series) { 57 | if (option.series.length > 0) { 58 | option.series.forEach(series => { 59 | series.progressive = 0; 60 | }); 61 | } 62 | else if (typeof option.series === 'object') { 63 | option.series.progressive = 0; 64 | } 65 | } 66 | }); 67 | 68 | if (!this.data.ec) { 69 | console.warn('组件需绑定 ec 变量,例:'); 71 | return; 72 | } 73 | 74 | if (!this.data.ec.lazyLoad) { 75 | this.init(); 76 | } 77 | }, 78 | 79 | methods: { 80 | init: function (callback) { 81 | const version = wx.getSystemInfoSync().SDKVersion 82 | 83 | const canUseNewCanvas = compareVersion(version, '2.9.0') >= 0; 84 | const forceUseOldCanvas = this.data.forceUseOldCanvas; 85 | const isUseNewCanvas = canUseNewCanvas && !forceUseOldCanvas; 86 | this.setData({ isUseNewCanvas }); 87 | 88 | if (forceUseOldCanvas && canUseNewCanvas) { 89 | console.warn('开发者强制使用旧canvas,建议关闭'); 90 | } 91 | 92 | if (isUseNewCanvas) { 93 | // console.log('微信基础库版本大于2.9.0,开始使用'); 94 | // 2.9.0 可以使用 95 | this.initByNewWay(callback); 96 | } else { 97 | const isValid = compareVersion(version, '1.9.91') >= 0 98 | if (!isValid) { 99 | console.error('微信基础库版本过低,需大于等于 1.9.91。' 100 | + '参见:https://github.com/ecomfe/echarts-for-weixin' 101 | + '#%E5%BE%AE%E4%BF%A1%E7%89%88%E6%9C%AC%E8%A6%81%E6%B1%82'); 102 | return; 103 | } else { 104 | console.warn('建议将微信基础库调整大于等于2.9.0版本。升级后绘图将有更好性能'); 105 | this.initByOldWay(callback); 106 | } 107 | } 108 | }, 109 | 110 | initByOldWay(callback) { 111 | // 1.9.91 <= version < 2.9.0:原来的方式初始化 112 | ctx = wx.createCanvasContext(this.data.canvasId, this); 113 | const canvas = new WxCanvas(ctx, this.data.canvasId, false); 114 | 115 | echarts.setCanvasCreator(() => { 116 | return canvas; 117 | }); 118 | // const canvasDpr = wx.getSystemInfoSync().pixelRatio // 微信旧的canvas不能传入dpr 119 | const canvasDpr = 1 120 | var query = wx.createSelectorQuery().in(this); 121 | query.select('.ec-canvas').boundingClientRect(res => { 122 | if (typeof callback === 'function') { 123 | this.chart = callback(canvas, res.width, res.height, canvasDpr); 124 | } 125 | else if (this.data.ec && typeof this.data.ec.onInit === 'function') { 126 | this.chart = this.data.ec.onInit(canvas, res.width, res.height, canvasDpr); 127 | } 128 | else { 129 | this.triggerEvent('init', { 130 | canvas: canvas, 131 | width: res.width, 132 | height: res.height, 133 | canvasDpr: canvasDpr // 增加了dpr,可方便外面echarts.init 134 | }); 135 | } 136 | }).exec(); 137 | }, 138 | 139 | initByNewWay(callback) { 140 | // version >= 2.9.0:使用新的方式初始化 141 | const query = wx.createSelectorQuery().in(this) 142 | query 143 | .select('.ec-canvas') 144 | .fields({ node: true, size: true }) 145 | .exec(res => { 146 | try { 147 | const canvasNode = res[0].node 148 | this.canvasNode = canvasNode 149 | 150 | const canvasDpr = wx.getSystemInfoSync().pixelRatio 151 | const canvasWidth = res[0].width 152 | const canvasHeight = res[0].height 153 | 154 | const ctx = canvasNode.getContext('2d') 155 | 156 | const canvas = new WxCanvas(ctx, this.data.canvasId, true, canvasNode) 157 | echarts.setCanvasCreator(() => { 158 | return canvas 159 | }) 160 | 161 | if (typeof callback === 'function') { 162 | this.chart = callback(canvas, canvasWidth, canvasHeight, canvasDpr) 163 | } else if (this.data.ec && typeof this.data.ec.onInit === 'function') { 164 | this.chart = this.data.ec.onInit(canvas, canvasWidth, canvasHeight, canvasDpr) 165 | } else { 166 | this.triggerEvent('init', { 167 | canvas: canvas, 168 | width: canvasWidth, 169 | height: canvasHeight, 170 | dpr: canvasDpr 171 | }) 172 | } 173 | } catch (e) { 174 | console.log(e) 175 | } 176 | }) 177 | }, 178 | canvasToTempFilePath(opt) { 179 | if (this.data.isUseNewCanvas) { 180 | // 新版 181 | const query = wx.createSelectorQuery().in(this) 182 | query 183 | .select('.ec-canvas') 184 | .fields({ node: true, size: true }) 185 | .exec(res => { 186 | const canvasNode = res[0].node 187 | opt.canvas = canvasNode 188 | wx.canvasToTempFilePath(opt) 189 | }) 190 | } else { 191 | // 旧的 192 | if (!opt.canvasId) { 193 | opt.canvasId = this.data.canvasId; 194 | } 195 | ctx.draw(true, () => { 196 | wx.canvasToTempFilePath(opt, this); 197 | }); 198 | } 199 | }, 200 | 201 | touchStart(e) { 202 | if (this.chart && e.touches.length > 0) { 203 | var touch = e.touches[0]; 204 | var handler = this.chart.getZr().handler; 205 | handler.dispatch('mousedown', { 206 | zrX: touch.x, 207 | zrY: touch.y 208 | }); 209 | handler.dispatch('mousemove', { 210 | zrX: touch.x, 211 | zrY: touch.y 212 | }); 213 | handler.processGesture(wrapTouch(e), 'start'); 214 | } 215 | }, 216 | 217 | touchMove(e) { 218 | if (this.chart && e.touches.length > 0) { 219 | var touch = e.touches[0]; 220 | var handler = this.chart.getZr().handler; 221 | handler.dispatch('mousemove', { 222 | zrX: touch.x, 223 | zrY: touch.y 224 | }); 225 | handler.processGesture(wrapTouch(e), 'change'); 226 | } 227 | }, 228 | 229 | touchEnd(e) { 230 | if (this.chart) { 231 | const touch = e.changedTouches ? e.changedTouches[0] : {}; 232 | var handler = this.chart.getZr().handler; 233 | handler.dispatch('mouseup', { 234 | zrX: touch.x, 235 | zrY: touch.y 236 | }); 237 | handler.dispatch('click', { 238 | zrX: touch.x, 239 | zrY: touch.y 240 | }); 241 | handler.processGesture(wrapTouch(e), 'end'); 242 | } 243 | } 244 | } 245 | }); 246 | 247 | function wrapTouch(event) { 248 | for (let i = 0; i < event.touches.length; ++i) { 249 | const touch = event.touches[i]; 250 | touch.offsetX = touch.x; 251 | touch.offsetY = touch.y; 252 | } 253 | return event; 254 | } 255 | -------------------------------------------------------------------------------- /miniprogram/pages/scoreDetailPage/scoreDetailPage.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | // 获取应用实例 4 | // @ts-ignore 5 | import * as echarts from '../../ec-canvas/echarts' 6 | 7 | const app: IMyApp = getApp() 8 | let chart = null 9 | 10 | // 全校成绩分布 11 | const scoreDistribute = [0, 0, 0, 0, 0, 0, 0, 0, 0, 12 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 14 | 15 | let chartNext = function() {} 16 | let init = false 17 | 18 | // 分布图初始化(先不画出来) 19 | function initChart(canvas, width, height, dpr) { 20 | try { 21 | chart = echarts.init(canvas, null, { 22 | width: width, 23 | height: height, 24 | devicePixelRatio: dpr, // new 25 | }) 26 | canvas.setChart(chart) 27 | chart.setOption({}) 28 | init = true 29 | chartNext() 30 | return chart 31 | } catch (e) { 32 | console.log(e) 33 | } 34 | } 35 | 36 | Page({ 37 | 38 | data: { 39 | option: {}, 40 | 41 | // 服务器返回信息 42 | class: null, 43 | 44 | // 课程基本信息 45 | className: '', 46 | classID: undefined, 47 | credit: undefined, 48 | classType: undefined, 49 | academy: undefined, 50 | 51 | // 你的成绩 52 | yourScore: undefined, 53 | yourGPA: undefined, 54 | predict: undefined, 55 | 56 | // 全校平均成绩 57 | averScore: null, 58 | averGPA: null, 59 | stdDev: null, 60 | GPAdistribution_ABC_Rate: null, 61 | GPAdistribution_A_Rate: null, 62 | higestScoree: null, 63 | GPAdistribution_A: null, 64 | GPAdistribution_B: null, 65 | GPAdistribution_C: null, 66 | GPAdistribution_D: null, 67 | 68 | // 更新时间与样本量 69 | count: 0, 70 | 71 | // 是否显示成绩分布直方图 72 | ifDistribution: true, 73 | 74 | ec: { 75 | onInit: initChart, 76 | }, 77 | halfScreen: { 78 | show: false, 79 | title: '加载中', 80 | subtitle: undefined, 81 | desc: '', 82 | tips: '', 83 | }, 84 | buttons: [{ 85 | type: 'primary', 86 | text: '接受', 87 | }, { 88 | type: 'default', 89 | text: '拒绝', 90 | }], 91 | demo: false, 92 | hidePersonal: false, 93 | share_score: false, 94 | messages: [], 95 | my_time: null, 96 | time: null, 97 | timestamp: 0, 98 | 99 | ad: undefined, 100 | }, 101 | 102 | redirected: false, 103 | 104 | onLoad: function(option) { 105 | if ( 106 | typeof app.globalData.scoreData === 'undefined' || !app.globalData.scoreData || 107 | !wx.$validateType(app.globalData.scoreData.result, 'term') || 108 | !app.globalData.authorization || !app.globalData.student_id 109 | ) { 110 | wx.reLaunch({ 111 | url: '/pages/login/login', 112 | }) 113 | return 114 | } 115 | wx.showToast({ 116 | title: '加载中', 117 | icon: 'loading', 118 | duration: 6000, 119 | }) 120 | const classID = option.id 121 | if ( 122 | typeof option.year !== 'undefined' && typeof option.term !== 'undefined' && 123 | typeof app.globalData.unread[option.year + '-' + option.term + '-' + classID] !== 'undefined' 124 | ) { 125 | const classDetail = app.globalData.unread[option.year + '-' + option.term + '-' + classID] 126 | option.score = String(classDetail.score) 127 | option.gpa = String(classDetail.gpa) 128 | option.name = classDetail.name 129 | } 130 | this.setData({ 131 | yourScore: option.score, 132 | yourGPA: option.gpa, 133 | classID: classID, 134 | share_score: app.globalData.scoreData.result.share_score, 135 | ad: app.globalData.scoreData.result.ad_id_banner, 136 | my_time: wx.$formatDate(app.globalData.scoreData.result.time), 137 | option, 138 | }) 139 | if (typeof option.name !== 'undefined' && option.name) { 140 | this.setData({ className: option.name }) 141 | } 142 | if (option.score === 'NotAScore' || option.gpa === 'NotAScore') { 143 | // 此时为全校成绩页导航至此,不显示个人成绩卡片 144 | this.setData({ hidePersonal: true }) 145 | } 146 | 147 | // 通过api获取课程详情成绩信息 148 | wx.$request({ 149 | path: 'overview/scores/' + classID, 150 | actions: false, 151 | methods: 'GET', 152 | type: 'detail', 153 | failed: () => { 154 | wx.navigateBack() 155 | }, 156 | success: (result: IDetail, messages) => { 157 | this.setData({messages: [], time: wx.$formatDate(result.time), timestamp: result.time}) 158 | if (messages.length > 2) { 159 | const data: any = { 160 | show: true, 161 | title: messages[0], 162 | desc: messages[1], 163 | tips: messages[2], 164 | } 165 | if (messages.length > 3) { 166 | data.subtitle = messages[3] 167 | } else { 168 | data.subtitle = undefined 169 | } 170 | this.initDate(result, option.score) 171 | wx.hideToast() 172 | this.setData ({ 173 | halfScreen: data, 174 | demo: true, 175 | }) 176 | } else { 177 | this.setData({messages: messages}) 178 | this.initDate(result, option.score) 179 | wx.hideToast() 180 | } 181 | wx.$waterMark(this) 182 | }, 183 | }) 184 | }, 185 | 186 | onShow: function() { 187 | this.setData({timestamp: this.data.timestamp, time: wx.$formatDate(this.data.timestamp), my_time: wx.$formatDate(app.globalData.scoreData.result.time)}) 188 | }, 189 | 190 | shareScore: function() { 191 | wx.showToast({ 192 | title: '授权中', 193 | icon: 'loading', 194 | duration: 6000, 195 | }) 196 | wx.$request({ 197 | path: 'share_score', 198 | actions: true, 199 | methods: 'POST', 200 | type: 'any', 201 | success: () => { 202 | app.globalData.scoreData.result.share_score = true 203 | this.setData({share_score: true, halfScreen: {show: false}}) 204 | wx.setStorageSync('share_score', true) 205 | wx.showModal({ 206 | title: '共享分数成功', 207 | content: '请重新进入该课程详情页面', 208 | showCancel: false, 209 | success() { 210 | app.globalData.refreshCourses = true 211 | wx.navigateBack({delta: 1}) 212 | }, 213 | }) 214 | }, 215 | failed: () => { 216 | wx.navigateBack() 217 | }, 218 | }) 219 | }, 220 | 221 | bindHalf: function(e) { 222 | switch (e.detail.index) { 223 | case 0: 224 | this.shareScore() 225 | break 226 | case 1: 227 | this.setData ({ 228 | halfScreen: {show: false}, 229 | }) 230 | break 231 | } 232 | }, 233 | 234 | 235 | initDate: function(data: IDetail, yourScore) { 236 | let predict = null 237 | if (data.scores !== null) { 238 | predict = data.count 239 | for (let i = 100; i > yourScore; i--) { 240 | predict -= data.scores[i] 241 | } 242 | predict = (predict / data.count * 100).toFixed(0) + '%' 243 | } 244 | if (this.data.className) { 245 | data.name = this.data.className 246 | } 247 | this.setData({ 248 | className: data.name, 249 | credit: data.credit, 250 | classType: data.type, 251 | academy: data.academy, 252 | type_full: data.type_full, 253 | academy_full: data.academy_full, 254 | majors: data.majors, 255 | id_full: data.id_full, 256 | // 全校平均成绩 257 | averScore: data.avg, 258 | averGPA: (4.0 * data.A + 3.0 * data.B + 2.0 * data.C) / (data.A + data.B + data.C + data.F), 259 | stdDev: data.std, 260 | higestScoree: data.max, 261 | GPAdistribution_A: data.A, 262 | GPAdistribution_B: data.B, 263 | GPAdistribution_C: data.C, 264 | GPAdistribution_D: data.F, 265 | GPAdistribution_A_Rate: data.A / data.count * 100, 266 | GPAdistribution_ABC_Rate: (data.A + data.B + data.C) / data.count * 100, 267 | // 时间与样本量 268 | count: data.count, 269 | predict: predict, 270 | instructors: data.instructors, 271 | year_term: data.year_term, 272 | }) 273 | 274 | if (data.scores === null) { 275 | this.setData({ifDistribution: false}) 276 | return 277 | } 278 | 279 | for (let i = 60; i <= 100; i++) { 280 | scoreDistribute[i - 60] = data.scores[i] 281 | } 282 | 283 | chartNext = function() { 284 | // 画出分布图 285 | chart.setOption({ 286 | color: ['#5e5e5e'], 287 | 288 | grid: { 289 | left: 10, 290 | right: 40, 291 | bottom: 20, 292 | top: 40, 293 | containLabel: true, 294 | }, 295 | xAxis: [ 296 | { 297 | type: 'value', 298 | name: '人数', 299 | axisLine: { 300 | lineStyle: { 301 | color: '#999', 302 | }, 303 | }, 304 | axisLabel: { 305 | color: '#666', 306 | }, 307 | 308 | }, 309 | ], 310 | yAxis: [ 311 | { 312 | type: 'category', 313 | name: '成绩/分', 314 | axisTick: { show: false }, 315 | data: [60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 316 | 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100], 317 | axisLine: { 318 | lineStyle: { 319 | color: '#999', 320 | }, 321 | }, 322 | axisLabel: { 323 | color: '#666', 324 | }, 325 | }, 326 | ], 327 | series: [ 328 | { 329 | name: '人数', 330 | type: 'bar', 331 | label: { 332 | normal: { 333 | show: true, 334 | position: 'right', 335 | }, 336 | }, 337 | data: scoreDistribute, 338 | itemStyle: { 339 | // emphasis: { 340 | // color: '#37a2da' 341 | // } 342 | }, 343 | }, 344 | ], 345 | }) 346 | } 347 | 348 | if (init) { 349 | chartNext() 350 | } 351 | }, 352 | }) 353 | 354 | export {} 355 | -------------------------------------------------------------------------------- /miniprogram/pages/overAllPage/overAllPage.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 欢迎回来! 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 学号:{{sid}} 23 | 学院:{{institute}} 24 | 25 | 26 | 姓名:{{name}} 27 | 专业:{{major}} 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | {{filter.startYear(item.year, item.term, first_semester)}} 43 | 44 | 45 | 46 | 47 | 通过率:{{filter.toFix(item.detail.passRate, 0)}}% 48 | 学分:{{item.detail.passCredits}}/{{item.detail.totalCredit}} 49 | 50 | 51 | 52 | 53 | 54 | 55 | GPA 56 | 57 | {{filter.toFix(item.detail.gpa, 2)}} 58 | 59 | 60 | 61 | 62 | 63 | 加权平均分 64 | 65 | {{filter.toFix(item.detail.score, 2)}} 66 | 67 | 68 | 69 | 70 | 71 | 已出课程数 72 | 73 | {{item.detail.count}}/{{item.detail.count+item.ungraded}} 74 | 75 | 76 | 77 | 78 | 79 | 分数更新时间:{{time}} 80 | 选课更新时间:{{item.course_time}} 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 第一学位成绩 89 | 总成绩 90 | 91 | 92 | 93 | 94 | 通过率:{{filter.toFix(passRate_total, 0)}}% 95 | 学分:{{passCredit_total}}/{{totalCredit_total}} 96 | 97 | 98 | 99 | 100 | 101 | 102 | GPA 103 | 104 | {{filter.toFix(totalGPA, 2)}} 105 | 106 | 107 | 108 | 109 | 110 | 加权平均分 111 | 112 | {{filter.toFix(totalScore, 2)}} 113 | 114 | 115 | 116 | 117 | 118 | 已出课程数 119 | 120 | {{totalNums}}/{{totalUngraded+totalNums}} 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 辅修/双学位成绩 129 | 130 | 131 | 132 | 133 | 通过率:{{filter.toFix(passRate_minor, 0)}}% 134 | 学分:{{passCredit_minor}}/{{totalCredit_minor}} 135 | 136 | 137 | 138 | 139 | 140 | 141 | GPA 142 | 143 | {{filter.toFix(minorGPA, 2)}} 144 | 145 | 146 | 147 | 148 | 149 | 加权平均分 150 | 151 | {{filter.toFix(minorScore, 2)}} 152 | 153 | 154 | 155 | 156 | 157 | 已出课程数 158 | 159 | {{minorNums}}/{{minorNums+minorUngraded}} 160 | 161 | 162 | 163 | 164 | 165 | 166 | 各学期成绩 167 | 168 | 169 | 170 | 171 | 172 | 设置/关于 173 | 174 | 175 | 176 | {{item}} 177 | 178 | 点击右上角 … 进行分享,分享此页不会分享分数 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 可能是教务分数接口目前不可用,请稍后再试 190 | 191 | 192 | 193 | 194 | 195 | Copyright © 野生工大助手 196 | 197 | 198 | 199 | 200 | 201 | 本小程序支持查分、查课表、查考试、出分提醒,专供北工大在校生和已毕业校友使用,点击页面底部 “前往小程序” 登录 202 | 203 | Copyright © 野生工大助手 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /miniprogram/pages/scoreDetailPage/scoreDetailPage.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 你的成绩 11 | 12 | 13 | {{yourName}}的 14 | {{className}} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 绩点 23 | 24 | {{filter.toFix(yourGPA, 1)}} 25 | 26 | 27 | 28 | 29 | 得分 30 | 31 | {{yourScore}} 32 | 33 | 34 | 35 | 36 | 超越 37 | 38 | {{predict}} 39 | 40 | 41 | 42 | 43 | 44 | 45 | 绩点 46 | 47 | 48 | 49 | 50 | 51 | 52 | 得分 53 | 54 | 55 | 56 | 57 | 58 | 59 | 超越 60 | 61 | 62 | 63 | 64 | 65 | 分数更新时间:{{my_time}} 66 | 统计更新时间:{{time}} 67 | 68 | 69 | 70 | 71 | {{item}} 72 | 73 | 74 | 年级分数情况 75 | 76 | 77 | 演示 - {{className}} 78 | 79 | 80 | 课程性质:{{filter.join(type_full, ', ')}} 81 | 82 | 开设学院:{{filter.join(academy_full, '\n ')}} 83 | 课程代码:{{filter.join(id_full, ', ')}} 84 | 85 | 开设专业:{{filter.join(majors, ', ')}} 86 | 87 | 88 | 89 | 90 | 91 | 平均绩点 92 | 93 | {{filter.toFix(averGPA, 2)}} 94 | 95 | 96 | 97 | 98 | 平均分 99 | 100 | {{filter.toFix(averScore, 2)}} 101 | 102 | 103 | 104 | 105 | 标准差 106 | 107 | {{filter.toFix(stdDev, 2)}} 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 优秀率 116 | 117 | {{filter.toFix(GPAdistribution_A_Rate, 1)}}% 118 | 119 | 120 | 121 | 122 | 及格率 123 | 124 | {{filter.toFix(GPAdistribution_ABC_Rate, 1)}}% 125 | 126 | 127 | 128 | 129 | 最高分 130 | 131 | {{higestScoree}} 132 | 133 | 134 | 135 | 136 | 成绩分布直方图 137 | 138 | 139 | 140 | 141 | 142 | 143 | 该课程样本量太少,不予显示分布直方图 144 | 145 | 146 | 147 | 148 | 149 | A (4.0) 150 | 151 | 152 | {{GPAdistribution_A}} 153 | 154 | 155 | 156 | B (3.0) 157 | 158 | 159 | {{GPAdistribution_B}} 160 | 161 | 162 | 163 | C (2.0) 164 | 165 | 166 | {{GPAdistribution_C}} 167 | 168 | 169 | 170 | F (0.0) 171 | 172 | 173 | {{GPAdistribution_D}} 174 | 175 | 176 | 177 | 更新时间:{{time}} 178 | 样本量:{{count}} 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 学期平均成绩 187 | 188 | 189 | {{index}} ({{item.count}}) 190 | {{filter.toFix(item.gpa/item.count, 2)}} ({{filter.diff(item.gpa/item.count - averGPA)}}) | {{filter.toFix(item.score/item.count, 2)}} ({{filter.diff(item.score/item.count - averScore)}}) 191 | 192 | 193 | 194 | 195 | 196 | 教师平均成绩 197 | 198 | 199 | {{index}} ({{item.count}}) 200 | {{filter.toFix(item.gpa/item.count, 2)}} ({{filter.diff(item.gpa/item.count - averGPA)}}) | {{filter.toFix(item.score/item.count, 2)}} ({{filter.diff(item.score/item.count - averScore)}}) 201 | 202 | 203 | 204 | 216 | -------------------------------------------------------------------------------- /miniprogram/pages/entireClass/entireClass.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | const app: IMyApp = getApp() 4 | 5 | Page({ 6 | defaultData: { 7 | // 全部课程数 8 | count: undefined, 9 | // 当前页显示的课程 10 | classes_show: [], 11 | // 当前显示页 12 | curPage: 0, 13 | // 显示起始项与显示终止项序号 14 | startIndex: undefined, 15 | endIndex: undefined, 16 | // 页面号 17 | pages: [], 18 | // 每页显示内容 19 | perpage: 10, 20 | // 课程种类 21 | types: [], 22 | curType: '全部课程类型', 23 | academys: [], 24 | major: undefined, 25 | curAcademy: '全部学院', 26 | // 搜索框输入内容 27 | searchKey: '', 28 | showClearBtn: false, 29 | messages: [], 30 | checked: false, 31 | checkedElective: false, 32 | // 当前排序方式 true为降序 33 | sortD: { 34 | 'avg': true, 35 | 'count': true, 36 | 'excellentRate': true, 37 | 'passRate': true, 38 | 'gpa': true, 39 | }, 40 | ad: undefined, 41 | }, 42 | data: {}, 43 | // 全部课程 44 | resDate: [], 45 | classes: [], 46 | classesRaw: [], 47 | classesNew: [], 48 | searchKey: '', 49 | time: 0, 50 | onInput(evt) { 51 | this.searchKey = evt.detail.value 52 | this.setData({ 53 | showClearBtn: !!this.searchKey.length, 54 | }) 55 | this.search(this.searchKey) 56 | }, 57 | onClearCount() { 58 | this.search('') 59 | }, 60 | search: function(searchKey) { 61 | if (searchKey === '') { 62 | this.setData({ 63 | searchKey: '', 64 | showClearBtn: false, 65 | }) 66 | } 67 | const classes: IOverview[] = this.classesRaw 68 | // 筛选全部课程列表 更新总页数 总课程数 69 | const classesNew = [] 70 | for (const i in classes) { 71 | // tslint:disable-next-line:triple-equals 72 | if ((classes[i].name.toLowerCase() + ' / ' + classes[i].id_full.join(' / ')).indexOf(searchKey.toLowerCase()) != -1) { 73 | classesNew.push(classes[i]) 74 | } 75 | } 76 | const totalPageNumber = parseInt(String((classesNew.length - 1) / this.data.perpage + 1), 10) 77 | const pages = [] 78 | for (let i = 1; i <= totalPageNumber; i += 1) { pages.push(i) } 79 | this.classesNew = classesNew 80 | this.setData({ 81 | count: classesNew.length, 82 | pages: pages, 83 | }) 84 | // 重置各数据 85 | this.changeShow(0) 86 | }, 87 | 88 | // 根据页数,更改显示内容、显示起始项、显示终止项序号 89 | changeShow: function(curPage) { 90 | if (typeof this.classesNew === 'undefined' || !this.classesNew || typeof this.classesNew.slice !== 'function') { 91 | return 92 | } 93 | const classes_show: IOverview[] = this.classesNew.slice(curPage * this.data.perpage, curPage * this.data.perpage + this.data.perpage) 94 | let endIndex: number 95 | if ((curPage + 1) * this.data.perpage < this.data.count) { endIndex = (curPage + 1) * this.data.perpage } else { endIndex = this.data.count} 96 | this.setData({ 97 | curPage: curPage, 98 | classes_show: classes_show, 99 | startIndex: curPage * this.data.perpage + 1, 100 | endIndex: endIndex, 101 | }) 102 | wx.pageScrollTo({ 103 | scrollTop: 0, 104 | duration: 0, 105 | }) 106 | }, 107 | 108 | bindPageChange: function(e) { 109 | const curPage = parseInt(e.detail.value, 10) 110 | this.changeShow(curPage) 111 | }, 112 | 113 | bindTypeChange: function(e) { 114 | const curType = this.data.types[parseInt(e.detail.value, 10)] 115 | this.setData({curType: curType, classes_show: []}, () => { 116 | this.filter() 117 | }) 118 | }, 119 | 120 | bindAcademyChange: function(e) { 121 | const curAcademy = this.data.academys[parseInt(e.detail.value, 10)] 122 | this.setData({curAcademy: curAcademy, classes_show: []}, () => { 123 | this.filter() 124 | }) 125 | }, 126 | 127 | filter: function() { 128 | const classesNew: IOverview[] = [] 129 | const {curType, curAcademy} = this.data 130 | // 筛选type与academy 131 | for (const i in this.resDate) { 132 | const curClass: IOverview = this.resDate[i] 133 | if ((curType === '全部课程类型' || curClass.type_full.indexOf(this.data.curType) !== -1) && (curAcademy === '全部学院' || curClass.academy_full.indexOf(this.data.curAcademy) !== -1)) { 134 | if (typeof this.data.major === 'undefined') { 135 | classesNew.push(curClass) 136 | } else if (this.data.major === '仅公选课' && curClass.is_elective) { 137 | classesNew.push(curClass) 138 | } else if (curClass.majors.indexOf(this.data.major) !== -1) { 139 | classesNew.push(curClass) 140 | } 141 | } 142 | } 143 | 144 | this.classesRaw = classesNew 145 | this.classes = classesNew 146 | this.setData({ 147 | count: classesNew.length, 148 | }) 149 | this.search('') 150 | this.changeShow(0) 151 | }, 152 | 153 | setTypesAndAcademies: function(classesRaw: IOverview[]) { 154 | const typeSet: any = {} 155 | const academySet: any = {} 156 | for (const i of classesRaw) { 157 | for (const j of i.type_full) { 158 | if (typeof typeSet[j] === 'undefined') { 159 | typeSet[j] = 1 160 | } else { 161 | ++typeSet[j] 162 | } 163 | } 164 | for (const j of i.academy_full) { 165 | if (typeof academySet[j] === 'undefined') { 166 | academySet[j] = 1 167 | } else { 168 | ++academySet[j] 169 | } 170 | } 171 | } 172 | 173 | if (typeof academySet.体育教学部 !== 'undefined') { 174 | academySet.体育教学部 = Number.MAX_SAFE_INTEGER - 100 175 | } 176 | 177 | const types = [] 178 | for (const i in typeSet) { 179 | if (typeSet[i] >= 10 || wx.$getTypeOrder(i) <= 100) { 180 | types.push(i) 181 | } 182 | } 183 | const academy = [] 184 | for (const i in academySet) { 185 | academy.push(i) 186 | } 187 | if (!types || typeof types.sort !== 'function') { 188 | return 189 | } 190 | types.sort((a: string, b: string) => { 191 | let l = wx.$getTypeOrder(a) 192 | let r = wx.$getTypeOrder(b) 193 | if (l >= Number.MAX_SAFE_INTEGER - 100) { 194 | l -= typeSet[a] 195 | } 196 | if (r === Number.MAX_SAFE_INTEGER - 100) { 197 | r -= typeSet[b] 198 | } 199 | return l - r 200 | }) 201 | if (!academy || typeof academy.sort !== 'function') { 202 | return 203 | } 204 | academy.sort((a: string, b: string) => { 205 | return academySet[b] - academySet[a] 206 | }) 207 | types.unshift('全部课程类型') 208 | academy.unshift('全部学院') 209 | this.setData({types: types, academys: academy}) 210 | }, 211 | 212 | resp: undefined, 213 | onLoad: function() { 214 | if ( 215 | typeof app.globalData.scoreData === 'undefined' || !app.globalData.scoreData || 216 | !wx.$validateType(app.globalData.scoreData.result, 'term') 217 | ) { 218 | wx.reLaunch({ 219 | url: '/pages/login/login', 220 | }) 221 | return 222 | } 223 | this.defaultData.ad = app.globalData.scoreData.result.ad_id_banner 224 | this.setData(JSON.parse(JSON.stringify(this.defaultData)), () => { 225 | const success = (result: IOverview[], messages) => { 226 | this.resDate = result 227 | this.classes = result 228 | this.classesRaw = result 229 | this.classesNew = result 230 | this.setData({ 231 | count: result.length, 232 | messages: messages, 233 | }) 234 | if (typeof this.classes === 'undefined' || !this.classes || typeof this.classes.slice !== 'function') { 235 | return 236 | } 237 | // 默认显示第一页 238 | const classes_show: IOverview[] = this.classes.slice(this.data.curPage, this.data.curPage + this.data.perpage) 239 | let endIndex 240 | if (this.data.perpage < this.classes.length) { endIndex = this.data.perpage } else { endIndex = this.classes.length} 241 | this.setData({ 242 | classes_show: classes_show, 243 | startIndex: 1, 244 | endIndex: endIndex, 245 | }) 246 | 247 | // 遍历所有课程 总结课程种类信息 248 | this.setTypesAndAcademies(this.classesRaw) 249 | 250 | // 计算总页数 251 | const totalPageNumber = parseInt(String((this.classes.length - 1) / this.data.perpage + 1), 10) 252 | const pages = [] 253 | for (let i = 1; i <= totalPageNumber; i += 1) { pages.push(i) } 254 | this.setData({pages: pages}) 255 | wx.showToast({ 256 | title: '加载成功', 257 | icon: 'success', 258 | duration: 500, 259 | }) 260 | wx.$waterMark(this) 261 | } 262 | if (this.resp) { 263 | success(this.resp.resp, this.resp.messages) 264 | wx.stopPullDownRefresh() 265 | return 266 | } 267 | this.time = +new Date() / 1000 268 | // 获取全部成绩列表 269 | wx.showLoading({ 270 | title: '加载中', 271 | }) 272 | wx.$request({ 273 | path: 'overview/scores', 274 | actions: false, 275 | methods: 'GET', 276 | type: 'overview', 277 | success: (resp, messages) => { 278 | this.resp = { 279 | resp, 280 | messages, 281 | } 282 | success(resp, messages) 283 | }, 284 | failed: () => { 285 | wx.hideLoading() 286 | }, 287 | complete: () => { 288 | wx.stopPullDownRefresh() 289 | }, 290 | }) 291 | }) 292 | }, 293 | 294 | pagingHandeler: function(e) { 295 | this.setData({classes_show: []}, () => { 296 | let curPage = this.data.curPage 297 | if (e.currentTarget.dataset.d === 'up') { 298 | // 上翻页 299 | if (curPage > 0) { 300 | curPage -= 1 301 | } else { 302 | // 弹窗 已到首页 303 | wx.showToast({ 304 | title: '已到首页', 305 | icon: 'none', 306 | duration: 1500, 307 | }) 308 | } 309 | } else { 310 | // 下翻页 311 | if (curPage < this.data.pages.length - 1) { 312 | curPage += 1 313 | } else { 314 | // 弹窗 已到末页 315 | wx.showToast({ 316 | title: '已到末页', 317 | icon: 'none', 318 | duration: 1500, 319 | }) 320 | } 321 | } 322 | 323 | this.changeShow(curPage) 324 | 325 | }) 326 | }, 327 | 328 | JumpHandeler: function(e) { 329 | const id = e.currentTarget.dataset.id 330 | wx.navigateTo({ 331 | url: '../scoreDetailPage/scoreDetailPage?id=' + id + '&gpa=' + 'NotAScore' + '&score=' + 'NotAScore', 332 | }) 333 | }, 334 | 335 | sort: function(event) { 336 | this.setData({ 337 | classes_show: [], 338 | }, () => { 339 | const classesNew: IOverview[] = this.classesNew 340 | const poperty = event.currentTarget.dataset.poperty 341 | const sortD = this.data.sortD 342 | const d = true 343 | this.setData({ 344 | sortD: sortD, 345 | }) 346 | if (!classesNew || typeof classesNew.sort !== 'function') { 347 | return 348 | } 349 | classesNew.sort(function(a, b) { 350 | let score0 351 | let score1 352 | switch (poperty) { 353 | case 'avg': 354 | score0 = a.avg 355 | score1 = b.avg 356 | break 357 | case 'count': 358 | score0 = a.count 359 | score1 = b.count 360 | break 361 | case 'excellentRate': 362 | score0 = a.A / a.count 363 | score1 = b.A / b.count 364 | break 365 | case 'passRate': 366 | score0 = (a.A + a.B + a.C) / a.count 367 | score1 = (b.A + b.B + b.C) / b.count 368 | break 369 | case 'gpa': 370 | score0 = (a.A * 4 + a.B * 3 + a.C * 2) / a.count 371 | score1 = (b.A * 4 + b.B * 3 + b.C * 2) / b.count 372 | break 373 | default: 374 | return 0 375 | } 376 | if (typeof score0 !== 'number' || isNaN(score0)) { 377 | score0 = -1 378 | } 379 | if (typeof score1 !== 'number' || isNaN(score1)) { 380 | score1 = -1 381 | } 382 | // 默认降序,再次点击后升序 383 | if (d) { 384 | return score1 - score0 385 | } else { 386 | return score0 - score1 387 | } 388 | }) 389 | this.classes = classesNew 390 | this.changeShow(0) 391 | }) 392 | }, 393 | 394 | onShow: function() { 395 | if (typeof this.getTabBar === 'function' && this.getTabBar()) { 396 | wx.$initTab(this.getTabBar()) 397 | this.getTabBar().setData({ 398 | selected: app.globalData.courses_tab, 399 | }) 400 | } 401 | if (app.globalData.refreshCourses) { 402 | this.onLoad() 403 | app.globalData.refreshCourses = false 404 | } 405 | }, 406 | 407 | onPullDownRefresh: function() { 408 | if (this.time + 6 > +new Date() / 1000) { 409 | wx.stopPullDownRefresh() 410 | wx.showToast({ 411 | title: '手速太快了,等一会儿再刷新试试吧', 412 | icon: 'none', 413 | duration: 3000, 414 | }) 415 | return 416 | } 417 | this.onLoad() 418 | }, 419 | 420 | bindMajor: function(e) { 421 | this.setData({classes_show: []}, () => { 422 | if (e.detail.value.length === 2) { 423 | if (this.data.major === '仅公选课') { 424 | e.detail.value = ['majorOnly'] 425 | } else { 426 | e.detail.value = ['elective'] 427 | } 428 | } 429 | if (e.detail.value[0] === 'majorOnly') { 430 | this.data.major = app.globalData.scoreData.result.major 431 | this.setData({checkedElective: false}) 432 | } else if (e.detail.value[0] === 'elective') { 433 | this.data.major = '仅公选课' 434 | this.setData({checked: false}) 435 | } else { 436 | this.data.major = undefined 437 | this.setData({checked: false, checkedElective: false}) 438 | } 439 | this.setData({curAcademy: '全部学院', curType: '全部课程类型'}) 440 | this.filter() 441 | this.setTypesAndAcademies(this.classesNew) 442 | if (this.data.major === '仅公选课') { 443 | this.setData({types: ['全部课程类型', '体育课', '通识教育选修课', '外语选修课', '通识教育任意选修', '经济管理选修课', 444 | '数学与自然科学选修课', '校选修课', '经管文法艺术类选修课', '工程自然类选修课']}) 445 | } 446 | }) 447 | }, 448 | }) 449 | 450 | export {} 451 | -------------------------------------------------------------------------------- /miniprogram/pages/overAllPage/overAllPage.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | const app: IMyApp = getApp() 4 | 5 | Page({ 6 | data: { 7 | name: null, // 姓名 8 | institute: null, // 学院 9 | sid: null, // 学号 10 | major: null, // 专业 11 | first_semester: '9999-9999-9', 12 | 13 | totalGPA: null, // 总成绩-GPA 14 | totalScore: null, // 总成绩-加权平均分 15 | totalNums: null, // 总成绩-已出课程数 16 | passRate_total: null, // 总成绩-通过率 17 | totalCredit_total: null, // 总成绩-总学分 18 | passCredit_total: null, // 总成绩-通过学分 19 | totalUnread: 0, // 总成绩-未读 20 | totalUngraded: 0, // 总成绩-未出分 21 | 22 | minorGPA: null, 23 | minorScore: null, 24 | minorNums: null, 25 | minorUngraded: null, 26 | passRate_minor: null, 27 | totalCredit_minor: null, 28 | passCredit_minor: null, 29 | 30 | term: [], 31 | termNums: 0, 32 | 33 | success: 0, 34 | fail: false, 35 | messages: [], 36 | time: null, 37 | share_score: false, 38 | has_open: false, 39 | is_no_password: false, 40 | sceneId: app.globalData.sceneId, 41 | }, 42 | time: 0, 43 | request: undefined, 44 | timeoutId: undefined, 45 | 46 | onLoad: function() { 47 | if (app.globalData.sceneId === 1154) { 48 | this.setData( { success: 3 } ) 49 | return 50 | } else { 51 | wx.setNavigationBarTitle({ 52 | title: '成绩总览', 53 | }) 54 | } 55 | if (app.globalData.sceneId === 1155) { 56 | const authorization = wx.getStorageSync('authorization3') 57 | const student_id = wx.getStorageSync('student_id') 58 | const open = wx.getStorageSync('open') 59 | if (authorization && student_id && open) { 60 | app.globalData.authorization = authorization 61 | app.globalData.student_id = student_id 62 | app.globalData.open = open 63 | } else { 64 | wx.reLaunch({ 65 | url: '/pages/login/login', 66 | }) 67 | return 68 | } 69 | } 70 | 71 | // 通过api获取分数信息 72 | this.setData( { success: 0 } ) 73 | 74 | wx.showToast({ 75 | title: '更新成绩中', 76 | icon: 'loading', 77 | duration: 6000, 78 | }) 79 | if (typeof app.globalData.redirect !== 'undefined' && typeof app.globalData.redirect.term !== 'undefined' && typeof app.globalData.redirect.year !== 'undefined') { 80 | this.fetchData() 81 | } else { 82 | const score: IApi = wx.getStorageSync('score') 83 | if (score && wx.$validateType(score.result, 'term') && !score.result.is_no_password) { 84 | this.time = score.result.updated_time 85 | app.globalData.scoreData = score 86 | this.setData({messages: score.messages, time: wx.$formatDate(score.result.time), course_time: wx.$formatDate(app.globalData.scoreData.result.course_time)}) 87 | this.scoreSparser(score.result) 88 | // 不是基础库 2.12.0,直接再发一次请求 89 | if (!wx.canIUse('updateWeChatApp')) { 90 | this.fetchData() 91 | } else { 92 | wx.showToast({ 93 | title: '等待预拉取中', 94 | icon: 'loading', 95 | duration: 3000, 96 | }) 97 | this.timeoutId = setTimeout(() => { 98 | if (typeof this.request === 'undefined') { 99 | wx.showToast({ 100 | title: '重试中', 101 | icon: 'loading', 102 | duration: 6000, 103 | }) 104 | this.fetchData() 105 | } 106 | }, 3000) 107 | } 108 | this.setBackgroundFetch() 109 | } else { 110 | this.fetchData() 111 | } 112 | } 113 | 114 | // @ts-ignore 115 | // TODO: Typings 错误 116 | wx.onBackgroundFetchData((res) => { 117 | console.log('onBackgroundFetchData接收到数据') 118 | this.parseFetchData(res) 119 | }) 120 | }, 121 | 122 | setBackgroundFetch: function() { 123 | wx.getBackgroundFetchData({ 124 | fetchType: 'pre', 125 | success: this.parseFetchData, 126 | fail: () => { 127 | this.fetchData() 128 | }, 129 | }) 130 | }, 131 | 132 | parseFetchData: function(res: IFetchCallback) { 133 | if (res && (res.errMsg === 'getBackgroundFetchData:ok' || res.errMsg === 'onBackgroundFetchData:ok')) { 134 | const {fetchedData} = res 135 | let parsedData: IApi<{score: ITerms}> 136 | if (fetchedData) { 137 | parsedData = JSON.parse(fetchedData) 138 | } 139 | if ( 140 | wx.$validateTypeResp(parsedData, 'any') && typeof parsedData.result.score !== 'undefined' 141 | && parsedData.result.score && wx.$validateType(parsedData.result.score, 'term') 142 | ) { 143 | app.globalData.scoreData = { 144 | success: parsedData.success, 145 | result: parsedData.result.score, 146 | messages: parsedData.messages, 147 | errors: parsedData.errors, 148 | } 149 | this.setData({messages: parsedData.messages, time: wx.$formatDate(parsedData.result.score.time), course_time: wx.$formatDate(app.globalData.scoreData.result.course_time)}) 150 | this.scoreSparser(parsedData.result.score) 151 | if (this.time < parsedData.result.score.updated_time) { 152 | // 预拉取到的是新数据 153 | this.time = parsedData.result.score.updated_time 154 | wx.setStorageSync('score', app.globalData.scoreData) 155 | wx.showToast({ 156 | title: '预拉取成功', 157 | icon: 'success', 158 | duration: 500, 159 | }) 160 | console.log('预拉取成功', parsedData.result.score.updated_time) 161 | if (typeof this.request !== 'undefined' && typeof this.request.abort === 'function') { 162 | this.request.abort() 163 | } 164 | this.request = {} 165 | if (typeof this.timeoutId !== 'undefined') { 166 | clearTimeout(this.timeoutId) 167 | this.timeoutId = undefined 168 | } 169 | } 170 | } else if (typeof this.request === 'undefined') { 171 | wx.showToast({ 172 | title: '重试中', 173 | icon: 'loading', 174 | duration: 6000, 175 | }) 176 | this.fetchData() 177 | } 178 | } 179 | }, 180 | 181 | fetchData: function() { 182 | if (typeof this.request !== 'undefined') { 183 | return 184 | } 185 | this.time = +new Date() / 1000 - 5 186 | this.request = wx.$request({ 187 | path: 'scores/term', 188 | actions: true, 189 | methods: 'GET', 190 | type: 'term', 191 | success: (result, messages, errors) => { 192 | if (errors && errors.length > 0) { 193 | wx.hideToast() 194 | wx.showModal({ 195 | content: errors.map((elem) => { 196 | return elem.message 197 | }).join(' '), 198 | showCancel: false, 199 | }) 200 | } else { 201 | wx.showToast({ 202 | title: '更新成功', 203 | icon: 'success', 204 | duration: 500, 205 | }) 206 | } 207 | app.globalData.scoreData = { 208 | result, 209 | messages, 210 | success: true, 211 | errors: [], 212 | } 213 | if (result.term_lesson_count > 0) { 214 | wx.setStorageSync('score', app.globalData.scoreData) 215 | this.setData({messages, time: wx.$formatDate(result.time), course_time: wx.$formatDate(app.globalData.scoreData.result.course_time)}) 216 | } else { 217 | this.setData( { success: 2 } ) 218 | } 219 | this.scoreSparser(result) 220 | 221 | if (typeof app.globalData.redirect !== 'undefined') { 222 | const {year, term, courseId} = app.globalData.redirect 223 | app.globalData.redirect = undefined 224 | if (typeof year !== 'undefined' && typeof term !== 'undefined' && typeof courseId !== 'undefined') { 225 | const unreadKey = year + '-' + term + '-' + courseId 226 | if (typeof app.globalData.unread[unreadKey] !== 'undefined') { 227 | app.globalData.unread[unreadKey].unread = false 228 | wx.navigateTo({ 229 | url: '/pages/scoreDetailPage/scoreDetailPage?year=' + year + '&term=' + term + '&id=' + courseId + 230 | '&instructor=' + encodeURIComponent((app.globalData.unread[unreadKey] as IUngradedCourse).instructor), 231 | }) 232 | } 233 | } 234 | } 235 | }, 236 | noneData: () => { 237 | this.setData({success: 2}) 238 | }, 239 | complete: () => { 240 | if (typeof this.timeoutId !== 'undefined') { 241 | clearTimeout(this.timeoutId) 242 | this.timeoutId = undefined 243 | } 244 | wx.stopPullDownRefresh() 245 | }, 246 | }) 247 | }, 248 | 249 | scoreSparser: function(result: ITerms) { 250 | if (typeof this.getTabBar === 'function') { 251 | wx.$initTab(this.getTabBar()) 252 | } 253 | const unread: { 254 | [courseId: string]: ICourse, 255 | } = {} 256 | if (result == null) { 257 | this.setData({success: 2}) 258 | return 259 | } 260 | if (typeof result.terms === 'undefined' || !result.terms) { 261 | this.setData({success: 2}) 262 | return 263 | } 264 | this.setData({success: 1}) 265 | // 解析得到最新学期的成绩 总成绩 通过率与总学分 266 | let lastYear = Object.keys(result.terms)[Object.keys(result.terms).length - 1] 267 | let lastTerm = Object.keys(result.terms[lastYear])[Object.keys(result.terms[lastYear]).length - 1] 268 | for (const year in result.terms) { 269 | for (const term in result.terms[year]) { 270 | if (result.terms[year][term].term_lesson_count > 0) { 271 | lastYear = year 272 | lastTerm = term 273 | } 274 | } 275 | } 276 | const t = [] 277 | this.setData({ 278 | name: result.name, 279 | institute: result.institute, 280 | sid: result.sid, 281 | major: result.major, 282 | 283 | totalGPA: result.average_GPA_term, 284 | totalScore: result.average_score_term, 285 | totalNums: result.term_lesson_count, 286 | share_score: result.share_score, 287 | has_open: result.has_open, 288 | is_no_password: result.is_no_password, 289 | minorGPA: result.average_GPA_minor, 290 | minorScore: result.average_score_minor, 291 | minorNums: result.term_lesson_minor, 292 | first_semester: result.first_semester, 293 | }) 294 | 295 | let passCredit_total = 0 296 | let passGraded_total = 0 297 | let totalCredit_total = 0 298 | let totalUnread = 0 299 | let totalUngraded = 0 300 | let termNums = 0 301 | let minorUngraded = 0 302 | let passCredit_minor = 0 303 | let totalCredit_minor = 0 304 | 305 | for (const year in result.terms) { 306 | for (const term in result.terms[year]) { 307 | if (result.terms[year][term].term_lesson_count > 0) { 308 | ++termNums 309 | } 310 | const {unreadCount, ungraded, unsubscribed} = wx.$forCourses(result.terms[year][term].courses, unread) 311 | const {credits, passCredits, gradedCredits, ungraded: ungradedTotal} = wx.$forCourses(result.terms[year][term].courses, unread, false, false) 312 | 313 | if ( 314 | year + '-' + term >= app.globalData.scoreData.result.current_year_term || 315 | (year === lastYear && term === lastTerm) || 316 | unreadCount > 0 || ungraded > 0 317 | ) { 318 | t.push({ 319 | year, 320 | term, 321 | detail: { 322 | gpa: result.terms[year][term].average_GPA_term, 323 | score: result.terms[year][term].average_score_term, 324 | count: result.terms[year][term].term_lesson_count, 325 | passRate: passCredits / gradedCredits * 100, 326 | totalCredit: credits, 327 | passCredits, 328 | }, 329 | unread: unreadCount, 330 | ungraded, 331 | unsubscribed, 332 | course_time: wx.$formatDate(result.terms[year][term].course_time), 333 | }) 334 | } 335 | passCredit_total += passCredits 336 | passGraded_total += gradedCredits 337 | totalCredit_total += credits 338 | totalUnread += unreadCount 339 | totalUngraded += ungradedTotal 340 | const minor = wx.$forCourses(result.terms[year][term].courses, {}, true) 341 | passCredit_minor += minor.passCredits 342 | totalCredit_minor += minor.credits 343 | minorUngraded += minor.ungraded 344 | } 345 | } 346 | let passRate_total = 0 347 | if (result.term_lesson_count > 0) { 348 | passRate_total = passCredit_total / passGraded_total * 100 349 | } 350 | 351 | this.setData({ 352 | passRate_total, 353 | totalCredit_total, 354 | passCredit_total, 355 | termNums, 356 | totalUnread, 357 | totalUngraded, 358 | term: t, 359 | passRate_minor: passCredit_minor / totalCredit_minor * 100, 360 | totalCredit_minor, 361 | passCredit_minor, 362 | minorUngraded, 363 | }) 364 | app.globalData.unread = unread 365 | }, 366 | 367 | bindtapHandeler: function(e) { 368 | switch (e.currentTarget.dataset.from) { 369 | case 'total': 370 | wx.navigateTo({ 371 | url: '/pages/scoreList/scoreList?id=total', 372 | }) 373 | break 374 | case 'minor': 375 | wx.navigateTo({ 376 | url: '/pages/scoreList/scoreList?id=minor', 377 | }) 378 | break 379 | case 'term': 380 | const {year, term} = e.currentTarget.dataset 381 | wx.navigateTo({ 382 | url: '/pages/scoreList/scoreList?year=' + year + '&term=' + term, 383 | }) 384 | break 385 | case 'history': 386 | wx.navigateTo({ 387 | url: '/pages/previousScore/previousScore', 388 | }) 389 | break 390 | case 'more': 391 | wx.navigateTo({ 392 | url: '/pages/more/more?logged_in=1', 393 | }) 394 | break 395 | case 'reload': 396 | wx.showToast({ 397 | title: '刷新中', 398 | icon: 'loading', 399 | duration: 6000, 400 | }) 401 | this.request = undefined 402 | this.fetchData() 403 | break 404 | 405 | default: 406 | break 407 | } 408 | }, 409 | 410 | onShareAppMessage: function() { 411 | if (typeof app.globalData.scoreData !== 'undefined' && wx.$validateType(app.globalData.scoreData.result, 'term')) { 412 | return { 413 | title: app.globalData.scoreData.result.share_message.title, 414 | imageUrlId: app.globalData.scoreData.result.share_message.imageUrlId, 415 | imageUrl: app.globalData.scoreData.result.share_message.imageUrl, 416 | path: '/pages/login/login', 417 | } 418 | } 419 | return { 420 | title: '快来查分啦', 421 | path: '/pages/login/login', 422 | } 423 | }, 424 | 425 | onShareTimeline: function() { 426 | if (typeof app.globalData.scoreData !== 'undefined' && wx.$validateType(app.globalData.scoreData.result, 'term')) { 427 | return { 428 | title: app.globalData.scoreData.result.share_message.title, 429 | query: '', 430 | } 431 | } 432 | return { 433 | title: '快来查分啦', 434 | query: '', 435 | } 436 | }, 437 | 438 | onPullDownRefresh: function() { 439 | if (app.globalData.sceneId === 1154) { 440 | wx.showToast({title: '请点击页面底部的 “前往小程序” 登录', icon: 'none', duration: 3000}) 441 | wx.stopPullDownRefresh() 442 | return 443 | } 444 | if (this.time + 10 > +new Date() / 1000) { 445 | wx.stopPullDownRefresh() 446 | wx.showToast({ 447 | title: '手速太快了,等一会儿再刷新试试吧', 448 | icon: 'none', 449 | duration: 3000, 450 | }) 451 | return 452 | } 453 | wx.showToast({ 454 | title: '更新成绩中', 455 | icon: 'loading', 456 | duration: 6000, 457 | }) 458 | this.request = undefined 459 | this.fetchData() 460 | }, 461 | 462 | onShow: function() { 463 | if (typeof this.getTabBar === 'function' && this.getTabBar()) { 464 | wx.$initTab(this.getTabBar()) 465 | this.getTabBar().setData({ 466 | selected: 0, 467 | }) 468 | } 469 | 470 | if (typeof app.globalData.scoreData !== 'undefined' && app.globalData.scoreData && 471 | typeof app.globalData.scoreData.result !== 'undefined' 472 | && wx.$validateType(app.globalData.scoreData.result, 'term')) { 473 | this.setData({ 474 | time: wx.$formatDate(app.globalData.scoreData.result.time), 475 | course_time: wx.$formatDate(app.globalData.scoreData.result.course_time), 476 | }) 477 | this.scoreSparser(app.globalData.scoreData.result) 478 | } 479 | 480 | if (this.data.fail) { 481 | this.setData({success: 2}) 482 | wx.showModal({ 483 | title: '无法连接到服务器,请检查网络', 484 | showCancel: false, 485 | }) 486 | } 487 | 488 | this.setData({fail: false}) 489 | }, 490 | }) 491 | 492 | export {} 493 | -------------------------------------------------------------------------------- /miniprogram/pages/index/index.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | // index.js 4 | // 获取应用实例 5 | const app: IMyApp = getApp() 6 | 7 | Page({ 8 | time: 0, 9 | share_title: '快查查北工大最新的课表', 10 | theme: 'light', 11 | originalData: { 12 | // curWeek: 15, 13 | finished: true, 14 | 15 | classes: { 16 | '1,2节': ['1 2节', '', '', '', '', '', '', ''], 17 | '3,4节': ['3 4节', '', '', '', '', '', '', ''], 18 | '5,6节': ['5 6节', '', '', '', '', '', '', ''], 19 | '7,8节': ['7 8节', '', '', '', '', '', '', ''], 20 | '9,10节': ['9 10节', '', '', '', '', '', '', ''], 21 | '11,12节': ['11 12节', '', '', '', '', '', '', ''], 22 | }, 23 | 24 | backgroundColor: 'transparent', 25 | optionalColorList: ['#fbcd9e', '#e4b0f5', '#9ef2e0', '#fb93ab', '#b1b1dc', '#b4cfc2', '#ead3c7', '#ffba71', '#ffccff', '#a1afc9', '#a4e2c6', '#eedeb0', '#96ce54', '#c2ccd0' ], 26 | 27 | color: { 28 | '1,2节': ['var(--weui-BG-0);', '', '', '', '', '', '', ''], 29 | '3,4节': ['var(--weui-BG-0);', '', '', '', '', '', '', ''], 30 | '5,6节': ['var(--weui-BG-0);', '', '', '', '', '', '', ''], 31 | '7,8节': ['var(--weui-BG-0);', '', '', '', '', '', '', ''], 32 | '9,10节': ['var(--weui-BG-0);', '', '', '', '', '', '', ''], 33 | '11,12节': ['var(--weui-BG-0);', '', '', '', '', '', '', ''], 34 | }, 35 | 36 | marker: { 37 | '1,2节': [0, 0, 0, 0, 0, 0, 0, 0], 38 | '3,4节': [0, 0, 0, 0, 0, 0, 0, 0], 39 | '5,6节': [0, 0, 0, 0, 0, 0, 0, 0], 40 | '7,8节': [0, 0, 0, 0, 0, 0, 0, 0], 41 | '9,10节': [0, 0, 0, 0, 0, 0, 0, 0], 42 | '11,12节': [0, 0, 0, 0, 0, 0, 0, 0], 43 | }, 44 | 45 | comment: '', 46 | message: '', 47 | messages: [], 48 | semester: '最新学期', 49 | semesters: ['最新学期'], 50 | sceneId: app.globalData.sceneId, 51 | }, 52 | 53 | data: { 54 | noTime: [], 55 | allClasses: 0, 56 | }, 57 | shared: { 58 | title: '分享错误,请重试', 59 | }, 60 | isShared: false, 61 | semester: '0/0', 62 | semesters: ['最新学期'], 63 | 64 | onLoad: function(options) { 65 | if (app.globalData.sceneId !== 1154) { 66 | wx.setNavigationBarTitle({ 67 | title: '我的课表', 68 | }) 69 | } 70 | this.theme = wx.getSystemInfoSync().theme 71 | if ( typeof wx.onThemeChange === 'function') { 72 | wx.onThemeChange(() => { 73 | this.onShow() 74 | }) 75 | } 76 | this.setData({finished: false}) 77 | const score: IApi = wx.getStorageSync('score') 78 | if (score && wx.$validateType(score.result, 'term') && !score.result.is_no_password) { 79 | app.globalData.scoreData = score 80 | } 81 | 82 | if (app.globalData.sceneId === 1155) { 83 | options = {} 84 | } 85 | 86 | if (options && options.shared && options.id && options.semester) { 87 | app.globalData.authorization = options.shared 88 | app.globalData.student_id = options.id 89 | app.globalData.open = '' 90 | this.semester = options.semester 91 | if (app.globalData.sceneId === 1154) { 92 | this.originalData.comment = '点击 “前往小程序” 查看自己的课表' 93 | } else { 94 | this.originalData.comment = '下拉刷新以查看自己的课表' 95 | } 96 | this.fetch(false, true) 97 | const authorization = wx.getStorageSync('authorization3') 98 | const student_id = wx.getStorageSync('student_id') 99 | const open = wx.getStorageSync('open') 100 | if (authorization && student_id && open) { 101 | app.globalData.authorization = authorization 102 | app.globalData.student_id = student_id 103 | app.globalData.open = open 104 | } else { 105 | app.globalData.authorization = '' 106 | app.globalData.student_id = '' 107 | app.globalData.open = '' 108 | } 109 | this.semester = '0/0' 110 | } else { 111 | this.originalData.comment = '' 112 | const schedule: ISchedule = wx.getStorageSync('schedule') 113 | if (schedule && wx.$validateType(schedule, 'schedule')) { 114 | app.globalData.schedule = schedule 115 | this.time = schedule.updated_time 116 | this.initSchedule(schedule) 117 | } 118 | if (this.time + 21600 < +new Date() / 1000) { 119 | this.fetch() 120 | } else if (app.globalData.scoreData && app.globalData.scoreData.result && app.globalData.scoreData.result.share_score === true) { 121 | this.fetch(false, false) 122 | } 123 | } 124 | }, 125 | 126 | fetch: function(actions: boolean = true, shared: boolean = false) { 127 | this.time = +new Date() / 1000 128 | wx.showToast({ 129 | title: '更新课表中', 130 | icon: 'loading', 131 | duration: 6000, 132 | }) 133 | wx.$request({ 134 | path: 'courses/term/' + this.semester, 135 | actions, 136 | methods: 'GET', 137 | type: 'schedule', 138 | success: (result, messages, errors) => { 139 | this.originalData.messages = messages 140 | app.globalData.schedule = result 141 | if (result.list.length > 0) { 142 | if (actions) { 143 | wx.setStorageSync('schedule', result) 144 | } 145 | this.initSchedule(result) 146 | } 147 | if (!errors || errors.length === 0) { 148 | if (actions) { 149 | wx.showToast({ 150 | title: '更新成功', 151 | icon: 'success', 152 | duration: 500, 153 | }) 154 | } else { 155 | wx.hideToast() 156 | } 157 | } else { 158 | wx.showToast({ 159 | title: errors.map((elem) => { 160 | return elem.message 161 | }).join(' '), 162 | icon: 'none', 163 | duration: 3000, 164 | }) 165 | } 166 | }, 167 | complete: () => { 168 | wx.stopPullDownRefresh() 169 | this.isShared = shared 170 | }, 171 | }) 172 | }, 173 | 174 | initSchedule: function(res: ISchedule) { 175 | this.originalData.semester = res.year + '/' + res.term 176 | 177 | if (res.semesters) { 178 | this.semesters = res.semesters 179 | if (this.originalData.semester > res.semesters[res.semesters.length - 1]) { 180 | this.originalData.semester = '最新学期' 181 | } 182 | this.originalData.semesters = JSON.parse(JSON.stringify(res.semesters)) 183 | this.originalData.semesters.unshift('最新学期') 184 | } 185 | 186 | this.setData(JSON.parse(JSON.stringify(this.originalData))) 187 | const classList = res.list 188 | const week = res.week 189 | const term = res.term 190 | const year = res.year 191 | // 初始化color列表背景颜色 192 | const color = this.data.color 193 | for (const key in color) { 194 | for (const index in color[key]) { 195 | // @ts-ignore 196 | // tslint:disable-next-line:triple-equals 197 | if (index != 0) { 198 | color[key][index] = this.data.backgroundColor 199 | } 200 | } 201 | } 202 | this.setData({color: color}) 203 | 204 | const classes = this.data.classes 205 | const marker = this.data.marker 206 | const dateTable = {'周一': 1, '周二': 2, '周三': 3, '周四': 4, '周五': 5, '周六': 6, '周日': 7} 207 | // 课程解析并填入 208 | this.data.noTime = [] 209 | let credits = 0 210 | for (const i in classList) { 211 | if (classList[i].type !== '第二课堂' && classList[i].belong !== '第二课堂') { 212 | credits += parseFloat(classList[i].credit) 213 | } 214 | if (classList[i].time === '') { 215 | this.data.noTime.push(classList[i]) 216 | continue 217 | } 218 | 219 | const timeList = classList[i].time.split(';') 220 | 221 | // 解决‘9,10,11,12节‘这种情况 222 | // 只能解决四节连续的问题,‘7,8,9,10,11,12节‘不支持 223 | for (const j in timeList) { 224 | const time = timeList[j].slice(3).split('节')[0].split(',') 225 | if (time.length > 2) { 226 | const time0 = time.slice(0, 2) 227 | const time1 = time.slice(2) 228 | timeList[j] = timeList[j].slice(0, 3) + time0 + '节' + timeList[j].split('节')[1] 229 | timeList.push(timeList[j].slice(0, 3) + time1 + '节' + timeList[j].split('节')[1]) 230 | if (classList[i].room && classList[i].room !== ' ') { 231 | classList[i].room = classList[i].room + ';' + classList[i].room 232 | } 233 | } 234 | } 235 | // console.log(timeList); 236 | 237 | for (const j in timeList) { 238 | const date = dateTable[timeList[j].slice(0, 2)] 239 | let time = timeList[j].slice(3).split('{')[0] 240 | let appendDate = '' 241 | 242 | switch (time.split(',').length) { 243 | case 1: 244 | const timeNum = parseInt(time.split('节')[0], 10) 245 | appendDate = '(仅第' + timeNum + '节)' 246 | let set 247 | if (timeNum % 2 === 1) { 248 | time = timeNum + ',' + (timeNum + 1) + '节' 249 | set = 1 250 | } else { 251 | time = timeNum - 1 + ',' + timeNum + '节' 252 | set = 2 253 | } 254 | if (typeof marker[time] !== 'undefined' && typeof marker[time][date] !== 'undefined') { 255 | marker[time][date] = set 256 | } else { 257 | continue 258 | } 259 | break 260 | case 2: 261 | break 262 | default: 263 | continue 264 | } 265 | 266 | if (typeof color === 'undefined' || typeof color[time] === 'undefined' || typeof color[time][date] === 'undefined') { 267 | console.log({color, time, date}) 268 | continue 269 | } 270 | // @ts-ignore 271 | let newColor = this.data.optionalColorList[Math.abs(classList[i].instructor.hashCode()) % this.data.optionalColorList.length] 272 | if (this.theme === 'dark') { 273 | newColor = '#' + newColor.substring(1) + '80' 274 | } 275 | if (color[time][date] === 'transparent') { 276 | color[time][date] = newColor 277 | } 278 | // console.log(classList[i].name+" "+this.data.optionalColorList[i]+" "+i) 279 | const weekRange = timeList[j].split('{')[1].split('}')[0] 280 | let prev = '' 281 | if (typeof classes[time][date].text !== 'undefined') { 282 | prev = classes[time][date].text + '\n\n' 283 | } else { 284 | classes[time][date] = {} 285 | } 286 | if (classList[i].room && classList[i].room !== ' ' && classList[i].room.split(';')[j]) { 287 | classes[time][date].text = prev + classList[i].room.split(';')[j] + '\n' + classList[i].name + '-' + classList[i].instructor + '\n' + weekRange + appendDate 288 | } else { 289 | classes[time][date].text = prev + classList[i].name + '-' + classList[i].instructor + '\n' + weekRange + appendDate 290 | } 291 | const weekNoStart = weekRange.indexOf('第') 292 | const weekNoEnd = weekRange.indexOf('周') 293 | if (weekNoStart !== -1 && weekNoEnd !== -1) { 294 | const weekNo = weekRange.substr(weekNoStart + 1, weekNoStart + weekNoEnd - 1).split('-') 295 | if (weekNo.length === 2) { 296 | // @ts-ignore 297 | let currentWeek = new Date().getWeek() 298 | if (typeof week[year] !== 'undefined' && typeof week[year][term] === 'number') { 299 | currentWeek += 1 - week[year][term] 300 | if (currentWeek <= 0 || currentWeek > 24) { 301 | currentWeek = 1 302 | } 303 | const test = (currentWeek > weekNo[1] || currentWeek < weekNo[0]) 304 | if (typeof classes[time][date].opacity === 'undefined' && test) { 305 | classes[time][date].opacity = 'true' 306 | } else { 307 | if (classes[time][date].opacity === 'true') { // 新 append 到最后额课程是目前在上的课程 308 | classes[time][date].text = classes[time][date].text.split('\n\n').reverse().join('\n\n') 309 | color[time][date] = newColor 310 | } 311 | if (test) { 312 | continue 313 | } 314 | if (classes[time][date].opacity !== 'false') { 315 | if (weekRange.indexOf('单周') !== -1) { 316 | if (currentWeek % 2 !== 1) { 317 | classes[time][date].opacity = 'true' 318 | } else { 319 | classes[time][date].opacity = 'false' 320 | } 321 | } else if (weekRange.indexOf('双周') !== -1) { 322 | if (currentWeek % 2 !== 0) { 323 | classes[time][date].opacity = 'true' 324 | } else { 325 | classes[time][date].opacity = 'false' 326 | } 327 | } else { 328 | classes[time][date].opacity = 'false' 329 | } 330 | } 331 | } 332 | } 333 | } 334 | } 335 | } 336 | } 337 | 338 | if ( 339 | typeof res.major === 'string' && res.major && res.name && res.shared && res.id 340 | ) { 341 | this.share_title = res.name + '的' + 342 | res.year.substring(2, 4) + '-' + res.year.substring(7, 9) + '第' + res.term + '学期有' + 343 | res.list.length + '门课, 共' + credits + '学分, 快来围观' 344 | if (res.shared) { 345 | this.shared = { 346 | title: this.share_title, 347 | query: 'shared=' + res.shared + '&semester=' + res.year + '/' + res.term + '&id=' + res.id, 348 | } 349 | } 350 | if (this.data.comment) { 351 | this.data.comment = '这是' + res.major + ' – ' + res.name + '的课表\n' + this.data.comment 352 | if (app.globalData.sceneId !== 1154) { 353 | wx.setNavigationBarTitle({ 354 | title: res.name + '的课表', 355 | }) 356 | } 357 | } else { 358 | if (app.globalData.sceneId !== 1154) { 359 | wx.setNavigationBarTitle({ 360 | title: '我的课表', 361 | }) 362 | } 363 | } 364 | } else { 365 | this.share_title = '快查查' + res.year.substring(2, 4) + '-' + res.year.substring(7, 9) + '第' + res.term + '学期的课表!' 366 | } 367 | 368 | let title 369 | if (res.list.length > 0) { 370 | const {week, year, term} = res 371 | // @ts-ignore 372 | let currentWeek = new Date().getWeek() 373 | if (typeof week[year] !== 'undefined' && typeof week[year][term] === 'number') { 374 | currentWeek += 1 - week[year][term] 375 | if (this.data.noTime.length >= classList.length) { 376 | title = '您的' + res.year.substring(2, 4) + '-' + res.year.substring(7, 9) + '第' + res.term + '学期没有其他课程' 377 | } else { 378 | title = '正在显示' + res.year.substring(2, 4) + '-' + res.year.substring(7, 9) + '第' + res.term + '学期第' + (currentWeek > 0 ? currentWeek : 1) + '周课程' 379 | } 380 | if (currentWeek < 0) { 381 | title += ';距离开学还有' + (1 - currentWeek) + '周' 382 | } else if (currentWeek === 0) { 383 | title += ';下周就开学了,做好准备吧!' 384 | } else if (currentWeek > 24) { 385 | title = '正在显示' + res.year.substring(2, 4) + '-' + res.year.substring(7, 9) + '第' + res.term + '学期第1周课程' 386 | } 387 | } else { 388 | title = '正在显示' + res.year.substring(2, 4) + '-' + res.year.substring(7, 9) + '第' + res.term + '学期课程' 389 | } 390 | } else { 391 | title = res.year + '学年第' + res.term + '学期的课表为空' 392 | } 393 | this.setData({classes, color, height: marker, noTime: this.data.noTime, allClasses: classList.length, message: title, comment: this.data.comment}) 394 | }, 395 | 396 | onShow: function() { 397 | if (typeof this.getTabBar === 'function' && this.getTabBar()) { 398 | wx.$initTab(this.getTabBar()) 399 | this.getTabBar().setData({ 400 | selected: 1, 401 | }) 402 | } 403 | // @ts-ignore 404 | const theme = wx.getSystemInfoSync().theme 405 | if (typeof theme === 'string' && theme !== this.theme) { 406 | this.theme = theme 407 | this.initSchedule(app.globalData.schedule) 408 | } else if (typeof app.globalData.schedule !== 'undefined') { 409 | this.initSchedule(app.globalData.schedule) 410 | } 411 | }, 412 | 413 | onPullDownRefresh: function() { 414 | if (app.globalData.sceneId === 1154) { 415 | wx.showToast({title: '请点击页面底部的 “前往小程序” 查看自己的课表', icon: 'none', duration: 3000}) 416 | wx.stopPullDownRefresh() 417 | return 418 | } 419 | if (!app.globalData.authorization || !app.globalData.student_id || !app.globalData.open) { 420 | wx.stopPullDownRefresh() 421 | } 422 | if (this.isShared) { 423 | this.originalData.comment = '' 424 | this.isShared = false 425 | if (app.globalData.scoreData && app.globalData.scoreData.result && typeof app.globalData.scoreData.result.share_score === 'boolean') { 426 | this.fetch(!app.globalData.scoreData.result.share_score, false) 427 | } else { 428 | this.fetch() 429 | } 430 | return 431 | } else if (this.time + 10 > +new Date() / 1000) { 432 | wx.stopPullDownRefresh() 433 | wx.showToast({ 434 | title: '手速太快了,等一会儿再刷新试试吧', 435 | icon: 'none', 436 | duration: 3000, 437 | }) 438 | return 439 | } 440 | this.fetch() 441 | }, 442 | 443 | onShareAppMessage: function() { 444 | if (this.shared.query) { 445 | return { 446 | title: this.share_title, 447 | path: '/pages/index/index?' + this.shared.query, 448 | } 449 | } else { 450 | return { 451 | title: this.share_title, 452 | path: '/pages/login/login', 453 | } 454 | } 455 | }, 456 | 457 | onShareTimeline: function() { 458 | return this.shared 459 | }, 460 | 461 | bindSemesterChange: function(e) { 462 | console.log(e) 463 | const value = parseInt(e.detail.value, 10) - 1 464 | if (value < 0) { 465 | this.semester = '0/0' 466 | this.setData({semester: '最新学期'}) 467 | this.fetch() 468 | } else if (this.semesters[value]) { 469 | this.semester = this.semesters[value] 470 | this.setData({semester: this.semester}) 471 | if (app.globalData.scoreData && app.globalData.scoreData.result && app.globalData.scoreData.result.share_score === true) { 472 | this.fetch(false, false) 473 | } else { 474 | this.fetch() 475 | } 476 | } 477 | }, 478 | }) 479 | 480 | export {} 481 | --------------------------------------------------------------------------------