├── .gitignore ├── LICENSE ├── README.md ├── app.js ├── app.json ├── app.wxss ├── config.js ├── image ├── 1.png ├── 2.png └── arrowright.png ├── mock ├── achievements.html ├── data.js └── parser.js ├── package.json ├── pages ├── achievement │ ├── achievement.js │ ├── achievement.json │ ├── achievement.wxml │ └── achievement.wxss ├── elective │ ├── elective.js │ └── elective.wxml ├── index │ ├── index.js │ ├── index.wxml │ └── index.wxss ├── introduce │ ├── introduce.js │ └── introduce.wxml └── panel │ ├── panel.js │ ├── panel.wxml │ └── panel.wxss └── utils ├── parser.js ├── simulator.js └── util.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 zonghua 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 教务系统 微信小程序 2 | 3 | ## 说明 4 | 5 | 正方教务系统的(微信)小程序,~~成绩数据爬虫~~ 6 | 7 | **准备用服务端做数据抓取,客户端仅作数据显示** 8 | 9 | ~~请见下方局限~~ 10 | 11 | **此项目使用 Applet 名称仅为借古讽今只之用** 12 | 13 | ## 演示 14 | 15 | 1. 从 GitHub 克隆项目 16 | 17 | 2. 启动微信 Web 开发着工具,添加项目 18 | 19 | 3. 选择无 AppID ,填写任意名称,找到项目目录添加 20 | 21 | 4. 点击“查询” 22 | 23 | ![教务系统 微信小程序](https://raw.githubusercontent.com/zh-h/student-information-system-wechat-applet/master/image/1.png) 24 | 25 | ![教务系统 微信小程序](https://raw.githubusercontent.com/zh-h/student-information-system-wechat-applet/master/image/2.png) 26 | 27 | 28 | ## ~~局限~~ ## 29 | 30 | **由于 Javascript 跨域限制暂时无法直接通过小程序去抓取目标网站的 Cookie** 31 | 32 | xhr 33 | ``` 34 | Date: Fri, 30 Sep 2016 12:46:33 GMT 35 | Server: Microsoft-IIS/6.0 36 | X-AspNet-Version: 1.1.4322 37 | X-Powered-By: ASP.NET 38 | MicrosoftOfficeWebServer: 5.0_Pub 39 | Content-Type: image/Gif; charset=gb2312 40 | Access-Control-Allow-Origin: * 41 | Cache-Control: private 42 | Access-Control-Allow-Headers: X-Requested-With, Content-Type 43 | Content-Length: 2245 44 | 45 | ``` 46 | chrome 47 | ``` 48 | HTTP/1.1 200 OK 49 | Date: Fri, 30 Sep 2016 12:46:33 GMT 50 | Server: Microsoft-IIS/6.0 51 | MicrosoftOfficeWebServer: 5.0_Pub 52 | X-Powered-By: ASP.NET 53 | X-AspNet-Version: 1.1.4322 54 | Set-Cookie: ASP.NET_SessionId=hw4tpo55f4005ojii14d2e3r; path=/ 55 | Cache-Control: private 56 | Content-Type: image/Gif; charset=gb2312 57 | Content-Length: 2245 58 | ``` 59 | 60 | **小程序暂时使用直接 GET 获取到的 HTML 内容进行解析然后填充视图作为演示** 61 | 62 | ``` 63 | var achievementUrl = isDebug ? mockUrl + 'achievements.html' : sisUrl + '/xscj.aspx?xh=' 64 | ``` 65 | 66 | **如果能够避免跨域访问的限制,小程序可以不依赖服务端完成抓取的任务** 67 | 68 | ``` 69 | // XMLHttpRequest 完备 70 | var test = function (url, callback) { 71 | var xhr = new XMLHttpRequest() 72 | xhr.responseType = 'blob' 73 | xhr.onload = function () { 74 | var reader = new FileReader() 75 | var headers = xhr.getAllResponseHeaders() 76 | reader.onloadend = function () { 77 | callback(reader.result, headers) 78 | } 79 | reader.readAsDataURL(xhr.response) 80 | } 81 | xhr.open('GET', url) 82 | xhr.send() 83 | } 84 | ``` -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | // app.js 2 | App({ 3 | onLaunch: function () { 4 | // 调用API从本地缓存中获取数据 5 | // var logs = wx.getStorageSync( 'logs' ) || [] 6 | // logs.unshift( Date.now() ) 7 | // wx.setStorageSync( 'logs', logs ) 8 | }, 9 | getUserInfo: function (cb) { 10 | var that = this 11 | if (this.globalData.userInfo) { 12 | typeof cb == 'function' && cb(this.globalData.userInfo) 13 | } else { 14 | // 调用登录接口 15 | wx.login({ 16 | success: function () { 17 | wx.getUserInfo({ 18 | success: function (res) { 19 | that.globalData.userInfo = res.userInfo 20 | typeof cb == 'function' && cb(that.globalData.userInfo) 21 | } 22 | }) 23 | } 24 | }) 25 | } 26 | }, 27 | globalData: { 28 | userInfo: null 29 | } 30 | }) 31 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/index/index", 4 | "pages/achievement/achievement", 5 | "pages/introduce/introduce", 6 | "pages/elective/elective", 7 | "pages/panel/panel" 8 | ], 9 | "window": { 10 | "backgroundTextStyle": "light", 11 | "navigationBarBackgroundColor": "#fff", 12 | "navigationBarTitleText": "教务系统小程序", 13 | "navigationBarTextStyle": "black" 14 | }, 15 | "debug":true 16 | } -------------------------------------------------------------------------------- /app.wxss: -------------------------------------------------------------------------------- 1 | page { 2 | background-color: #fbf9fe; 3 | height: 100%; 4 | } 5 | .container { 6 | display: flex; 7 | flex-direction: column; 8 | min-height: 100%; 9 | justify-content: space-between; 10 | } 11 | .page-header { 12 | display: flex; 13 | font-size: 32rpx; 14 | color: #aaa; 15 | margin-top: 50rpx; 16 | flex-direction: column; 17 | align-items: center; 18 | } 19 | .page-header-text { 20 | padding: 20rpx 40rpx; 21 | } 22 | .page-header-line { 23 | width: 150rpx; 24 | height: 1px; 25 | border-bottom: 1px solid #ccc; 26 | } 27 | 28 | .page-body { 29 | width: 100%; 30 | display: flex; 31 | flex-direction: column; 32 | align-items: center; 33 | flex-grow: 1; 34 | overflow-x: hidden; 35 | } 36 | .page-body-wrapper { 37 | margin-top: 100rpx; 38 | display: flex; 39 | flex-direction: column; 40 | align-items: center; 41 | width: 100%; 42 | } 43 | .page-body-wrapper form { 44 | width: 100%; 45 | } 46 | .page-body-wording { 47 | text-align: center; 48 | padding: 200rpx 100rpx; 49 | } 50 | .page-body-info { 51 | display: flex; 52 | flex-direction: column; 53 | align-items: center; 54 | background-color: #fff; 55 | margin-bottom: 50rpx; 56 | width: 100%; 57 | padding: 50rpx 0 150rpx 0; 58 | } 59 | .page-body-title { 60 | margin-bottom: 100rpx; 61 | font-size: 32rpx; 62 | } 63 | .page-body-text { 64 | font-size: 30rpx; 65 | line-height: 26px; 66 | color: #ccc; 67 | } 68 | .page-body-text-small { 69 | font-size: 24rpx; 70 | color: #000; 71 | margin-bottom: 100rpx; 72 | } 73 | .page-body-form { 74 | width: 100%; 75 | background-color: #fff; 76 | display: flex; 77 | flex-direction: column; 78 | width: 100%; 79 | border: 1px solid #eee; 80 | } 81 | .page-body-form-item { 82 | display: flex; 83 | align-items: center; 84 | margin-left: 30rpx; 85 | border-bottom: 1px solid #eee; 86 | height: 88rpx; 87 | font-size: 34rpx; 88 | } 89 | .page-body-form-key { 90 | width: 180rpx; 91 | color: #000; 92 | } 93 | .page-body-form-value { 94 | flex-grow: 1; 95 | } 96 | .page-body-form-value .input-placeholder { 97 | color: #b2b2b2; 98 | } 99 | 100 | .page-body-form-picker { 101 | display: flex; 102 | justify-content: space-between; 103 | height: 100rpx; 104 | align-items: center; 105 | font-size: 36rpx; 106 | margin-left: 20rpx; 107 | padding-right: 20rpx; 108 | border-bottom: 1px solid #eee; 109 | } 110 | .page-body-form-picker-value { 111 | color: #ccc; 112 | } 113 | 114 | .page-body-buttons { 115 | width: 100%; 116 | } 117 | .page-body-button { 118 | margin: 25rpx; 119 | } 120 | .page-body-button image { 121 | width: 150rpx; 122 | height: 150rpx; 123 | } 124 | .page-footer { 125 | text-align: center; 126 | color: #1aad19; 127 | font-size: 28rpx; 128 | padding: 6px; 129 | bottom: 0px; 130 | position: fixed; 131 | } 132 | 133 | .green{ 134 | color: #09BB07; 135 | } 136 | .red{ 137 | color: #F76260; 138 | } 139 | .blue{ 140 | color: #10AEFF; 141 | } 142 | .yellow{ 143 | color: #FFBE00; 144 | } 145 | .gray{ 146 | color: #C9C9C9; 147 | } 148 | 149 | .strong{ 150 | font-weight: bold; 151 | } 152 | 153 | .bc_green{ 154 | background-color: #09BB07; 155 | } 156 | .bc_red{ 157 | background-color: #F76260; 158 | } 159 | .bc_blue{ 160 | background-color: #10AEFF; 161 | } 162 | .bc_yellow{ 163 | background-color: #FFBE00; 164 | } 165 | .bc_gray{ 166 | background-color: #C9C9C9; 167 | } 168 | 169 | .tc{ 170 | text-align: center; 171 | } 172 | 173 | .page input,checkbox{ 174 | padding: 20rpx 30rpx; 175 | background-color: #fff; 176 | } 177 | checkbox, radio{ 178 | margin-right: 10px; 179 | } 180 | 181 | .btn-area{ 182 | padding: 0 30px; 183 | } 184 | .btn-area button{ 185 | margin-top: 20rpx; 186 | margin-bottom: 20rpx; 187 | } 188 | 189 | .page { 190 | min-height: 100%; 191 | flex: 1; 192 | background-color: #FBF9FE; 193 | font-size: 32rpx; 194 | font-family: -apple-system-font,Helvetica Neue,Helvetica,sans-serif; 195 | overflow: hidden; 196 | } 197 | .page__hd{ 198 | padding: 50rpx 50rpx 50rpx 50rpx; 199 | text-align: center; 200 | } 201 | .page__title{ 202 | display: inline-block; 203 | padding: 20rpx 40rpx; 204 | font-size: 32rpx; 205 | color: #AAAAAA; 206 | border-bottom: 1px solid #CCCCCC; 207 | } 208 | .page__info{ 209 | display: inline-block; 210 | font-size: 38rpx; 211 | color: #AAAAAA; 212 | } 213 | .page__desc{ 214 | display: none; 215 | margin-top: 20rpx; 216 | font-size: 26rpx; 217 | color: #BBBBBB; 218 | } 219 | 220 | .section{ 221 | margin-bottom: 80rpx; 222 | } 223 | .section_gap{ 224 | padding: 0 30rpx; 225 | } 226 | .section__title{ 227 | margin-bottom: 16rpx; 228 | padding-left: 30rpx; 229 | padding-right: 30rpx; 230 | } 231 | .section_gap .section__title{ 232 | padding-left: 0; 233 | padding-right: 0; 234 | } 235 | 236 | .shading{ 237 | background-color: #eee; 238 | background-image: -moz-linear-gradient(45deg,#fff 25%, transparent 25%, transparent 50%,#fff 50%,#fff 75%, transparent 75%, transparent); 239 | background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(25%,rgba(255,255,255,0.2)),color-stop(25%,transparent),color-stop(50%,transparent),color-stop(50%,rgba(255,255,255,0.2)),color-stop(75%,rgba(255,255,255,0.2)),color-stop(75%,transparent)); 240 | background-size: 16px 16px; 241 | 242 | } 243 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | var isDebug = true 2 | var sisUrl = 'http://10.50.17.10:80' 3 | var mockUrl = 'https://applehater.cn/mock/' 4 | 5 | // 只能用模拟得数据做演示 6 | var achievementUrl = isDebug ? mockUrl + 'achievements.html' : sisUrl + '/xscj.aspx?xh=' 7 | 8 | module.exports = { 9 | loginUrl: sisUrl + '/default3.aspx', 10 | codeUrl: sisUrl + '/CheckCode.aspx', 11 | infoUrl: sisUrl + '/xsxx.aspx?xh=', 12 | achievementUrl: achievementUrl, 13 | electiveUrl: sisUrl + '/ryxk.aspx?xh=', 14 | mockUrl: mockUrl 15 | } 16 | -------------------------------------------------------------------------------- /image/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh-h/student-information-system-wechat-applet/026561871eaffe181619e179b86e2f7aba24b652/image/1.png -------------------------------------------------------------------------------- /image/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh-h/student-information-system-wechat-applet/026561871eaffe181619e179b86e2f7aba24b652/image/2.png -------------------------------------------------------------------------------- /image/arrowright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh-h/student-information-system-wechat-applet/026561871eaffe181619e179b86e2f7aba24b652/image/arrowright.png -------------------------------------------------------------------------------- /mock/achievements.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 学生成绩查询 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 36 | 37 | 38 | 520 | 521 | 522 |
22 | 23 | 24 | 25 | 28 | 29 | 32 | 33 | 34 |
26 |
27 |
当前位置:学生—>学生成绩查询 30 |
31 |
35 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 513 | 514 | 515 | 516 | 517 | 518 |
46 | 47 | 48 | 49 | 509 | 510 | 511 |
50 | 51 | 52 | 53 | 110 | 111 | 112 | 358 | 359 | 360 | 505 | 506 | 507 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | 103 | 104 | 105 | 106 | 107 | 108 |
61 | 62 | 63 | 64 | 82 | 83 | 84 | 99 | 100 | 101 |
学年: 65 |   学期: 73 |    79 | 80 | 81 |
85 | 课程类型: 86 | 97 |             98 |
102 |
109 |
113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 |
学年学期课程名称课程类型任课教师考核方式总评成绩补考成绩重修成绩重修成绩2重修成绩3绩点应得学分
2012-20131C 从入门到精通必修课吴讲得集中59    010
2012-20132Java 从入门到精通必修课吴讲得集中59    010
2013-20141C++ 从入门到精通必修课吴讲得集中59    010
2013-20142Python 从入门到精通必修课吴讲得集中59    010
2014-20151C# 从入门到精通必修课吴讲得集中59    010
2014-20152PHP 从入门到精通必修课吴讲得集中59    010
2015-20161Javascript 从入门到精通必修课吴讲得集中59    010
2015-20162Visual Basic 入门经典必修课吴讲得集中59    010
2015-20162Visual Basic 语言应用实践必修课吴讲得集中59    010
2015-20162Visual Basic 语言高阶编程必修课吴讲得集中59    010
2015-20162Visual Basic 语言的科学与艺术必修课吴讲得集中59    010
2015-20162编程之美必修课吴讲得集中59    010
2015-20162编程之道必修课吴讲得集中59    010
2015-20162编程之禅必修课吴讲得集中59    010
2015-20162颈椎病康复指南必修课吴讲得集中59    010
357 |
361 | 362 | 363 | 364 | 365 | 366 | 367 | 390 | 391 | 392 | 393 | 394 | 395 | 495 | 496 | 497 | 498 | 501 | 502 | 503 |
历年未通过课程:
368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 |
学年学期课程名称课程类型任课教师考核方式总评成绩补考成绩重修成绩重修成绩2重修成绩3绩点应得学分
388 |
389 |
396 | 398 | 399 | 400 | 403 | 404 | 405 | 407 | 408 | 410 | 411 | 412 | 413 | 415 | 417 | 419 | 420 | 421 | 422 | 424 | 426 | 428 | 429 | 430 | 431 | 433 | 435 | 437 | 438 | 439 | 440 | 442 | 444 | 446 | 447 | 448 | 449 | 451 | 453 | 455 | 456 | 457 | 458 | 460 | 462 | 464 | 465 | 466 | 467 | 471 | 472 | 476 | 477 | 478 | 479 | 480 | 483 | 484 | 487 | 488 | 489 | 490 | 491 | 492 | 493 |
401 | 对应实得总学分为:0分。 402 |
406 | 应修必修课学分 409 | 实得必修课学分0分
414 | 应修限选课学分 416 | 分 418 | 实得限选课学分0分
423 | 应修专业自选课学分 425 | 分 427 | 实得专业自选课学分0分
432 | 应修公共选修课学分 434 | 分 436 | 实得公共选修课学分0分
441 | 应修跨学院专业自选课学分 443 | 分 445 | 实得跨学院专业自选课学分0分
450 | 应修本学院专业自选课学分 452 | 分 454 | 实得本学院专业自选课学分0分
459 | 应修特色课学分 461 | 分 463 | 实得特色课学分0分
468 | 469 | 应修总学分小计 470 | 0分 473 | 474 | 实得总学分小计 475 | 0分
481 | 平均学分绩点 482 | 0 485 | 总学分绩点 486 | 0
说明:学分完成情况统计(仅作参考,具体数值以教学计划文本为准)
494 |
499 |   500 |
504 |
508 |
512 |
519 |
523 |
524 | 525 | 526 | 527 | 528 | -------------------------------------------------------------------------------- /mock/data.js: -------------------------------------------------------------------------------- 1 | var data = [ [ '学年', 2 | '学期', 3 | '课程名称', 4 | '课程类型', 5 | '任课教师', 6 | '考核方式', 7 | '总评成绩', 8 | '补考成绩', 9 | '重修成绩', 10 | '重修成绩2', 11 | '重修成绩3', 12 | '绩点', 13 | '应得学分' ], 14 | [ '2012-2013', 15 | '1', 16 | 'C 从入门到精通', 17 | '必修课', 18 | '吴讲得', 19 | '集中', 20 | '59', 21 | '', 22 | '', 23 | '', 24 | '', 25 | '0', 26 | '10' ], 27 | [ '2012-2013', 28 | '2', 29 | 'Java 从入门到精通', 30 | '必修课', 31 | '吴讲得', 32 | '集中', 33 | '59', 34 | '', 35 | '', 36 | '', 37 | '', 38 | '0', 39 | '10' ], 40 | [ '2013-2014', 41 | '1', 42 | 'C++ 从入门到精通', 43 | '必修课', 44 | '吴讲得', 45 | '集中', 46 | '59', 47 | '', 48 | '', 49 | '', 50 | '', 51 | '0', 52 | '10' ], 53 | [ '2013-2014', 54 | '2', 55 | 'Python 从入门到精通', 56 | '必修课', 57 | '吴讲得', 58 | '集中', 59 | '59', 60 | '', 61 | '', 62 | '', 63 | '', 64 | '0', 65 | '10' ], 66 | [ '2014-2015', 67 | '1', 68 | 'C# 从入门到精通', 69 | '必修课', 70 | '吴讲得', 71 | '集中', 72 | '59', 73 | '', 74 | '', 75 | '', 76 | '', 77 | '0', 78 | '10' ], 79 | [ '2014-2015', 80 | '2', 81 | 'PHP 从入门到精通', 82 | '必修课', 83 | '吴讲得', 84 | '集中', 85 | '59', 86 | '', 87 | '', 88 | '', 89 | '', 90 | '0', 91 | '10' ], 92 | [ '2015-2016', 93 | '1', 94 | 'Javascript 从入门到精通', 95 | '必修课', 96 | '吴讲得', 97 | '集中', 98 | '59', 99 | '', 100 | '', 101 | '', 102 | '', 103 | '0', 104 | '10' ], 105 | [ '2015-2016', 106 | '2', 107 | 'Visual Basic 从入门到精通', 108 | '必修课', 109 | '吴讲得', 110 | '集中', 111 | '59', 112 | '', 113 | '', 114 | '', 115 | '', 116 | '0', 117 | '10' ] ] 118 | module.exports = data 119 | -------------------------------------------------------------------------------- /mock/parser.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh-h/student-information-system-wechat-applet/026561871eaffe181619e179b86e2f7aba24b652/mock/parser.js -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "student-information-system-wechat-applet", 3 | "version": "1.0.0", 4 | "main": "app.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "zonghua", 9 | "license": "MIT", 10 | "description": "", 11 | "dependencies": { 12 | "iconv-lite": "^0.4.13" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /pages/achievement/achievement.js: -------------------------------------------------------------------------------- 1 | var mockData = require('../../mock/data.js') 2 | 3 | var data = {} 4 | 5 | Page({ 6 | data: { 7 | username: 0, 8 | list: [ 9 | { 10 | id: 1, 11 | years: '2016-2017', 12 | term: 1, 13 | open: false, 14 | table: [] 15 | } 16 | ] 17 | }, 18 | 19 | widgetsToggle: function (e) { 20 | var id = e.currentTarget.id 21 | var list = this.data.list 22 | for (var i = 0, len = list.length; i < len; ++i) { 23 | if (list[i].id == id) { 24 | list[i].open = !list[i].open 25 | } else { 26 | // list[i].open = false 27 | } 28 | } 29 | this.setData({ 30 | list: list 31 | }) 32 | }, 33 | 34 | setCurrentTermShow: function () { 35 | var today = new Date(); // 获得当前日期 36 | var year = today.getFullYear(); // 获得年份 37 | var month = today.getMonth() + 1; // 此方法获得的月份是从0---11,所以要加1才是当前月份 38 | var day = today.getDate(); // 获得当前日期 39 | var list = this.data.list 40 | for (var i = 0, len = list.length; i < len; ++i) { 41 | if (list[i].years.indexOf(year) != -1) { 42 | if (month >= 6 && list[i].term == 2) { 43 | list[i].open = true // 上半年看第一学期,下半年第二学期成绩打开 44 | }else if (month < 6 && list[i].term == 1) { 45 | list[i].open = true 46 | } 47 | } 48 | } 49 | this.setData({ 50 | list: list 51 | }) 52 | }, 53 | 54 | fillData: function (options) { 55 | data = JSON.parse(options['data']) 56 | data.shift() // 去掉表头 57 | var list = [] 58 | var tempDiffVal = '' // 按照年份和学期分组标记 59 | var tempItem = {} 60 | for (var i = 0;i < data.length;i++) { 61 | var dataRow = data[i] 62 | var years = dataRow[0] 63 | var term = dataRow[1] 64 | diffVal = years + term 65 | if (diffVal != tempDiffVal) { // 如果下一行变了就创建新的栏目 66 | var item = { 67 | id: list.length + 1, 68 | years: years, 69 | term: term, 70 | open: false, 71 | table: [] 72 | } 73 | tempItem = item 74 | list.push(item) // 把新的栏目放到列表 75 | tempDiffVal = diffVal 76 | } 77 | 78 | var tableRow = [] 79 | tableRow[0] = dataRow[2] // 课程 80 | tableRow[1] = dataRow[6] // 成绩 81 | tableRow[2] = dataRow[11] // 绩点 82 | tempItem.table.push(tableRow) 83 | } 84 | this.setData({ 85 | list: list, 86 | username: options.username 87 | }) 88 | }, 89 | 90 | onLoad: function (options) { 91 | this.fillData(options) 92 | this.setCurrentTermShow() 93 | }, 94 | 95 | onShow:function(){ 96 | wx.hideNavigationBarLoading() 97 | }, 98 | 99 | // https://mp.weixin.qq.com/debug/wxadoc/dev/framework/app-service/page.html?t=1475052056377#生命周期函数 100 | onReady: function () { 101 | var page = this 102 | wx.setNavigationBarTitle({ 103 | title: '学号 ' + page.data.username + ' 的成绩' 104 | } 105 | ) 106 | } 107 | }) 108 | -------------------------------------------------------------------------------- /pages/achievement/achievement.json: -------------------------------------------------------------------------------- 1 | { 2 | "backgroundTextStyle": "light", 3 | "navigationBarBackgroundColor": "#fff", 4 | "navigationBarTitleText": "成绩", 5 | "navigationBarTextStyle": "black" 6 | } -------------------------------------------------------------------------------- /pages/achievement/achievement.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 学号 {{username}} 的成绩 4 | 5 | 6 | 7 | 8 | 9 | 10 | {{item.years}} 学年 第 {{item.term}} 学期 11 | 12 | 13 | 14 | 15 | 课程 16 | 成绩 17 | 绩点 18 | 19 | 20 | 21 | {{row[0]}} 22 | {{row[1]}} 23 | {{row[2]}} 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /pages/achievement/achievement.wxss: -------------------------------------------------------------------------------- 1 | .widgets__item{ 2 | margin-top: 20rpx; 3 | margin-bottom: 20rpx; 4 | background-color: #FFFFFF; 5 | overflow: hidden; 6 | cursor: pointer; 7 | } 8 | .widgets__info{ 9 | display: flex; 10 | padding: 40rpx; 11 | align-items: center; 12 | flex-direction: row; 13 | } 14 | .widgets__info_show{ 15 | color: #FFFFFF; 16 | background-color: #1AAD19; 17 | } 18 | .widgets__info_show .widgets__info-img{ 19 | transform: rotate(-90deg); 20 | } 21 | .widgets__info-name{ 22 | flex: 1; 23 | } 24 | .widgets__info-img{ 25 | width: 32rpx; 26 | height: 32rpx; 27 | transition: transform .4s; 28 | transform: rotate(90deg); 29 | } 30 | 31 | .widgets__list{ 32 | display: none; 33 | } 34 | .widgets__list_show{ 35 | display: block; 36 | } 37 | 38 | .widgets__table{ 39 | display: none; 40 | } 41 | .widgets__table_show{ 42 | display: block; 43 | border-bottom: 1px solid #1AAD19 44 | } 45 | .widget{ 46 | position: relative; 47 | padding-top: 26rpx; 48 | padding-bottom: 26rpx; 49 | padding-left: 40rpx; 50 | padding-right: 40rpx; 51 | } 52 | 53 | .table__row{ 54 | display: list-item; 55 | padding: 12px; 56 | } 57 | 58 | .table_row_header{ 59 | font-weight: bold; 60 | background-color: #eee; 61 | background-image: -moz-linear-gradient(45deg,#fff 25%, transparent 25%, transparent 50%,#fff 50%,#fff 75%, transparent 75%, transparent); 62 | background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(25%,rgba(255,255,255,0.2)),color-stop(25%,transparent),color-stop(50%,transparent),color-stop(50%,rgba(255,255,255,0.2)),color-stop(75%,rgba(255,255,255,0.2)),color-stop(75%,transparent)); 63 | background-size: 16px 16px; 64 | 65 | } 66 | .table__grid{ 67 | align-items: center; 68 | text-align: center; 69 | display: inline-table; 70 | } 71 | 72 | .table__grid_4_2{ 73 | width: 50%; 74 | } 75 | 76 | .table__grid_4_1{ 77 | width: 25%; 78 | } 79 | -------------------------------------------------------------------------------- /pages/elective/elective.js: -------------------------------------------------------------------------------- 1 | Page( { 2 | data: { 3 | // text:"这是一个页面" 4 | }, 5 | onLoad: function( options ) { 6 | // 页面初始化 options为页面跳转所带来的参数 7 | }, 8 | onReady: function() { 9 | // 页面渲染完成 10 | }, 11 | onShow: function() { 12 | // 页面显示 13 | }, 14 | onHide: function() { 15 | // 页面隐藏 16 | }, 17 | onUnload: function() { 18 | // 页面关闭 19 | } 20 | }) -------------------------------------------------------------------------------- /pages/elective/elective.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /pages/index/index.js: -------------------------------------------------------------------------------- 1 | var config = require('../../config.js') 2 | var mockData = require('../../mock/data.js') 3 | var simulator = require('../../utils/simulator.js') 4 | 5 | var inputs = {} 6 | 7 | // 获取应用实例 8 | var app = getApp() 9 | Page({ 10 | data: { 11 | loadingHidden: true, 12 | modalHidden: true, 13 | modalContent: '', 14 | inputs: {} 15 | }, 16 | 17 | tapLoading: function () { 18 | this.setData({ 19 | loadingHidden: true 20 | }) 21 | }, 22 | 23 | loading: function () { 24 | this.setData({ 25 | loadingHidden: false 26 | }) 27 | }, 28 | 29 | unloading: function () { 30 | this.setData({ 31 | loadingHidden: true 32 | }) 33 | }, 34 | inputChange: function (e) { 35 | inputs[e.currentTarget.id] = e.detail.value 36 | }, 37 | 38 | formSubmit: function () { 39 | var page = this 40 | if (inputs['username'] == null || inputs['username'] == '') { 41 | page.showModal('请输入学号') 42 | return 43 | } 44 | if (inputs['password'] == null || inputs['password'] == '') { 45 | page.showModal('请输入密码') 46 | return 47 | } 48 | page.loading() 49 | // 异步请求不能 50 | simulator.login(inputs['username'], inputs['password']) 51 | simulator.getAchievement(function (data) { 52 | console.log(data) 53 | wx.setStorageSync('username', inputs['username']) 54 | wx.setStorageSync('password', inputs['password']) 55 | 56 | page.unloading() 57 | wx.hideNavigationBarLoading() 58 | 59 | wx.navigateTo({ 60 | // 必须要序列化成字符串,URL编码自动完成 61 | url: '/pages/achievement/achievement?data=' + JSON.stringify(data) + '&username=' + inputs['username'] 62 | }) 63 | }, function (error) { 64 | console.log(error) 65 | 66 | page.unloading() 67 | page.showModal(error) 68 | }) 69 | }, 70 | 71 | formReset: function () { 72 | inputs = {} 73 | wx.setStorageSync('username', '') 74 | wx.setStorageSync('password', '') 75 | }, 76 | 77 | modalCancel: function () { 78 | this.setData({ 79 | modalHidden: true 80 | }) 81 | }, 82 | 83 | modalConfirm: function () { 84 | this.setData({ 85 | modalHidden: true 86 | }) 87 | }, 88 | 89 | showModal: function (msg) { 90 | this.setData({ 91 | modalHidden: false, 92 | modalContent: msg 93 | }) 94 | }, 95 | 96 | onLoad: function () { 97 | // 调用应用实例的方法获取全局数据 98 | var that = this 99 | inputs['username'] = wx.getStorageSync('username') 100 | inputs['password'] = wx.getStorageSync('password') // 这里没有加密安全性较低 101 | this.setData({ 102 | inputs: inputs 103 | }) 104 | }, 105 | 106 | switchChange: function (e) { 107 | inputs[e.currentTarget.id] = e.detail.value 108 | }, 109 | 110 | tapnav:function (){ 111 | wx.navigateTo({ 112 | url:'/pages/introduce/introduce' 113 | }) 114 | } 115 | }) 116 | -------------------------------------------------------------------------------- /pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | 11 | 12 |
13 | 14 | 学号 15 | 16 | 17 | 18 | 密码 19 | 20 | 21 | 22 | 记住学号和密码 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 |
37 | 使用说明 38 |
-------------------------------------------------------------------------------- /pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | /**index.wxss**/ 2 | .userinfo { 3 | display: flex; 4 | flex-direction: column; 5 | align-items: center; 6 | } 7 | 8 | .userinfo-avatar { 9 | width: 128rpx; 10 | height: 128rpx; 11 | margin: 20rpx; 12 | border-radius: 50%; 13 | } 14 | 15 | .userinfo-nickname { 16 | color: #aaa; 17 | } 18 | 19 | .usermotto { 20 | margin-top: 200px; 21 | } -------------------------------------------------------------------------------- /pages/introduce/introduce.js: -------------------------------------------------------------------------------- 1 | Page( { 2 | data: { 3 | // text:"这是一个页面" 4 | }, 5 | onLoad: function( options ) { 6 | // 页面初始化 options为页面跳转所带来的参数 7 | }, 8 | onReady: function() { 9 | // 页面渲染完成 10 | }, 11 | onShow: function() { 12 | // 页面显示 13 | }, 14 | onHide: function() { 15 | // 页面隐藏 16 | }, 17 | onUnload: function() { 18 | // 页面关闭 19 | } 20 | }) -------------------------------------------------------------------------------- /pages/introduce/introduce.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 跨域限制 5 | 6 | 7 | 8 | 由于同源策略,跨域访问受限,模拟登录暂时无法获取到 Cookie 9 | 10 | 11 | 使用模拟得数据进行演示,直接通过 GET 方法从个人站点获取 HTML 12 | 13 | 14 | -------------------------------------------------------------------------------- /pages/panel/panel.js: -------------------------------------------------------------------------------- 1 | Page( { 2 | data: { 3 | // text:"这是一个页面" 4 | }, 5 | onLoad: function( options ) { 6 | // 页面初始化 options为页面跳转所带来的参数 7 | }, 8 | onReady: function() { 9 | // 页面渲染完成 10 | }, 11 | onShow: function() { 12 | // 页面显示 13 | }, 14 | onHide: function() { 15 | // 页面隐藏 16 | }, 17 | onUnload: function() { 18 | // 页面关闭 19 | } 20 | }) -------------------------------------------------------------------------------- /pages/panel/panel.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /pages/panel/panel.wxss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh-h/student-information-system-wechat-applet/026561871eaffe181619e179b86e2f7aba24b652/pages/panel/panel.wxss -------------------------------------------------------------------------------- /utils/parser.js: -------------------------------------------------------------------------------- 1 | var config = require('../config.js') 2 | 3 | var viewStatePattern = /name="__VIEWSTATE[\w\W]+?value="(.+?)"/ 4 | var tablePattern = /([\w\W]+?)<\/table>/ig 5 | var trPattern = /([\w\W]+?)<\/tr>/ig 6 | var tdPatterb = /([\w\W]+?)<\/td>/ig 7 | var szxfPattern = /(.+?)<\/font><\/span>/ 8 | var pjxfjdPattern = /(.+?)<\/font><\/span>/ 9 | var zxfjdPattern = /(.+?)<\/font>/ 10 | 11 | // ajax 访问图片/二进制 12 | var test = function (url, callback) { 13 | var xhr = new XMLHttpRequest() 14 | xhr.responseType = 'blob' 15 | xhr.onload = function () { 16 | var reader = new FileReader() 17 | var headers = xhr.getAllResponseHeaders() 18 | reader.onloadend = function () { 19 | callback(reader.result, headers) 20 | } 21 | reader.readAsDataURL(xhr.response) 22 | } 23 | xhr.open('GET', url) 24 | xhr.send() 25 | } 26 | 27 | var paseAchievement = function (html) { 28 | // match values 29 | var achievement = [] 30 | 31 | // match tables 32 | var tableMatchers = html.match(tablePattern) 33 | var table1 = tableMatchers[2] 34 | var trMatchers1 = table1.match(trPattern) 35 | // match table grids 36 | for (var i in trMatchers1) { 37 | var tr = trMatchers1[i] 38 | var tdMatchers = tr.match(tdPatterb) 39 | var row = [] 40 | for (var j in tdMatchers) { 41 | var value = tdMatchers[j].replace(/()/, '').replace(/(<\/td>)/, '') 42 | if (value == ' ') 43 | value = '' 44 | row.push(value) 45 | } 46 | achievement.push(row) 47 | } 48 | 49 | return achievement 50 | } 51 | 52 | var paseViewState = function (html) { 53 | var viewState = '' 54 | var viewStateMatcher = html.match(viewStatePattern) 55 | if (viewStateMatcher != null) 56 | viewState = viewStateMatcher[1] 57 | return viewState 58 | } 59 | 60 | module.exports = { 61 | 'test': test, 62 | 'paseAchievement': paseAchievement, 63 | 'paseViewState': paseViewState 64 | } 65 | -------------------------------------------------------------------------------- /utils/simulator.js: -------------------------------------------------------------------------------- 1 | var config = require('../config.js') 2 | var paser = require('./parser.js') 3 | 4 | var loginUrl = config.loginUrl 5 | var infoUrl = config.infoUrl 6 | var electiveUrl = config.electiveUrl 7 | var achievementUrl = config.achievementUrl 8 | var cookieStr = '' 9 | 10 | login = function (username, password) { 11 | // TODO 12 | } 13 | 14 | getAchievement = function (successFunc, failFunc) { 15 | console.log(achievementUrl) 16 | wx.request({ 17 | url: achievementUrl, 18 | header: { 19 | 'Cookie': cookieStr 20 | }, 21 | success: function (res) { 22 | var data = [] 23 | try { 24 | data = paser.paseAchievement(res.data) 25 | successFunc(data) 26 | } catch (error) { 27 | failFunc('parse error') 28 | } 29 | }, 30 | fail: function (res) { 31 | failFunc('network error') 32 | } 33 | }) 34 | } 35 | 36 | getElective = function () { 37 | // TODO 38 | return [] 39 | } 40 | 41 | getInfo = function () { 42 | // TODO 43 | return [] 44 | } 45 | 46 | module.exports = { 47 | 'getAchievement': getAchievement, 48 | 'login': login, 49 | 'getElective': getElective, 50 | 'getInfo': getInfo 51 | } 52 | -------------------------------------------------------------------------------- /utils/util.js: -------------------------------------------------------------------------------- 1 | function formatTime( date ) { 2 | var year = date.getFullYear() 3 | var month = date.getMonth() + 1 4 | var day = date.getDate() 5 | 6 | var hour = date.getHours() 7 | var minute = date.getMinutes() 8 | var second = date.getSeconds() 9 | 10 | 11 | return [ year, month, day ].map( formatNumber ).join( '/' ) + ' ' + [ hour, minute, second ].map( formatNumber ).join( ':' ) 12 | } 13 | 14 | function formatNumber( n ) { 15 | n = n.toString() 16 | return n[ 1 ] ? n : '0' + n 17 | } 18 | 19 | module.exports = { 20 | formatTime: formatTime 21 | } 22 | --------------------------------------------------------------------------------