├── my_score.php ├── .gitignore ├── imgs └── xuanchuan.png ├── js ├── login_score.js.dump ├── require_score.js ├── login_score.js └── accordion.js ├── style ├── main.css ├── accordion.css └── weui.min.css ├── core ├── model │ ├── APIResult.php │ ├── CourseSchedule.php │ ├── Course.php │ └── CourseDetailed.php ├── const.php ├── utils.php ├── http.php ├── requests.php ├── parser.php └── BJUTHelper.php ├── sources.list ├── LICENSE ├── index.php ├── Dockerfile ├── README.md ├── API.php ├── login_grade.php └── require_grade.php /my_score.php: -------------------------------------------------------------------------------- 1 | (.*?)<\/'.$tag.'>/is', $content, $mat); 11 | return $mat[3]; 12 | } 13 | 14 | function divide_string_by_colon(string $content){ 15 | $item = str_replace(array(":"),':',$content); 16 | return explode(":", $item); 17 | } 18 | 19 | function get_argument(string $name, $default=null){ 20 | if(isset($_POST[$name])){ 21 | return $_POST[$name]; 22 | } 23 | if(isset($_GET[$name])){ 24 | return $_GET[$name]; 25 | } 26 | return $default; 27 | } 28 | -------------------------------------------------------------------------------- /core/model/CourseDetailed.php: -------------------------------------------------------------------------------- 1 | /etc/timezone 11 | 12 | #更新安装依赖包和PHP核心拓展 13 | #换源 14 | #COPY sources.list . 15 | #RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak && mv sources.list /etc/apt/ 16 | 17 | #RUN apt-get update && apt-get install -y \ 18 | # git \ 19 | # libfreetype6-dev \ 20 | # libjpeg62-turbo-dev \ 21 | # libpng-dev 22 | #RUN docker-php-ext-install -j$(nproc) iconv \ 23 | #&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \ 24 | #&& docker-php-ext-install -j$(nproc) gd \ 25 | # && docker-php-ext-install zip \ 26 | # && docker-php-ext-install pdo_mysql \ 27 | # && docker-php-ext-install opcache \ 28 | # && docker-php-ext-install mysqli \ 29 | # && rm -r /var/lib/apt/lists/* 30 | 31 | 32 | 33 | #安装 PECL 拓展,这里我们安装的是Redis和xdebug 34 | #RUN pecl install redis-4.2.0RC1 \ 35 | # && pecl install xdebug-2.7.0beta1 \ 36 | # && docker-php-ext-enable redis xdebug 37 | 38 | #安装第三方拓展,这里是 Phalcon 拓展 39 | 40 | #RUN cd /home \ 41 | #&& tar -zxvf cphalcon.tar.gz \ 42 | #&& mv cphalcon-* phalcon \ 43 | #&& cd phalcon/build \ 44 | #&& ./install \ 45 | #&& echo "extension=phalcon.so" > /usr/local/etc/php/conf.d/phalcon.ini 46 | 47 | #安装 Composer 48 | 49 | #ENV COMPOSER_HOME /root/composer 50 | #RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer 51 | #ENV PATH $COMPOSER_HOME/vendor/bin:$PATH 52 | 53 | #RUN rm -f /home/redis.tgz \ 54 | # rm -f /home/cphalcon.tar.gz 55 | 56 | WORKDIR /var/www/html 57 | 58 | COPY . . 59 | 60 | #Write Permission 61 | 62 | #RUN usermod -u 1000 www-data 63 | 64 | #CMD php-fpm -------------------------------------------------------------------------------- /style/accordion.css: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: 卓文理 3 | * @Email : 531840344@qq.com 4 | * @Desc : 折叠菜单 5 | */ 6 | 7 | .weui_accordion_box{ 8 | background: #fbf9fe; 9 | } 10 | .weui_accordion_box .weui_accordion_title{ 11 | position: relative; 12 | display: block; 13 | padding: 10px 15px 12px; 14 | font-size: 17px; 15 | background: #fff; 16 | -webkit-tap-highlight-color: transparent; 17 | } 18 | .weui_accordion_box .weui_accordion_title:before{ 19 | content: " "; 20 | position: absolute; 21 | left: 0; 22 | bottom: 0; 23 | width: 100%; 24 | height: 1px; 25 | border-bottom: 1px solid #D9D9D9; 26 | color: #D9D9D9; 27 | -webkit-transform-origin: 0 100%; 28 | transform-origin: 0 100%; 29 | -webkit-transform: scaleY(0.5); 30 | transform: scaleY(0.5); 31 | } 32 | .weui_accordion_box .weui_accordion_title:after{ 33 | content: " "; 34 | display: inline-block; 35 | -webkit-transform: rotate(45deg); 36 | transform: rotate(45deg); 37 | height: 6px; 38 | width: 6px; 39 | border-width: 0 2px 2px 0; 40 | border-color: #C7C7CC; 41 | border-style: solid; 42 | top: -2px; 43 | position: absolute; 44 | right: 15px; 45 | top: 50%; 46 | margin-top: -4px; 47 | } 48 | .weui_accordion_box .weui_accordion_title.active:after{ 49 | border-width: 2px 0 0 2px; 50 | } 51 | .weui_accordion_box .weui_accordion_content{ 52 | position: relative; 53 | padding: 15px; 54 | display: none; 55 | } 56 | .weui_accordion_box .weui_accordion_content:after{ 57 | content: " "; 58 | position: absolute; 59 | left: 0; 60 | bottom: 0; 61 | width: 100%; 62 | height: 1px; 63 | border-bottom: 1px solid #D9D9D9; 64 | color: #D9D9D9; 65 | -webkit-transform-origin: 0 100%; 66 | transform-origin: 0 100%; 67 | -webkit-transform: scaleY(0.5); 68 | transform: scaleY(0.5); 69 | } 70 | .weui_accordion_box .weui_accordion_content.active{ 71 | display: block; 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 工大野生助手 2 | 3 | ## TODO LIST 4 | 5 | - 更新README,以适应重构后的代码 6 | - 制作API 7 | - 双学位课程的加权问题 8 | 9 | ## 访问量 10 | 11 | ![](http://7xku3h.com1.z0.glb.clouddn.com/16-7-16/85684382.jpg) 12 | 13 | (注:最新的单日最高PV是3.5万) 14 | 15 | ## 预览 16 | 17 | ![](http://7xku3h.com1.z0.glb.clouddn.com/16-7-23/12366574.jpg) 18 | ![](http://7xku3h.com1.z0.glb.clouddn.com/16-7-23/47965690.jpg) 19 | 20 | ## 功能 21 | 22 | - 查某学期期末考试的各科成绩。 23 | - 查所有学期总的加权平均分和平均GPA。 24 | 25 | ## 运行模式 26 | 27 | - 这两个PHP页面运行在Apache中; 28 | - Apache安装在我的电脑上; 29 | - 电脑连了学校的内网,也就是bjut-wifi或者插网线; 30 | - 将自己电脑的80端口映射到外网。映射的方法很多,我用的是ngrok这个开源工具,也可以用花生壳等。 31 | - 【手机端(扫二维码访问登陆页面)】<--->【外网映射服务器】<--(学校网关)-->【内网电脑,运行着Apache】<--->【教务网站】 32 | - 更新:ngrok的开源版本有内存泄漏的BUG,实测大约20000个访问就会造成服务挂掉。我已替换为同类软件frp。 33 | 34 | ## 细节 35 | 36 | - `login_grade.php`负责登陆,`require_grade.php`负责具体的查询。 37 | 38 | ### login_grade.php 39 | 40 | - UI用的是「WeUI」,微信团队做的,和微信的视觉效果一致; 41 | - 每个学校的正方教务都不太一样,我的代码中的各种http header参数、post参数基本符合北工大的教务网站。 42 | - 教务系统的默认登陆页面是`default2`,可以换换default后面的数字,可以有不需要验证码的登陆页面。 43 | 44 | ### require_grade.php 45 | 46 | - 中文编码容易出现乱码,涉及到本地IDE的编码、PHP的编码和网站的编码; 47 | - 有两种post,第一种是登陆,第二种是获取成绩信息。获取成绩信息的时候有多次post,分别是获取某学期的成绩和获取总成绩; 48 | - 构造POST的思路:打开浏览器开发者工具,先正常走一遍查询成绩的流程,观察POST数据的key和value,并在代码中构造; 49 | - 由于页面返回的是一坨DIV,而且既没有name属性也没有id属性,所以需要将table标签整个转成array,并观察所需要的信息的位置,通过数组下标的方式访问; 50 | - 总的平均GPA无法区分主修专业和二专业/辅修专业,因为查询所有科目的总成绩页面中没有任何标签能区分这两者。 51 | 52 | 53 | ## 其他 54 | 55 | - 其实就是个爬虫,模拟了登陆正方教务系统并查询成绩的过程。Python写爬虫的话可能更方便,Python有个库requests,非常好用。 56 | - PHP中嵌入HTML感觉很不优雅,但HTML中嵌入PHP也好不到哪里去,有什么更好的办法吗? 57 | - 网上有很多关于正方教务的东西,虽然不太容易直接照搬,但学习价值很大。table转html的函数我就是从网上找的。 58 | - 交流请联系 807103724@qq.com,微信同QQ。 59 | - 欢迎提交Issue和Pull Request! 60 | 61 | ## 外网使用 62 | 63 | 更新后的野生助手可以安装在外网机器上,只需要提供 https://vpn.bjut.edu.cn/ 网关登录账户即可。 64 | 65 | 配置方式:只需要在主目录中创建 config.php 这个文件,内容包含 VPN 的用户名和密码。该用户名和密码可能与教务系统有所不同。 66 | 67 | ```php 68 | result = $years; 27 | echo json_encode($response, JSON_UNESCAPED_UNICODE); 28 | exit(); 29 | break; 30 | case "get_term"; 31 | $response = new APIResult(); 32 | $response->result = $terms; 33 | echo json_encode($response, JSON_UNESCAPED_UNICODE); 34 | exit(); 35 | break; 36 | } 37 | } 38 | 39 | $xh = get_argument('account'); 40 | $pw = get_argument('password'); 41 | $current_year = get_argument('current_year', ""); 42 | $current_term = get_argument('current_term', ""); 43 | 44 | $student = new BJUTHelper($xh, $pw); 45 | 46 | if(isset($proxyUserName)){ 47 | if(!$student->login_vpn()){ 48 | $response = new APIResult(); 49 | $response->err = 403; 50 | $response->err_msg = "VPN网关账户信息错误"; 51 | echo json_encode($response, JSON_UNESCAPED_UNICODE); 52 | exit(); 53 | } 54 | } 55 | $login_success = $student->login(); 56 | 57 | //若登陆信息输入有误 58 | if(!$login_success){ 59 | $response = new APIResult(); 60 | $response->err = 403; 61 | $response->err_msg = "您的账号 or 密码输入错误,或者是选择了无效的学年/学期"; 62 | echo json_encode($response, JSON_UNESCAPED_UNICODE); 63 | exit(); 64 | } 65 | 66 | if($action == "schedule"){ 67 | $result = $student->get_specified_schedule($current_year, $current_term); 68 | } 69 | else{ 70 | $result = $student->get_final_result($current_year, $current_term); 71 | } 72 | 73 | $response = new APIResult(); 74 | $response->result = $result; 75 | 76 | //var_dump($result); 77 | 78 | echo json_encode($response, JSON_UNESCAPED_UNICODE); 79 | -------------------------------------------------------------------------------- /core/http.php: -------------------------------------------------------------------------------- 1 | cookie = $cookie; 47 | } 48 | 49 | function get_cookie(){ 50 | return $this->cookie; 51 | } 52 | 53 | /** 54 | * 自动修改所属对象cookie的post函数 55 | * @param $url 56 | * @param string $post 57 | * @return mixed 58 | */ 59 | function post($url, $post="", $set_cookie=false, $follow=1){ 60 | $result = self::login_post_fp($url, $post, $this->cookie, $follow); 61 | $cookie = login_cookie_parser($result); 62 | if($set_cookie && $cookie){ 63 | $this->cookie .= $cookie . ";"; 64 | } 65 | //统一转码至utf-8 66 | $result = iconv("gb2312","utf-8//IGNORE", $result); 67 | return $result; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /js/accordion.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: 卓文理 3 | * @Email : 531840344@qq.com 4 | * @Desc : 折叠菜单 5 | */ 6 | 7 | (function (factory) { 8 | if ( typeof define === 'function' && define.amd ) { 9 | define(['jquery'], factory); 10 | } else if (typeof exports === 'object') { 11 | module.exports = factory; 12 | } else { 13 | factory(jQuery); 14 | } 15 | }(function ($) { 16 | var ua = navigator.userAgent; 17 | var isMobile = /(iPhone|Android|Mobile)/.test(ua); 18 | var EVENT_TAP = isMobile && !jQuery ? 'tap' : 'click'; 19 | 20 | var accordion = function(e, options) { 21 | var $accordion = this.$accordion = $(e); 22 | 23 | this.options = $.extend({ 24 | animate: true, 25 | duration: 300, 26 | onChange: function() {} 27 | }, options); 28 | 29 | this.init(); 30 | } 31 | 32 | accordion.prototype = { 33 | constructor: accordion, 34 | init: function() { 35 | var $accordion = this.$accordion; 36 | var that = this; 37 | 38 | $accordion.find('.weui_accordion_title').unbind(EVENT_TAP); 39 | $accordion.find('.weui_accordion_title').on(EVENT_TAP, function() { 40 | var $title = $(this); 41 | var $content = $title.parent().find('.weui_accordion_content'); 42 | 43 | if ($title.hasClass('active')) { 44 | $title.removeClass('active'); 45 | that.close($content); 46 | } else { 47 | $title.addClass('active'); 48 | that.open($content); 49 | } 50 | }); 51 | }, 52 | open: function($content) { 53 | var options = this.options; 54 | 55 | if (options.animate) { 56 | $content.slideDown(options.duration); 57 | 58 | setTimeout(function() { 59 | $content.addClass('active'); 60 | }, options.duration); 61 | } else { 62 | $content.show(); 63 | $content.addClass('active'); 64 | } 65 | 66 | options.onChange('open'); 67 | }, 68 | close: function($content) { 69 | var options = this.options; 70 | 71 | if (options.animate) { 72 | $content.slideUp(options.duration); 73 | 74 | setTimeout(function() { 75 | $content.removeClass('active'); 76 | }, options.duration); 77 | } else { 78 | $content.hide(); 79 | $content.removeClass('active'); 80 | } 81 | 82 | options.onChange('close'); 83 | }, 84 | } 85 | 86 | $.fn.extend({ 87 | accordion: function(options) { 88 | new accordion(this, options); 89 | }, 90 | }); 91 | })); 92 | $('.weui_accordion_box').accordion({ 93 | animate: true, // 开启动画效果 94 | duration: 300, // 动画时长 95 | onChange: function(event) { 96 | console.log(event); // 'open' or 'close' 97 | } 98 | }); -------------------------------------------------------------------------------- /core/requests.php: -------------------------------------------------------------------------------- 1 | ]+)" \/>/', $con1, $view); //获取__VIEWSTATE字段并存到$view数组中 35 | //为登陆准备的POST数据 36 | 37 | $post=array( 38 | //'__VIEWSTATE'=>$view[1][0], 39 | 'TextBox1'=>$stu_id, 40 | 'TextBox2'=>$pwd, 41 | //'txtSecretCode'=>$code, 42 | //'RadioButtonList1_2'=>'%D1%A7%C9%FA', //“学生”的gbk编码 43 | //'Button1'=>'', 44 | //'lbLanguage'=>'', 45 | //'hidPdrs'=>'', 46 | //'hidsc'=>'' 47 | ); 48 | 49 | return $http_holder->post($url,http_build_query($post), true, 0); //将数组连接成字符串, 登陆教务系统 50 | 51 | } 52 | 53 | /** 54 | * 用于生成查询成绩和获取view_state的链接 55 | * @param string $stu_id 学号 56 | * @return string 57 | */ 58 | function generate_grade_url(string $stu_id){ 59 | return URL_PREFIX . "gdjwgl.bjut.edu.cn/xscj_gc.aspx?xh=".$stu_id; 60 | } 61 | 62 | /** 63 | * 用于生成查询课表的链接 64 | * @param string $stu_id 学号 65 | * @return string 66 | */ 67 | function generate_course_url(string $stu_id){ 68 | return URL_PREFIX . "gdjwgl.bjut.edu.cn/xskbcx.aspx?xh=".$stu_id; 69 | } 70 | 71 | /** 72 | * 用于生成查询考试的链接 73 | * @param string $stu_id 学号 74 | * @return string 75 | */ 76 | function generate_exam_url(string $stu_id){ 77 | return URL_PREFIX . "gdjwgl.bjut.edu.cn/xskscx.aspx?xh=".$stu_id; 78 | } 79 | 80 | /** 81 | * 可配合view_state_parser获取页面的 view state 82 | * 用于查询成绩的请求 83 | * @param HttpHolder $http_holder 传入已经登录的HttpHolder 84 | * @param $url 链接 85 | * @return mixed 86 | */ 87 | function send_view_state_request(HttpHolder $http_holder, string $url){ 88 | 89 | // 不知道为什么,不提交姓名信息也能查询 90 | // preg_match_all('/([^<>]+)/', $con2, $xm); //正则出的数据存到$xm数组中 91 | // print_r($xm); 92 | // $xm[1][0]=substr($xm[1][0],0,-4); //字符串截取,获得姓名 93 | 94 | $http_content = $http_holder->post($url); 95 | 96 | return $http_content; 97 | 98 | } 99 | 100 | /** 101 | * 获取指定学期的成绩 102 | * 可配合specified_grade_parser获取页面的成绩 103 | * @param HttpHolder $http_holder 传入已经登录的HttpHolder 104 | * @param string $stu_id 学号 105 | * @param string $view_state 106 | * @param string $current_year 107 | * @param string $current_term 108 | * @return mixed 109 | */ 110 | function send_specified_grade_request(HttpHolder $http_holder, 111 | string $stu_id, 112 | string $view_state, 113 | string $current_year, 114 | string $current_term){ 115 | 116 | $url = generate_grade_url($stu_id); 117 | 118 | //查询某一学期的成绩 119 | $post=array( 120 | '__VIEWSTATE'=>$view_state, 121 | 'ddlXN'=>$current_year, //当前学年 122 | 'ddlXQ'=>$current_term, //当前学期 123 | 'Button1'=>'%B0%B4%D1%A7%C6%DA%B2%E9%D1%AF', //别问我是啥 我也不知道 124 | ); 125 | 126 | $content=$http_holder->post($url,http_build_query($post)); //获取原始数据 127 | 128 | return $content; 129 | } 130 | 131 | /** 132 | * 获取所有学期的成绩 133 | * 可配合all_grade_parser获取页面的成绩 134 | * @param HttpHolder $http_holder 传入已经登录的HttpHolder 135 | * @param string $stu_id 学号 136 | * @param string $view_state 137 | * @return mixed 138 | */ 139 | function send_all_grade_request(HttpHolder $http_holder, 140 | string $stu_id, 141 | string $view_state){ 142 | 143 | $url = generate_grade_url($stu_id); 144 | 145 | //查询总成绩 146 | $post = array( 147 | '__VIEWSTATE'=>$view_state, 148 | 'Button6'=>'%B2%E9%D1%AF%D2%D1%D0%DE%BF%CE%B3%CC%D7%EE%B8%DF%B3%C9%BC%A8', //蜜汁 149 | ); 150 | 151 | $content=$http_holder->post($url,http_build_query($post)); //获取原始数据 152 | 153 | return $content; 154 | } 155 | 156 | /** 157 | * 获取特定学期的课表 158 | * @param HttpHolder $http_holder 传入已经登录的HttpHolder 159 | * @param string $stu_id 学号 160 | * @param string $view_state 161 | * @param string $year 学年 162 | * @param string $term 学期 163 | * @return mixed 164 | */ 165 | function send_schedule_request(HttpHolder $http_holder, 166 | string $stu_id, 167 | string $view_state, 168 | string $year="", 169 | string $term="" 170 | ){ 171 | 172 | $url = generate_course_url($stu_id); 173 | 174 | if ($year and $term){ 175 | $post = array( 176 | // '__VIEWSTATE'=>$view_state, 177 | 'xnd'=>$year, 178 | 'xqd'=>$term 179 | ); 180 | } 181 | else{ 182 | // $post = array('__VIEWSTATE'=>$view_state); 183 | $post = array(); 184 | } 185 | 186 | $content=$http_holder->post($url,http_build_query($post)); //获取原始数据 187 | 188 | return $content; 189 | } 190 | 191 | /** 192 | * 获取特定学期的考试 193 | * @param HttpHolder $http_holder 传入已经登录的HttpHolder 194 | * @param string $stu_id 学号 195 | * @param string $view_state 196 | * @return mixed 197 | */ 198 | function send_exam_request(HttpHolder $http_holder, 199 | string $stu_id, 200 | string $view_state){ 201 | 202 | $url = generate_exam_url($stu_id); 203 | 204 | $post = array( 205 | '__VIEWSTATE'=>$view_state,); 206 | 207 | $content=$http_holder->post($url,http_build_query($post)); //获取原始数据 208 | 209 | return $content; 210 | } -------------------------------------------------------------------------------- /login_grade.php: -------------------------------------------------------------------------------- 1 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 野生工大助手 - 查个锤子 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 |
66 |
67 | 68 |
69 |
70 | 学年 71 |
72 |
73 | 86 |
87 |
88 | 89 |
90 |
91 | 学期 92 |
93 |
94 | 107 |
108 |
109 | 120 |
121 | 122 | 123 | 143 | 144 | 152 | 153 | 154 |
155 | 156 |
157 | 158 |

159 |  账号和密码不会保留,请放心使用。
160 |  数据仅供参考,请以教务系统为准。

161 |

总加权平均分和总平均 GPA 的数据只对没报双学位的同学有效。
162 | GPA 根据 北工大教务处文件,采用四分制计算。其他学校可能采用不同算法。
163 | 如果存在分数不足60分的科目,默认加权分数计算则不包含该科目。
164 | 展开详情可以查看该科目补考通过后的参考均分

165 | 166 |
167 | 关于更新后加权和绩点变化的说明
168 |
169 | 如数据有问题(或者网站打不开了)请:
170 | 点击此处在 Github 上提出 171 | 或 邮件联系我们
172 |
173 | 适用北京工业大学, by 西大望路东锤子研究所
174 |  本项目是已结题星火重点项目,已报备相关单位
175 | 176 | 京ICP备16062922号-1 177 |
178 | 179 |
180 |
181 |
182 | 183 | 184 | -------------------------------------------------------------------------------- /require_grade.php: -------------------------------------------------------------------------------- 1 | login_vpn(); 24 | } 25 | $login_success = $student->login(); 26 | 27 | //若登陆信息输入有误 28 | if(!$login_success){ 29 | echo '

  您的账号 or 密码输入错误,或者是选择了无效的学年/学期,请返回重新输入

'; 30 | exit(); 31 | } 32 | 33 | if(isset($_GET['term_only']) || isset($_POST['term_only']) || !$current_year || !$current_term) { 34 | $result = $student->get_final_result($current_year, $current_term, false); 35 | } else { 36 | $result = $student->get_final_result($current_year, $current_term); 37 | } 38 | 39 | 40 | ?> 41 | 42 | 43 | 44 | 45 | 46 | 47 | <?php printf($p_account); ?> - 成绩查询结果 48 | 49 | 50 | 51 | 52 | 53 |
54 |
课程统计情况
55 |
56 |
57 |
58 | 59 |
60 | 61 |
62 |

63 | 64 |

65 |

66 | 67 |

68 |
69 | 70 |
71 |
72 | 73 |
总平均分
74 |
75 |
76 |
77 | 78 |
79 |
80 |

81 | 82 |

83 |

84 | 85 |

86 |
87 |
88 |
89 |
90 |
91 |
92 | 93 |
94 |
95 |

96 | 97 |

98 |

99 | 100 |

101 |
102 |
103 |
104 |
105 |
106 |
所有课程
107 |
108 |
109 | '; 113 | echo '
'; 114 | echo $course->name. " ($course->credit)"; 115 | echo '
'; 116 | echo $course->score; 117 | echo '
'; 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 | GPA 根据 北工大教务处文件,采用四分制计算。其他学校可能采用不同算法。 145 |
146 |
147 |
148 |
149 | 150 | 0) { 153 | 154 | ?> 155 |
156 |
157 |

158 | 159 |

160 |
161 |
162 |
163 |
164 |

165 | 166 |

167 |
168 |
169 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 |
180 | term != $c_term || $course->year != $c_year) { 186 | echo '
' . $course->year . ' 学年,第 '. $course->term .' 学期
'; 187 | } 188 | if ($course->minor_maker == 0){ 189 | echo '
'; 190 | echo '
'; 191 | echo $course->name. " ($course->credit)"; 192 | echo '
'; 193 | echo $course->score; 194 | echo '
'; 195 | } 196 | if($course->term != $c_term || $course->year != $c_year) { 197 | $c_term = $course->term; 198 | $c_year = $course->year; 199 | } 200 | } 201 | ?> 202 |
203 | 204 |
本学期课程明细
205 |
206 | minor_maker == 0){ 210 | echo '
'; 211 | echo '
'; 212 | echo $course->name. " ($course->credit)"; 213 | echo '
'; 214 | echo $course->score; 215 | echo '
'; 216 | } 217 | } 218 | ?> 219 |
220 | 221 | 0) { 224 | // if ($total_score_fuxiu > 0 || $total_score_secondmajor > 0) { 225 | ?> 226 |
辅修/二专业课程
227 |
228 | minor_maker == 2){ 231 | echo '
'; 232 | echo '
'; 233 | echo $course->name. " ($course->credit)"; 234 | echo '
'; 235 | echo $course->score; 236 | echo '
'; 237 | } 238 | //辅修信息 239 | if ($course->minor_maker == 1){ 240 | echo '
'; 241 | echo '
'; 242 | echo $course->name. " ($course->credit)"; 243 | echo '
'; 244 | echo $course->score; 245 | echo '
'; 246 | } 247 | } 248 | ?> 249 |
250 | 253 | 254 | 返回 255 | 256 | 257 | -------------------------------------------------------------------------------- /core/parser.php: -------------------------------------------------------------------------------- 1 | ]+)" \/>/', $http_response, $vs); 51 | $state=$vs[1][0]; //$state存放一会post的__VIEWSTATE 52 | return $state; 53 | } 54 | 55 | 56 | /** 57 | * table转array 58 | * 为了可移植性而保留 59 | * @param string $content 60 | * @param bool $delete_tag 是否删除其它html标签 61 | * @return array 62 | */ 63 | function table_to_array(string $content, bool $delete_tag=true) { 64 | 65 | $content = preg_replace("']*?>'si","",$content); 66 | $content = preg_replace("']*?>'si","",$content); 67 | $content = preg_replace("']*?>'si","",$content); 68 | $content = str_replace("","{tr}",$content); 69 | $content = str_replace("","{td}",$content); 70 | //去掉 HTML 标记 71 | if($delete_tag){ 72 | $content = preg_replace("'<[/!]*?[^<>]*?>'si","",$content); 73 | } 74 | //去掉空白字符 75 | $content = preg_replace("'([rn])[s]+'","",$content); 76 | $content = preg_replace('/ /',"",$content); 77 | $content = str_replace(" ","",$content); 78 | $content = str_replace(" ","",$content); 79 | $content = explode('{tr}', $content); 80 | array_pop($content); 81 | $td_array = []; 82 | foreach ($content as $key=>$tr) { 83 | $td = explode('{td}', $tr); 84 | array_pop($td); 85 | $td_array[] = $td; 86 | } 87 | return $td_array; 88 | 89 | } 90 | 91 | 92 | 93 | /** 94 | * 该项目专用的课程抽取方法 95 | * @param $content 96 | * @return array 97 | */ 98 | function extract_grade_table_parser(string $content) { 99 | //去除多余空行 100 | $content = str_replace("\t", "", $content); 101 | $content = str_replace("\n", "", $content); 102 | $content = str_replace("\r", "", $content); 103 | 104 | $array = table_to_array($content); 105 | 106 | return $array; 107 | 108 | } 109 | 110 | 111 | 112 | /** 113 | * 该项目专用的课表抽取方法 114 | * @param $content 115 | * @return array 116 | */ 117 | function extract_schedule_table_parser(string $content) { 118 | //去除多余空行 119 | $content = str_replace("\t", "", $content); 120 | $content = str_replace("\n", "", $content); 121 | $content = str_replace("\r", "", $content); 122 | 123 | $array = table_to_array($content, false); 124 | 125 | return $array; 126 | 127 | } 128 | 129 | 130 | 131 | /** 132 | * 请务必注意以下两点: 133 | * 传进来的是【单个】课程的array数组 134 | * 获取的接口应该是all课程的接口 135 | * 输出的是单个Course对象 136 | * @param $course_array 137 | * @return Course 138 | */ 139 | function array_course_from_all_factory($course_array){ 140 | $result = new Course(); 141 | $result->year = $course_array[0]; 142 | $result->term = $course_array[1]; 143 | $result->id = $course_array[2]; 144 | $result->name = $course_array[3]; 145 | $result->credit = $course_array[4]; 146 | $result->type = $course_array[5]; 147 | $result->score = $course_array[6]; 148 | $result->belong = $course_array[7]; 149 | return $result; 150 | } 151 | /** 152 | * 请务必注意以下两点: 153 | * 传进来的是【单个】课程的array数组 154 | * 获取的接口应该是指定学期课程的接口 155 | * 输出的是单个CourseDetailed对象 156 | * @param $course_array 157 | * @return CourseDetailed 158 | */ 159 | function array_course_from_specific_factory($course_array){ 160 | $result = new CourseDetailed(); 161 | $result->year = $course_array[0]; //学年 162 | $result->term = $course_array[1]; //学期 163 | $result->id = $course_array[2]; //课程代码 164 | $result->name = $course_array[3]; //课程名称 165 | $result->type = $course_array[4]; //课程性质 166 | $result->belong = $course_array[5]; //课程归属 167 | $result->credit = $course_array[6]; //学分 168 | $result->gpa = $course_array[7]; //绩点 169 | $result->score = $course_array[8]; //成绩 170 | $result->minor_maker = $course_array[9]; //辅修标记 171 | $result->makeup_score = $course_array[10]; //补考成绩 172 | $result->retake_score = $course_array[11]; //重修成绩 173 | $result->academy = $course_array[12]; //开课学院 174 | $result->comment = $course_array[13]; //备注 175 | $result->retake_maker = $course_array[14]; //重修标记 176 | return $result; 177 | } 178 | 179 | /** 180 | * 请务必注意以下两点: 181 | * 传进来的是【单个】课程的array数组 182 | * 获取的接口应该是指定学期课程的接口 183 | * 输出的是单个CourseDetailed对象 184 | * @param $course_array 185 | * @return CourseSchedule 186 | */ 187 | function array_course_from_schedule_factory($course_array){ 188 | $result = new CourseSchedule(); 189 | $result->name = $course_array[0]; 190 | $result->time = $course_array[1]; 191 | $result->teacher = $course_array[2]; 192 | $result->classroom = $course_array[3]; 193 | return $result; 194 | } 195 | 196 | /** 197 | * 解析获取成绩为数组 198 | * 配合send_view_state_requests使用 199 | * @param $http_response 200 | * @return mixed 201 | */ 202 | function all_grade_parser(string $http_response){ 203 | 204 | $array = extract_grade_table_parser($http_response); 205 | //去前 206 | $array = array_slice($array, 1); 207 | //排后 208 | $array = array_slice($array, 0, count($array) - 0); 209 | 210 | $result = []; 211 | foreach ($array as $i){ 212 | $r = array_course_from_all_factory($i); 213 | array_push($result, $r); 214 | } 215 | 216 | return $result; 217 | 218 | /* 219 | * 解析后数组格式大致如下: 220 | * 特此记录供以后参考。 221 | * 因此,从第五个开始为课程,最后3个也是废弃信息。 222 | * 序号与课程对应关系见[4] 223 | * 224 | array (size=56) 225 | 4 => 226 | array (size=6) 227 | 0 => string '学年' (length=39) 228 | 1 => string '学期' (length=12) 229 | 2 => string '课程代码' (length=12) 230 | 3 => string '课程名称' (length=12) 231 | 4 => string '学分' (length=6) 232 | 5 => string '课程性质' (length=12) 233 | 6 => string '最高成绩值' (length=15) 234 | 7 => string '课程归属' (length=12) 235 | */ 236 | // array ( 237 | // 0 => '2017-2018', 238 | // 1 => '2', 239 | // 2 => '0003338', 240 | // 3 => 'JAVA程序设计', 241 | // 4 => '2.0', 242 | // 5 => '学科基础选修课', 243 | // 6 => '83', 244 | // 7 => '', 245 | // ) 246 | } 247 | 248 | /** 249 | * 从查分页面解析基本信息 250 | * 配合send_view_state_requests使用 251 | * @param $http_response 252 | * @return mixed 253 | */ 254 | function personal_score_info_parser(string $http_response){ 255 | 256 | $result = array( 257 | "sid"=> divide_string_by_colon( 258 | get_content_by_tag_and_id($http_response, "span", "Label3") 259 | )[1], //学号 260 | "name"=> divide_string_by_colon( 261 | get_content_by_tag_and_id($http_response, "span", "Label5") 262 | )[1],//姓名 263 | "institute"=> divide_string_by_colon( 264 | get_content_by_tag_and_id($http_response, "span", "Label6") 265 | )[1],//学院 266 | "major"=> 267 | get_content_by_tag_and_id($http_response, "span", "Label7"),//专业 268 | "direction"=> "", //这项学校不再提供了 269 | "class"=> divide_string_by_colon( 270 | get_content_by_tag_and_id($http_response, "span", "Label8") 271 | )[1],//行政班 272 | ); 273 | 274 | return $result; 275 | 276 | } 277 | 278 | 279 | 280 | /** 281 | * 从课表页面解析基本信息 282 | * 配合send_view_state_requests使用 283 | * @param $http_response 284 | * @return mixed 285 | */ 286 | function personal_schedule_info_parser(string $http_response){ 287 | 288 | $result = array( 289 | "sid"=> divide_string_by_colon( 290 | get_content_by_tag_and_id($http_response, "span", "Label5") 291 | )[1], //学号 292 | "name"=> divide_string_by_colon( 293 | get_content_by_tag_and_id($http_response, "span", "Label6") 294 | )[1],//姓名 295 | "institute"=> divide_string_by_colon( 296 | get_content_by_tag_and_id($http_response, "span", "Label7") 297 | )[1],//学院 298 | "major"=> divide_string_by_colon( 299 | get_content_by_tag_and_id($http_response, "span", "Label8") 300 | )[1],//专业 301 | "direction"=> "", //这项学校不再提供了 302 | "class"=> divide_string_by_colon( 303 | get_content_by_tag_and_id($http_response, "span", "Label9") 304 | )[1],//行政班 305 | ); 306 | 307 | return $result; 308 | 309 | } 310 | 311 | 312 | 313 | /** 314 | * 解析某一学期成绩 315 | * @param $http_response 316 | * @return mixed 317 | */ 318 | function specified_grade_parser(string $http_response){ 319 | 320 | $array = extract_grade_table_parser($http_response); 321 | //去前 322 | $array = array_slice($array, 1); 323 | //排后 324 | // $array = array_slice($array, 0, count($array) - 0); 325 | 326 | $result = []; 327 | foreach ($array as $i){ 328 | $r = array_course_from_specific_factory($i); 329 | array_push($result, $r); 330 | } 331 | 332 | return $result; 333 | 334 | /* 335 | * 解析后数组格式大致如下: 336 | * 下面这个已 337 | * 338 | array (size=22) 339 | 0 => 340 | array (size=15) 341 | 0 => string '学年' (length=6) 342 | 1 => string '学期' (length=6) 343 | 2 => string '课程代码' (length=12) 344 | 3 => string '课程名称' (length=12) 345 | 4 => string '课程性质' (length=12) 346 | 5 => string '课程归属' (length=12) 347 | 6 => string '学分' (length=6) 348 | 7 => string '绩点' (length=6) 349 | 8 => string '成绩' (length=6) 350 | 9 => string '辅修标记' (length=12) 351 | 10 => string '补考成绩' (length=12) 352 | 11 => string '重修成绩' (length=12) 353 | 12 => string '开课学院' (length=12) 354 | 13 => string '备注' (length=6) 355 | 14 => string '重修标记' (length=12) 356 | 【这里多了一个课程英文名称】 357 | 5 => 358 | array (size=15) 359 | 0 => string '2017-2018' (length=9) 360 | 1 => string '2' (length=1) 361 | 2 => string '0002549' (length=7) 362 | 3 => string '数据库原理Ⅰ' (length=18) 363 | 4 => string '学科基础必修课' (length=21) 364 | 5 => string '' (length=0) 365 | 6 => string '3.0' (length=3) 366 | 7 => string '4.00' (length=4) 367 | 8 => string '87' (length=2) 368 | 9 => string '0' (length=1) 369 | 10 => string '' (length=0) 370 | 11 => string '' (length=0) 371 | 12 => string '信息学部' (length=12) 372 | 13 => string '' (length=0) 373 | 14 => string '' (length=0) 374 | 6 => 375 | array (size=15) 376 | 0 => string '2017-2018' (length=9) 377 | 1 => string '2' (length=1) 378 | 2 => string '0003338' (length=7) 379 | 3 => string 'JAVA程序设计' (length=16) 380 | 4 => string '学科基础选修课' (length=21) 381 | 5 => string '' (length=0) 382 | 6 => string '2.0' (length=3) 383 | 7 => string '3.00' (length=4) 384 | 8 => string '83' (length=2) 385 | 9 => string '0' (length=1) 386 | 10 => string '' (length=0) 387 | 11 => string '' (length=0) 388 | 12 => string '信息学部' (length=12) 389 | 13 => string '' (length=0) 390 | 14 => string '' (length=0) 391 | 392 | ………………………… 393 | 394 | 18 => 395 | array (size=15) 396 | 0 => string '2017-2018' (length=9) 397 | 1 => string '2' (length=1) 398 | 2 => string 'ty01403' (length=7) 399 | 3 => string '篮球' (length=6) 400 | 4 => string '公共基础必修课' (length=21) 401 | 5 => string '' (length=0) 402 | 6 => string '1.0' (length=3) 403 | 7 => string '4.00' (length=4) 404 | 8 => string '86' (length=2) 405 | 9 => string '0' (length=1) 406 | 10 => string '' (length=0) 407 | 11 => string '' (length=0) 408 | 12 => string '' (length=0) 409 | 13 => string '' (length=0) 410 | 14 => string '' (length=0) 411 | 19 => 412 | array (size=1) 413 | 0 => string '' (length=0) 414 | 20 => 415 | array (size=4) 416 | 0 => string '' (length=0) 417 | 1 => string '' (length=0) 418 | 2 => string '' (length=0) 419 | 3 => string '' (length=0) 420 | 21 => 421 | array (size=2) 422 | 0 => string '成绩及绩点仅供参考,以教务管理端为准。' (length=57) 423 | 1 => string '' (length=0) 424 | 425 | */ 426 | 427 | } 428 | 429 | 430 | /** 431 | * 解析某一学期成绩 432 | * @param $http_response 433 | * @return mixed 434 | */ 435 | function specified_schedule_parser(string $http_response){ 436 | 437 | $courses_content = get_content_by_tag_and_id($http_response, "table", "Table1"); 438 | // $courses = explode("

"); 439 | 440 | $array = extract_schedule_table_parser($courses_content); 441 | 442 | $result = []; 443 | 444 | foreach ($array as $a){ 445 | foreach ($a as $i){ 446 | if(!$i or !preg_match("/
/", $i)){ 447 | continue; 448 | } 449 | $cs = explode("

", $i); 450 | foreach ($cs as $ca){ 451 | if(!$ca){ 452 | continue; 453 | } 454 | $a = explode("
", $ca); 455 | $c = array_course_from_schedule_factory($a); 456 | array_push($result, $c); 457 | } 458 | } 459 | } 460 | 461 | return $result; 462 | } 463 | 464 | -------------------------------------------------------------------------------- /core/BJUTHelper.php: -------------------------------------------------------------------------------- 1 | stu_id = $stu_id; 30 | $this->password = $password; 31 | $this->http = new HttpHolder(); 32 | } 33 | 34 | /** 35 | * 登录并检查密码。一切操作前都需要执行该方法 36 | * 成功返回真,失败(账号密码有误等等)返回假 37 | * @return bool 38 | */ 39 | function login(){ 40 | if (function_exists('apcu_fetch')) { 41 | $existing_cookie = apcu_fetch('cookie_'.$this->stu_id); 42 | if($existing_cookie){ 43 | $this->http->set_cookie($existing_cookie); 44 | return true; 45 | } 46 | } 47 | 48 | $login_context = send_login_request($this->http, $this->stu_id, $this->password); 49 | if(!check_login_success_parser($login_context)){ 50 | $this->is_login = false; 51 | return false; 52 | } 53 | 54 | if (function_exists('apcu_fetch')) { 55 | apcu_store('cookie_'.$this->stu_id, $this->http->get_cookie(), 300); 56 | } 57 | 58 | $this->is_login = true; 59 | return true; 60 | } 61 | /** 62 | * 查询是否登录 63 | * @return mixed 64 | * @throws Exception 账号密码有误 65 | */ 66 | function has_login(){ 67 | return $this->is_login && $this->view_state; 68 | } 69 | 70 | /** 71 | * 如果查分的view_state不是查分状态,切换为查分状态 72 | */ 73 | function ensure_score_view_state(){ 74 | //获取view_state以供后续查询成绩使用 75 | if($this->view_state_type != "score"){ 76 | $state_context = send_view_state_request($this->http, generate_grade_url($this->stu_id)); 77 | $this->view_state = view_state_parser($state_context); 78 | $this->view_state_type = "score"; 79 | } 80 | 81 | } 82 | 83 | /** 84 | * 如果查分的view_state不是课表状态,切换为课表状态 85 | */ 86 | function ensure_schedule_view_state(){ 87 | //获取view_state以供后续查询成绩使用 88 | if($this->view_state_type != "schedule"){ 89 | $state_context = send_view_state_request($this->http, generate_course_url($this->stu_id)); 90 | $this->view_state = view_state_parser($state_context); 91 | $this->view_state_type = "schedule"; 92 | } 93 | 94 | } 95 | 96 | /** 97 | * 获得指定一学期课程数据 98 | * @param string $current_year 99 | * @param string $current_term 100 | * @return array 101 | */ 102 | function get_specified_course(string $current_year, string $current_term){ 103 | $this->ensure_score_view_state(); 104 | $context = send_specified_grade_request( 105 | $this->http, 106 | $this->stu_id, 107 | $this->view_state, 108 | $current_year, 109 | $current_term); 110 | $table = get_content_by_tag_and_id($context, "table", "Datagrid1"); 111 | $courses = specified_grade_parser($table); 112 | $this->info = personal_score_info_parser($context); 113 | return $courses; 114 | } 115 | /** 116 | * 获得总成绩数据 117 | * @return array 118 | */ 119 | function get_all_course(){ 120 | $this->ensure_score_view_state(); 121 | $context = send_all_grade_request( 122 | $this->http, 123 | $this->stu_id, 124 | $this->view_state); 125 | $courses = all_grade_parser($context); 126 | $this->info = personal_score_info_parser($context); 127 | return $courses; 128 | } 129 | 130 | /** 131 | * 返回计算后结果 132 | * @param string $current_year 133 | * @param string $current_term 134 | * @param bool $all 是否获取在校期间全部 GPA 135 | * @return array 136 | */ 137 | function get_final_result(string $current_year, string $current_term, bool $all = true){ 138 | $this->ensure_score_view_state(); 139 | if(!$this->view_state) { return NULL; } 140 | $grade_total = array(); 141 | if($all) { 142 | $grade_total = $this->get_all_course(); 143 | } 144 | $grade_term = $this->get_specified_course($current_year, $current_term); 145 | //计算总的加权分数和总的GPA 146 | $all_score = 0; //总的加权*分数 147 | $all_score_include_unpassed = 0; // 包含不及格课程的总分数 148 | $all_score_include_unpassed_passed = 0; // 不及格课程按60计算的分数 149 | $all_value = 0; //总的学分权值 150 | $all_value_include_unpassed = 0; 151 | $all_GPA = 0; //总的GPA*分数 152 | $all_GPA_include_unpassed_passed = 0; 153 | $all_number_of_lesson_passed = 0; //总的课程数 154 | $all_number_of_lesson_include_unpassed = 0; //包含未过课程的总数 155 | //计算总和的东西,学分/GPA 156 | foreach ($grade_total as $course){ 157 | //不计算第二课堂和新生研讨课以及未通过课程 158 | if ($course->belong == "第二课堂" 159 | || $course->name == "新生研讨课" 160 | || ($course->belong == "" && $course->type == "校选修课") 161 | || $course->score < 60 162 | || strpos($course->type, "(辅)")) { 163 | //通过判断课程类型中的“(辅)”字样来过滤辅修成绩 164 | if ($course->score < 60 && is_numeric($course->score)){ 165 | $all_number_of_lesson_include_unpassed++; 166 | $all_score_include_unpassed += ($course->credit * $course->score); 167 | $all_score_include_unpassed_passed += ($course->credit * 60); 168 | $all_value_include_unpassed += $course->credit; 169 | $all_GPA_include_unpassed_passed += (2.0 * $course->credit); 170 | } 171 | } 172 | else{ 173 | $all_score += ($course->credit * $course->score); // 累加总分 174 | $all_score_include_unpassed += ($course->credit * $course->score); 175 | $all_score_include_unpassed_passed += ($course->credit * $course->score); 176 | $all_value += $course->credit; // 累加学分(权值) 177 | $all_value_include_unpassed += $course->credit; 178 | if ($course->score >= 85 && $course->score <= 100){ 179 | $all_GPA += (4.0 * $course->credit); 180 | $all_GPA_include_unpassed_passed += (4.0 * $course->credit); 181 | } 182 | else if ($course->score >= 70 && $course->score < 85){ 183 | $all_GPA += (3.0 * $course->credit); 184 | $all_GPA_include_unpassed_passed += (3.0 * $course->credit); 185 | } 186 | else if ($course->score >= 60 && $course->score < 70){ 187 | $all_GPA += (2.0 * $course->credit); 188 | $all_GPA_include_unpassed_passed += (2.0 * $course->credit); 189 | } 190 | $all_number_of_lesson_passed++; 191 | $all_number_of_lesson_include_unpassed++; 192 | } 193 | } 194 | $total_lesson_count = count($grade_total); 195 | //个别学期加权平均分和GPA的计算 196 | //主修课程 197 | $total_score = 0; 198 | $total_value = 0; 199 | $total_GPA = 0; 200 | $number_of_lesson = 0; //主修总课程数 201 | //二专业和辅修,$course->minor_maker == 2 202 | $total_score_minor = 0; 203 | $total_value_minor = 0; 204 | $total_GPA_minor = 0; 205 | $number_of_lesson_minor = 0; //二专业/辅修课程数 206 | //计算个别学期的信息 207 | foreach($grade_term as $course){ 208 | if (!($course->belong =="第二课堂" 209 | || $course->name === "新生研讨课" 210 | || ($course->belong == "" && $course->type == "校选修课") 211 | || $course->score < 60)){ 212 | //处理辅修/二专业 213 | if ($course->minor_maker == 2){ 214 | $total_score_minor += ($course->score * $course->credit); // 累加总分 215 | $total_value_minor += $course->credit; // 累加学分(权值) 216 | $total_GPA_minor += ($course->gpa * $course->credit); //加权总绩点 217 | $number_of_lesson_minor++; 218 | } 219 | //辅修 220 | if ($course->minor_maker == 1){ 221 | $total_score_minor += ($course->score * $course->credit); // 累加总分 222 | $total_value_minor += $course->credit; // 累加学分(权值) 223 | $total_GPA_minor += ($course->gpa * $course->credit); //加权总绩点 224 | $number_of_lesson_minor++; 225 | } 226 | //普通课程 227 | if ($course->minor_maker == 0){ 228 | $total_score += ($course->score * $course->credit); // 累加总分 229 | $total_value += $course->credit; // 累加学分(权值) 230 | $total_GPA += ($course->gpa * $course->credit); //加权总绩点 231 | $number_of_lesson++; 232 | } 233 | } 234 | } 235 | // $average_score = $total_score / $total_value; 236 | $term_lesson_count = count($grade_term); 237 | $average_score_all = $all_value !== 0 ? $all_score / $all_value : 0; 238 | $average_score_term = $total_value !== 0 ? $total_score / $total_value : 0; 239 | $average_score_minor = $total_value_minor !== 0 ? $total_score_minor / $total_value_minor : 0; 240 | $average_score_include_unpassed = $all_value_include_unpassed !== 0 ? $all_score_include_unpassed / $all_value_include_unpassed : 0; 241 | $average_score_include_unpassed_passed = $all_value_include_unpassed !== 0 ? $all_score_include_unpassed_passed / $all_value_include_unpassed : 0; 242 | $average_GPA_all = $all_value !== 0 ? $all_GPA / $all_value : 0; 243 | $average_GPA_term = $total_value !== 0 ? $total_GPA / $total_value : 0; 244 | $average_GPA_minor = $total_value_minor !== 0 ? $total_GPA_minor / $total_value_minor : 0; 245 | $average_GPA_include_unpassed = $all_value_include_unpassed !== 0 ? $all_GPA / $all_value_include_unpassed : 0; 246 | $average_GPA_include_unpassed_passed = $all_value_include_unpassed !== 0 ? $all_GPA_include_unpassed_passed / $all_value_include_unpassed : 0; 247 | $all_number_of_lesson_unpassed = $all_number_of_lesson_include_unpassed - $all_number_of_lesson_passed; 248 | $result = array( 249 | "sid"=> $this->info["sid"], 250 | "name"=> $this->info["name"], 251 | "institute"=> $this->info["institute"], 252 | "major"=> $this->info["major"], 253 | "direction"=> $this->info["direction"], 254 | "class"=> $this->info["class"], 255 | "grade_term" => $grade_term, //学习成绩数据集 256 | "grade_total" => $grade_total, //总成绩数据集 257 | "all_score" => $all_score, //总的加权*分数 258 | "all_value" => $all_value, //总的学分权值 259 | "all_GPA" => $all_GPA, //总的GPA*分数 260 | "all_number_of_lesson_passed" => $all_number_of_lesson_passed, //总的课程数 261 | "all_number_of_lesson_include_unpassed" => $all_number_of_lesson_include_unpassed, //包含未过课程的总数 262 | "all_number_of_lesson_unpassed" => $all_number_of_lesson_unpassed, //大学总未通过课程数 263 | "total_score" => $total_score, //学期累加总分 264 | "total_value" => $total_value, //学期累加学分(权值) 265 | "total_GPA" => $total_GPA, //学期GPA*分数 266 | "number_of_lesson" => $number_of_lesson, //主修总课程数 267 | "total_score_minor" => $total_score_minor, //二专业和辅修 268 | "total_value_minor" => $total_value_minor, 269 | "total_GPA_minor" => $total_GPA_minor, 270 | "number_of_lesson_minor" => $number_of_lesson_minor, //二专业/辅修课程数 271 | "average_score_all" => $average_score_all, //大学期间总加权平均分 272 | "average_score_term" => $average_score_term, //学期平均分 273 | "average_score_minor" => $average_score_minor, //辅修平均分 274 | "average_score_include_unpassed" => $average_score_include_unpassed, //含未通过课程均分(计实际分数) 275 | "average_score_include_unpassed_passed" => $average_score_include_unpassed_passed,//未通过课程补考后均分(计60分) 276 | "average_GPA_all" => $average_GPA_all, //大学期间总平均学分绩点(GPA) 277 | "average_GPA_term" => $average_GPA_term, //学期加权GPA 278 | "average_GPA_minor" => $average_GPA_minor, //辅修加权GPA 279 | "average_GPA_include_unpassed" => $average_GPA_include_unpassed, //含未通过课程绩点(未通过计0绩点) 280 | "average_GPA_include_unpassed_passed" => $average_GPA_include_unpassed_passed, //未通过课程补考后绩点(计60分2绩点) 281 | "term_lesson_count" => $term_lesson_count, //本学期已出分课程数 282 | "total_lesson_count" => $total_lesson_count, //大学总已出分课程数 283 | ); 284 | // var_dump($result); 285 | // exit(); 286 | return $result; 287 | } 288 | 289 | 290 | /** 291 | * 获得指定一学期课表 292 | * @param string $current_year 293 | * @param string $current_term 294 | * @return array 295 | */ 296 | function get_specified_schedule(string $current_year="", string $current_term=""){ 297 | $this->ensure_schedule_view_state(); 298 | if(!$this->view_state) { return NULL; } 299 | $context = send_schedule_request( 300 | $this->http, 301 | $this->stu_id, 302 | $this->view_state, 303 | $current_year, 304 | $current_term); 305 | $courses = specified_schedule_parser($context); 306 | $info = personal_schedule_info_parser($context); 307 | return array( 308 | "courses" => $courses, 309 | "info" => $info 310 | ); 311 | } 312 | 313 | /** 314 | * 登录 VPN 315 | */ 316 | function login_vpn(){ 317 | if (function_exists('apcu_fetch')) { 318 | $existing_cookie = apcu_fetch('vpn_cookie'); 319 | if($existing_cookie){ 320 | $this->http->set_cookie($existing_cookie); 321 | return true; 322 | } 323 | } 324 | global $proxyUserName, $proxyPassword; 325 | $url="https://vpn.bjut.edu.cn/prx/000/http/localhost/login"; // VPN 网关地址 326 | 327 | $post=array( 328 | 'uname'=>$proxyUserName, 329 | 'pwd'=>$proxyPassword, 330 | ); 331 | 332 | $login_context = $this->http->post($url, http_build_query($post), true, 0); //将数组连接成字符串, 登陆教务系统 333 | if(strstr($login_context, "https://vpn.bjut.edu.cn/prx/000/http/localhost/welcome")){ 334 | if (function_exists('apcu_fetch')) { 335 | apcu_store('vpn_cookie', $this->http->get_cookie(), 1800); 336 | } 337 | return true; 338 | } 339 | return false; 340 | } 341 | 342 | } 343 | //$test = new BJUTHelper("16080211", ""); 344 | //if($test->login()){ 345 | // $r = $test->get_all_course("2017-2018", "2"); 346 | // var_dump($r); 347 | //} -------------------------------------------------------------------------------- /style/weui.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * WeUI v0.4.0 (https://github.com/weui/weui) 3 | * Copyright 2016 Tencent, Inc. 4 | * Licensed under the MIT license 5 | */html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{line-height:1.6;font-family:Helvetica Neue,Helvetica,Arial,sans-serif}*{margin:0;padding:0}a img{border:0}a{text-decoration:none}@font-face{font-weight:400;font-style:normal;font-family:weui;src:url('data:application/octet-stream;base64,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') format('truetype')}[class*=" weui_icon_"]:before,[class^=weui_icon_]:before{font-family:weui;font-style:normal;font-weight:400;speak:none;display:inline-block;vertical-align:middle;text-decoration:inherit;width:1em;margin-right:.2em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;margin-left:.2em}.weui_icon_circle:before{content:"\EA01"}.weui_icon_download:before{content:"\EA02"}.weui_icon_info:before{content:"\EA03"}.weui_icon_safe_success:before{content:"\EA04"}.weui_icon_safe_warn:before{content:"\EA05"}.weui_icon_success:before{content:"\EA06"}.weui_icon_success_circle:before{content:"\EA07"}.weui_icon_success_no_circle:before{content:"\EA08"}.weui_icon_waiting:before{content:"\EA09"}.weui_icon_waiting_circle:before{content:"\EA0A"}.weui_icon_warn:before{content:"\EA0B"}.weui_icon_info_circle:before{content:"\EA0C"}.weui_icon_cancel:before{content:"\EA0D"}.weui_icon_search:before{content:"\EA0E"}.weui_icon_clear:before{content:"\EA0F"}[class*=" weui_icon_"]:before,[class^=weui_icon_]:before{margin:0}.weui_icon_success:before{font-size:23px;color:#09bb07}.weui_icon_waiting:before{font-size:23px;color:#10aeff}.weui_icon_warn:before{font-size:23px;color:#f43530}.weui_icon_info:before{font-size:23px;color:#10aeff}.weui_icon_success_circle:before,.weui_icon_success_no_circle:before{font-size:23px;color:#09bb07}.weui_icon_waiting_circle:before{font-size:23px;color:#10aeff}.weui_icon_circle:before{font-size:23px;color:#c9c9c9}.weui_icon_download:before,.weui_icon_info_circle:before{font-size:23px;color:#09bb07}.weui_icon_safe_success:before{color:#09bb07}.weui_icon_safe_warn:before{color:#ffbe00}.weui_icon_cancel:before{color:#f43530;font-size:22px}.weui_icon_clear:before,.weui_icon_search:before{color:#b2b2b2;font-size:14px}.weui_icon_msg:before{font-size:104px}.weui_icon_warn.weui_icon_msg:before{color:#f76260}.weui_icon_safe:before{font-size:104px}.weui_btn.weui_btn_mini{line-height:1.9;font-size:14px;padding:0 .75em;display:inline-block}button.weui_btn,input.weui_btn{width:100%;border-width:0;outline:0;-webkit-appearance:none}button.weui_btn:focus,input.weui_btn:focus{outline:0}button.weui_btn_inline,button.weui_btn_mini,input.weui_btn_inline,input.weui_btn_mini{width:auto}.weui_btn+.weui_btn{margin-top:15px}.weui_btn.weui_btn_inline+.weui_btn.weui_btn_inline{margin-top:auto;margin-left:15px}.weui_btn_area{margin:1.17647059em 15px .3em}.weui_btn_area.weui_btn_area_inline{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.weui_btn_area.weui_btn_area_inline .weui_btn{margin-top:auto;margin-right:15px;width:100%;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.weui_btn_area.weui_btn_area_inline .weui_btn:last-child{margin-right:0}.weui_btn{position:relative;display:block;margin-left:auto;margin-right:auto;padding-left:14px;padding-right:14px;box-sizing:border-box;font-size:18px;text-align:center;text-decoration:none;color:#fff;line-height:2.33333333;border-radius:5px;-webkit-tap-highlight-color:rgba(0,0,0,0);overflow:hidden}.weui_btn:after{content:" ";width:200%;height:200%;position:absolute;top:0;left:0;border:1px solid rgba(0,0,0,.2);-webkit-transform:scale(.5);transform:scale(.5);-webkit-transform-origin:0 0;transform-origin:0 0;box-sizing:border-box;border-radius:10px}.weui_btn.weui_btn_inline{display:inline-block}.weui_btn_default{background-color:#f7f7f7;color:#454545}.weui_btn_default:not(.weui_btn_disabled):visited{color:#454545}.weui_btn_default:not(.weui_btn_disabled):active{color:#a1a1a1;background-color:#dedede}.weui_btn_primary{background-color:#04be02}.weui_btn_primary:not(.weui_btn_disabled):visited{color:#fff}.weui_btn_primary:not(.weui_btn_disabled):active{color:hsla(0,0%,100%,.4);background-color:#039702}.weui_btn_warn{background-color:#ef4f4f}.weui_btn_warn:not(.weui_btn_disabled):visited{color:#fff}.weui_btn_warn:not(.weui_btn_disabled):active{color:hsla(0,0%,100%,.4);background-color:#c13e3e}.weui_btn_disabled{color:hsla(0,0%,100%,.6)}.weui_btn_disabled.weui_btn_default{color:#c9c9c9}.weui_btn_plain_primary{color:#04be02;border:1px solid #04be02}button.weui_btn_plain_primary,input.weui_btn_plain_primary{border-width:1px;background-color:transparent}.weui_btn_plain_primary:active{border-color:#039702}.weui_btn_plain_primary:after{border-width:0}.weui_btn_plain_default{color:#5a5a5a;border:1px solid #5a5a5a}button.weui_btn_plain_default,input.weui_btn_plain_default{border-width:1px;background-color:transparent}.weui_btn_plain_default:after{border-width:0}.weui_cell{position:relative}.weui_cell:before{content:" ";position:absolute;left:0;top:0;width:100%;height:1px;border-top:1px solid #d9d9d9;color:#d9d9d9;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5);left:15px}.weui_cell:first-child:before{display:none}.weui_cells{margin-top:1.17647059em;background-color:#fff;line-height:1.41176471;font-size:17px;overflow:hidden;position:relative}.weui_cells:before{top:0;border-top:1px solid #d9d9d9;-webkit-transform-origin:0 0;transform-origin:0 0}.weui_cells:after,.weui_cells:before{content:" ";position:absolute;left:0;width:100%;height:1px;color:#d9d9d9;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui_cells:after{bottom:0;border-bottom:1px solid #d9d9d9;-webkit-transform-origin:0 100%;transform-origin:0 100%}.weui_cells_title{margin-top:.77em;margin-bottom:.3em;padding-left:15px;padding-right:15px;color:#888;font-size:14px}.weui_cells_title+.weui_cells{margin-top:0}.weui_cells_tips{margin-top:.3em;color:#888;padding-left:15px;padding-right:15px;font-size:14px}.weui_cell{padding:10px 15px;position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.weui_cell_ft{text-align:right;color:#888}.weui_cell_primary{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.weui_cells_access .weui_cell:not(.no_access){-webkit-tap-highlight-color:rgba(0,0,0,0)}.weui_cells_access .weui_cell:not(.no_access):active{background-color:#ececec}.weui_cells_access a.weui_cell{color:inherit}.weui_cells_access .weui_cell_ft:after{content:" ";display:inline-block;-webkit-transform:rotate(45deg);transform:rotate(45deg);height:6px;width:6px;border-width:2px 2px 0 0;border-color:#c8c8cd;border-style:solid;position:relative;top:-2px;top:-1px;margin-left:.3em}.weui_check_label{-webkit-tap-highlight-color:rgba(0,0,0,0)}.weui_check{position:absolute;left:-9999em}.weui_cells_radio .weui_cell_ft{padding-left:.35em}.weui_cells_radio .weui_cell:active{background-color:#ececec}.weui_cells_radio .weui_check:checked+.weui_icon_checked:before{display:block;content:'\EA08';color:#09bb07;font-size:16px}.weui_cells_checkbox .weui_cell_hd{padding-right:.35em}.weui_cells_checkbox .weui_cell:active{background-color:#ececec}.weui_cells_checkbox .weui_icon_checked:before{content:'\EA01';color:#c9c9c9;font-size:23px;display:block}.weui_cells_checkbox .weui_check:checked+.weui_icon_checked:before{content:'\EA06';color:#09bb07}.weui_label{display:block;width:3em}.weui_input{width:100%;border:0;outline:0;-webkit-appearance:none;background-color:transparent;font-size:inherit;color:inherit;height:1.41176471em;line-height:1.41176471}.weui_input::-webkit-inner-spin-button,.weui_input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.weui_textarea{display:block;border:0;resize:none;width:100%;color:inherit;font-size:1em;line-height:inherit;outline:0}.weui_textarea_counter{color:#b2b2b2;text-align:right}.weui_cell_warn .weui_textarea_counter{color:#e64340}.weui_toptips{display:none;position:fixed;-webkit-transform:translateZ(0);width:100%;top:0;line-height:2.3;font-size:14px;text-align:center;color:#fff;z-index:2}.weui_toptips.weui_warn{background-color:#e64340}.weui_cells_form .weui_cell_warn{color:#e64340}.weui_cells_form .weui_cell_warn .weui_icon_warn{display:inline-block}.weui_cells_form .weui_cell_hd{padding-right:.3em}.weui_cells_form .weui_cell_ft{font-size:0}.weui_cells_form .weui_icon_warn{display:none}.weui_cells_form input,.weui_cells_form label[for],.weui_cells_form textarea{-webkit-tap-highlight-color:rgba(0,0,0,0)}.weui_cell_select{padding:0}.weui_cell_select .weui_select{padding-right:30px}.weui_cell_select .weui_cell_bd:after{content:" ";display:inline-block;-webkit-transform:rotate(45deg);transform:rotate(45deg);height:6px;width:6px;border-width:2px 2px 0 0;border-color:#c8c8cd;border-style:solid;position:relative;top:-2px;position:absolute;top:50%;right:15px;margin-top:-3px}.weui_select{-webkit-appearance:none;border:0;outline:0;background-color:transparent;width:100%;font-size:inherit;height:44px;position:relative;z-index:1;padding-left:15px}.weui_select_before{padding-right:15px}.weui_select_before .weui_select{width:auto}.weui_select_before .weui_cell_hd{position:relative}.weui_select_before .weui_cell_hd:after{content:" ";position:absolute;right:0;top:0;width:1px;height:100%;border-right:1px solid #d9d9d9;color:#d9d9d9;-webkit-transform-origin:0 100%;transform-origin:0 100%;-webkit-transform:scaleX(.5);transform:scaleX(.5)}.weui_select_before .weui_cell_hd:before{content:" ";display:inline-block;-webkit-transform:rotate(45deg);transform:rotate(45deg);height:6px;width:6px;border-width:2px 2px 0 0;border-color:#c8c8cd;border-style:solid;position:relative;top:-2px;position:absolute;top:50%;right:15px;margin-top:-3px}.weui_select_before .weui_cell_bd{padding-left:15px}.weui_select_before .weui_cell_bd:after{display:none}.weui_select_after{padding-left:15px}.weui_vcode{padding-top:0;padding-right:0;padding-bottom:0}.weui_vcode .weui_cell_ft img{margin-left:5px;height:44px;vertical-align:middle}.weui_cell_switch{padding-top:6px;padding-bottom:6px}.weui_switch{-webkit-appearance:none;-moz-appearance:none;appearance:none;position:relative;width:52px;height:32px;border:1px solid #dfdfdf;outline:0;border-radius:16px;box-sizing:border-box;background:#dfdfdf}.weui_switch:before{width:50px;background-color:#fdfdfd}.weui_switch:after,.weui_switch:before{content:" ";position:absolute;top:0;left:0;height:30px;border-radius:15px;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s}.weui_switch:after{width:30px;background-color:#fff;box-shadow:0 1px 3px rgba(0,0,0,.4)}.weui_switch:checked{border-color:#04be02;background-color:#04be02}.weui_switch:checked:before{-webkit-transform:scale(0);transform:scale(0)}.weui_switch:checked:after{-webkit-transform:translateX(20px);transform:translateX(20px)}.weui_uploader_hd{padding-top:0;padding-right:0;padding-left:0}.weui_uploader_hd .weui_cell_ft{font-size:1em}.weui_uploader_bd{margin-bottom:-4px;margin-right:-9px;overflow:hidden}.weui_uploader_files{list-style:none}.weui_uploader_file{float:left;margin-right:9px;margin-bottom:9px;width:79px;height:79px;background:no-repeat 50%;background-size:cover}.weui_uploader_status{position:relative}.weui_uploader_status:before{content:" ";position:absolute;top:0;right:0;bottom:0;left:0;background-color:rgba(0,0,0,.5)}.weui_uploader_status .weui_uploader_status_content{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#fff}.weui_uploader_status .weui_icon_warn{display:block}.weui_uploader_input_wrp{float:left;position:relative;margin-right:9px;margin-bottom:9px;width:77px;height:77px;border:1px solid #d9d9d9}.weui_uploader_input_wrp:after,.weui_uploader_input_wrp:before{content:" ";position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);background-color:#d9d9d9}.weui_uploader_input_wrp:before{width:2px;height:39.5px}.weui_uploader_input_wrp:after{width:39.5px;height:2px}.weui_uploader_input_wrp:active{border-color:#999}.weui_uploader_input_wrp:active:after,.weui_uploader_input_wrp:active:before{background-color:#999}.weui_uploader_input{position:absolute;z-index:1;top:0;left:0;width:100%;height:100%;opacity:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}.weui_msg{padding-top:36px;text-align:center}.weui_msg .weui_icon_area{margin-bottom:30px}.weui_msg .weui_text_area{margin-bottom:25px;padding:0 20px}.weui_msg .weui_msg_title{margin-bottom:5px;font-weight:400;font-size:20px}.weui_msg .weui_msg_desc{font-size:14px;color:#888}.weui_msg .weui_opr_area{margin-bottom:25px}.weui_msg .weui_extra_area{margin-bottom:15px;font-size:14px;color:#888}.weui_msg .weui_extra_area a{color:#61749b}@media screen and (min-height:438px){.weui_extra_area{position:fixed;left:0;bottom:0;width:100%;text-align:center}}.weui_article{padding:20px 15px;font-size:15px}.weui_article section{margin-bottom:1.5em}.weui_article h1{font-size:17px;font-weight:400;margin-bottom:.75em}.weui_article h2{font-size:16px;font-weight:400;margin-bottom:.3em}.weui_article h3{font-weight:400;font-size:15px}.weui_tabbar{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;position:absolute;bottom:0;width:100%;background-color:#f7f7fa}.weui_tabbar:before{content:" ";position:absolute;left:0;top:0;width:100%;height:1px;border-top:1px solid #979797;color:#979797;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui_tabbar_item{display:block;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;padding:7px 0 0;-webkit-tap-highlight-color:transparent}.weui_tabbar_item.weui_bar_item_on .weui_tabbar_label{color:#09bb07}.weui_tabbar_icon{margin:0 auto;width:24px;height:24px}.weui_tabbar_icon img{display:block;width:100%;height:100%}.weui_tabbar_icon+.weui_tabbar_label{margin-top:5px}.weui_tabbar_label{text-align:center;color:#888;font-size:12px}.weui_navbar{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;position:absolute;top:0;width:100%;background-color:#fafafa}.weui_navbar:after{content:" ";position:absolute;left:0;bottom:0;width:100%;height:1px;border-bottom:1px solid #bcbab6;color:#bcbab6;-webkit-transform-origin:0 100%;transform-origin:0 100%;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui_navbar+.weui_tab_bd{padding-top:50px;padding-bottom:0}.weui_navbar_item{position:relative;display:block;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;padding:13px 0;text-align:center;font-size:15px;-webkit-tap-highlight-color:transparent}.weui_navbar_item:active{background-color:#ededed}.weui_navbar_item.weui_bar_item_on{background-color:#f5f5f5}.weui_navbar_item:after{content:" ";position:absolute;right:0;top:0;width:1px;height:100%;border-right:1px solid #ccc;color:#ccc;-webkit-transform-origin:0 100%;transform-origin:0 100%;-webkit-transform:scaleX(.5);transform:scaleX(.5);right:-1px}.weui_navbar_item:last-child:after{display:none}.weui_tab{position:relative;height:100%}.weui_tab_bd{box-sizing:border-box;height:100%;padding-bottom:55px;overflow:auto;-webkit-overflow-scrolling:touch}.weui_tab_bd_item{display:none}.weui_progress{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.weui_progress_bar{background-color:#ebebeb;height:3px;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.weui_progress_inner_bar{width:0;height:100%;background-color:#09bb07}.weui_progress_opr{display:block;margin-left:15px;font-size:0}.weui_panel{background-color:#fff;margin-top:10px;position:relative;overflow:hidden}.weui_panel:first-child{margin-top:0}.weui_panel:before{top:0;border-top:1px solid #e5e5e5;-webkit-transform-origin:0 0;transform-origin:0 0}.weui_panel:after,.weui_panel:before{content:" ";position:absolute;left:0;width:100%;height:1px;color:#e5e5e5;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui_panel:after{bottom:0;border-bottom:1px solid #e5e5e5;-webkit-transform-origin:0 100%;transform-origin:0 100%}.weui_panel_hd{padding:14px 15px 10px;color:#999;font-size:13px;position:relative}.weui_panel_hd:after{content:" ";position:absolute;left:0;bottom:0;width:100%;height:1px;border-bottom:1px solid #e5e5e5;color:#e5e5e5;-webkit-transform-origin:0 100%;transform-origin:0 100%;-webkit-transform:scaleY(.5);transform:scaleY(.5);left:15px}.weui_panel_ft{padding:10px 15px 12px;color:#999;font-size:14px;position:relative}.weui_panel_ft:before{content:" ";position:absolute;left:0;top:0;width:100%;height:1px;border-top:1px solid #e5e5e5;color:#e5e5e5;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5);left:15px}.weui_panel_access .weui_panel_ft{display:block;color:#586c94;-webkit-tap-highlight-color:rgba(0,0,0,0)}.weui_panel_access .weui_panel_ft:active{background-color:#ececec}.weui_panel_access .weui_panel_ft:after{content:" ";display:inline-block;-webkit-transform:rotate(45deg);transform:rotate(45deg);height:6px;width:6px;border-width:2px 2px 0 0;border-color:#c7c7cc;border-style:solid;position:relative;top:-2px;position:absolute;right:15px;top:50%;margin-top:-4px}.weui_media_box{padding:15px;position:relative}.weui_media_box:before{content:" ";position:absolute;left:0;top:0;width:100%;height:1px;border-top:1px solid #e5e5e5;color:#e5e5e5;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5);left:15px}.weui_media_box:first-child:before{display:none}a.weui_media_box{color:#000;-webkit-tap-highlight-color:rgba(0,0,0,0)}a.weui_media_box:active{background-color:#ececec}.weui_media_box .weui_media_title{font-weight:400;font-size:17px;width:auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;word-wrap:break-word;word-break:break-all}.weui_media_box .weui_media_desc{color:#999;font-size:13px;line-height:1.2;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.weui_media_box.weui_media_text .weui_media_title{margin-bottom:8px}.weui_media_box.weui_media_text .weui_media_info{margin-top:15px;padding-bottom:5px;font-size:13px;color:#cecece;line-height:1em;list-style:none;overflow:hidden}.weui_media_box.weui_media_text .weui_media_info_meta{float:left;padding-right:1em}.weui_media_box.weui_media_text .weui_media_info_meta.weui_media_info_meta_extra{padding-left:1em;border-left:1px solid #cecece}.weui_media_box.weui_media_appmsg{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.weui_media_box.weui_media_appmsg .weui_media_hd{margin-right:.8em;width:60px;height:60px;line-height:60px;text-align:center}.weui_media_box.weui_media_appmsg .weui_media_appmsg_thumb{width:100%;max-height:100%;vertical-align:middle}.weui_media_box.weui_media_appmsg .weui_media_bd{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.weui_media_box.weui_media_small_appmsg{padding:0}.weui_media_box.weui_media_small_appmsg .weui_cells{margin-top:0}.weui_media_box.weui_media_small_appmsg .weui_cells:before{display:none}.weui_grids{position:relative;overflow:hidden}.weui_grids:before{width:100%;height:1px;border-top:1px solid #d9d9d9;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui_grids:after,.weui_grids:before{content:" ";position:absolute;left:0;top:0;color:#d9d9d9;-webkit-transform-origin:0 0}.weui_grids:after{width:1px;height:100%;border-left:1px solid #d9d9d9;transform-origin:0 0;-webkit-transform:scaleX(.5);transform:scaleX(.5)}.weui_grid{position:relative;float:left;padding:20px 10px;width:33.33333333%;box-sizing:border-box}.weui_grid:before{right:0;top:0;width:1px;height:100%;border-right:1px solid #d9d9d9;transform-origin:0 100%;-webkit-transform:scaleX(.5);transform:scaleX(.5);right:-1px}.weui_grid:after,.weui_grid:before{content:" ";position:absolute;color:#d9d9d9;-webkit-transform-origin:0 100%}.weui_grid:after{left:0;bottom:0;width:100%;height:1px;border-bottom:1px solid #d9d9d9;transform-origin:0 100%;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui_grid:active{background-color:#e4e4e4}.weui_grid_icon{width:28px;height:28px;margin:0 auto}.weui_grid_icon img{display:block;width:100%;height:100%}.weui_grid_icon+.weui_grid_label{margin-top:5px}.weui_grid_label{display:block;text-align:center;color:#000;font-size:14px}.weui_dialog{position:fixed;z-index:4;width:85%;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);background-color:#fafafc;text-align:center;border-radius:3px}.weui_dialog_confirm .weui_dialog .weui_dialog_hd{padding:1.2em 20px .5em}.weui_dialog_confirm .weui_dialog .weui_dialog_bd{text-align:left}.weui_dialog_hd{padding:1.2em 0 .5em}.weui_dialog_title{font-weight:400;font-size:17px}.weui_dialog_bd{padding:0 20px;font-size:15px;color:#888}.weui_dialog_ft{position:relative;line-height:42px;margin-top:20px;font-size:17px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.weui_dialog_ft a{display:block;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;color:#3cc51f;text-decoration:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}.weui_dialog_ft a:active{background-color:#eee}.weui_dialog_ft:after{content:" ";position:absolute;left:0;top:0;width:100%;height:1px;border-top:1px solid #d5d5d6;color:#d5d5d6;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui_dialog_confirm .weui_dialog_ft a{position:relative}.weui_dialog_confirm .weui_dialog_ft a:after{content:" ";position:absolute;left:0;top:0;width:1px;height:100%;border-left:1px solid #d5d5d6;color:#d5d5d6;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleX(.5);transform:scaleX(.5)}.weui_dialog_confirm .weui_dialog_ft a:first-child:after{display:none}.weui_btn_dialog.default{color:#353535}.weui_btn_dialog.primary{color:#0bb20c}@media screen and (min-width:1024px){.weui_dialog{width:35%}}.weui_toast{position:fixed;z-index:3;width:7.6em;min-height:7.6em;top:180px;left:50%;margin-left:-3.8em;background:rgba(40,40,40,.75);text-align:center;border-radius:5px;color:#fff}.weui_icon_toast{margin:22px 0 0;display:block}.weui_icon_toast:before{content:'\EA08';color:#fff;font-size:55px}.weui_toast_content{margin:0 0 15px}.weui_loading_toast .weui_toast_content{margin-top:64%;font-size:14px}.weui_loading{position:absolute;width:0;z-index:5;left:50%;top:38%}.weui_loading_leaf{position:absolute;top:-1px;opacity:.25}.weui_loading_leaf:before{content:" ";position:absolute;width:8.14px;height:3.08px;background:#d1d1d5;box-shadow:0 0 1px rgba(0,0,0,.0980392);border-radius:1px;-webkit-transform-origin:left 50% 0;transform-origin:left 50% 0}.weui_loading_leaf_0{-webkit-animation:a 1.25s linear infinite;animation:a 1.25s linear infinite}.weui_loading_leaf_0:before{-webkit-transform:rotate(0deg) translate(7.92px);transform:rotate(0deg) translate(7.92px)}.weui_loading_leaf_1{-webkit-animation:b 1.25s linear infinite;animation:b 1.25s linear infinite}.weui_loading_leaf_1:before{-webkit-transform:rotate(30deg) translate(7.92px);transform:rotate(30deg) translate(7.92px)}.weui_loading_leaf_2{-webkit-animation:c 1.25s linear infinite;animation:c 1.25s linear infinite}.weui_loading_leaf_2:before{-webkit-transform:rotate(60deg) translate(7.92px);transform:rotate(60deg) translate(7.92px)}.weui_loading_leaf_3{-webkit-animation:d 1.25s linear infinite;animation:d 1.25s linear infinite}.weui_loading_leaf_3:before{-webkit-transform:rotate(90deg) translate(7.92px);transform:rotate(90deg) translate(7.92px)}.weui_loading_leaf_4{-webkit-animation:e 1.25s linear infinite;animation:e 1.25s linear infinite}.weui_loading_leaf_4:before{-webkit-transform:rotate(120deg) translate(7.92px);transform:rotate(120deg) translate(7.92px)}.weui_loading_leaf_5{-webkit-animation:f 1.25s linear infinite;animation:f 1.25s linear infinite}.weui_loading_leaf_5:before{-webkit-transform:rotate(150deg) translate(7.92px);transform:rotate(150deg) translate(7.92px)}.weui_loading_leaf_6{-webkit-animation:g 1.25s linear infinite;animation:g 1.25s linear infinite}.weui_loading_leaf_6:before{-webkit-transform:rotate(180deg) translate(7.92px);transform:rotate(180deg) translate(7.92px)}.weui_loading_leaf_7{-webkit-animation:h 1.25s linear infinite;animation:h 1.25s linear infinite}.weui_loading_leaf_7:before{-webkit-transform:rotate(210deg) translate(7.92px);transform:rotate(210deg) translate(7.92px)}.weui_loading_leaf_8{-webkit-animation:i 1.25s linear infinite;animation:i 1.25s linear infinite}.weui_loading_leaf_8:before{-webkit-transform:rotate(240deg) translate(7.92px);transform:rotate(240deg) translate(7.92px)}.weui_loading_leaf_9{-webkit-animation:j 1.25s linear infinite;animation:j 1.25s linear infinite}.weui_loading_leaf_9:before{-webkit-transform:rotate(270deg) translate(7.92px);transform:rotate(270deg) translate(7.92px)}.weui_loading_leaf_10{-webkit-animation:k 1.25s linear infinite;animation:k 1.25s linear infinite}.weui_loading_leaf_10:before{-webkit-transform:rotate(300deg) translate(7.92px);transform:rotate(300deg) translate(7.92px)}.weui_loading_leaf_11{-webkit-animation:l 1.25s linear infinite;animation:l 1.25s linear infinite}.weui_loading_leaf_11:before{-webkit-transform:rotate(330deg) translate(7.92px);transform:rotate(330deg) translate(7.92px)}@-webkit-keyframes a{0%,0.01%{opacity:.25}0.02%{opacity:1}60.01%,to{opacity:.25}}@-webkit-keyframes b{0%,8.34333%{opacity:.25}8.35333%{opacity:1}68.3433%,to{opacity:.25}}@-webkit-keyframes c{0%,16.6767%{opacity:.25}16.6867%{opacity:1}76.6767%,to{opacity:.25}}@-webkit-keyframes d{0%,25.01%{opacity:.25}25.02%{opacity:1}85.01%,to{opacity:.25}}@-webkit-keyframes e{0%,33.3433%{opacity:.25}33.3533%{opacity:1}93.3433%,to{opacity:.25}}@-webkit-keyframes f{0%{opacity:.270958333333333}41.6767%{opacity:.25}41.6867%{opacity:1}1.67667%{opacity:.25}to{opacity:.270958333333333}}@-webkit-keyframes g{0%{opacity:.375125}50.01%{opacity:.25}50.02%{opacity:1}10.01%{opacity:.25}to{opacity:.375125}}@-webkit-keyframes h{0%{opacity:.479291666666667}58.3433%{opacity:.25}58.3533%{opacity:1}18.3433%{opacity:.25}to{opacity:.479291666666667}}@-webkit-keyframes i{0%{opacity:.583458333333333}66.6767%{opacity:.25}66.6867%{opacity:1}26.6767%{opacity:.25}to{opacity:.583458333333333}}@-webkit-keyframes j{0%{opacity:.687625}75.01%{opacity:.25}75.02%{opacity:1}35.01%{opacity:.25}to{opacity:.687625}}@-webkit-keyframes k{0%{opacity:.791791666666667}83.3433%{opacity:.25}83.3533%{opacity:1}43.3433%{opacity:.25}to{opacity:.791791666666667}}@-webkit-keyframes l{0%{opacity:.895958333333333}91.6767%{opacity:.25}91.6867%{opacity:1}51.6767%{opacity:.25}to{opacity:.895958333333333}}.weui_mask{background:rgba(0,0,0,.6)}.weui_mask,.weui_mask_transition,.weui_mask_transparent{position:fixed;z-index:1;width:100%;height:100%;top:0;left:0}.weui_mask_transition{display:none;background:transparent;-webkit-transition:background .3s;transition:background .3s}.weui_fade_toggle{background:rgba(0,0,0,.6)}.weui_actionsheet{position:fixed;left:0;bottom:0;-webkit-transform:translateY(100%);transform:translateY(100%);-webkit-backface-visibility:hidden;backface-visibility:hidden;z-index:2;width:100%;background-color:#efeff4;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s}.weui_actionsheet_menu{background-color:#fff}.weui_actionsheet_action{margin-top:6px;background-color:#fff}.weui_actionsheet_cell{position:relative;padding:10px 0;text-align:center;font-size:18px}.weui_actionsheet_cell:before{content:" ";position:absolute;left:0;top:0;width:100%;height:1px;border-top:1px solid #d9d9d9;color:#d9d9d9;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui_actionsheet_cell:active{background-color:#ececec}.weui_actionsheet_cell:first-child:before{display:none}.weui_actionsheet_toggle{-webkit-transform:translate(0);transform:translate(0)}.weui_search_bar{padding:8px 10px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;box-sizing:border-box;background-color:#efeff4;z-index:2}.weui_search_bar:before{top:0;border-top:1px solid #c7c7c7;-webkit-transform-origin:0 0;transform-origin:0 0}.weui_search_bar:after,.weui_search_bar:before{content:" ";position:absolute;left:0;width:100%;height:1px;color:#c7c7c7;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.weui_search_bar:after{bottom:0;border-bottom:1px solid #c7c7c7;-webkit-transform-origin:0 100%;transform-origin:0 100%}.weui_search_bar.weui_search_focusing .weui_search_cancel{display:block}.weui_search_bar.weui_search_focusing .weui_search_text{display:none}.weui_search_outer{position:relative;-webkit-box-flex:1;-webkit-flex:auto;-ms-flex:auto;flex:auto;background-color:#efeff4}.weui_search_outer:after{content:'';position:absolute;left:0;top:0;width:200%;height:200%;-webkit-transform:scale(.5);transform:scale(.5);-webkit-transform-origin:0 0;transform-origin:0 0;border-radius:10px;border:1px solid #e6e6ea;box-sizing:border-box;background:#fff}.weui_search_inner{position:relative;padding-left:30px;padding-right:30px;height:100%;width:100%;box-sizing:border-box;z-index:1}.weui_search_inner .weui_search_input{padding:4px 0;width:100%;height:1.42857143em;border:0;font-size:14px;line-height:1.42857143em;box-sizing:content-box;background:transparent}.weui_search_inner .weui_search_input:focus{outline:none}.weui_search_inner .weui_icon_search{position:absolute;left:10px;top:-2px;line-height:28px}.weui_search_inner .weui_icon_clear{position:absolute;top:-2px;right:0;padding:0 10px;line-height:28px}.weui_search_text{position:absolute;top:1px;right:1px;bottom:1px;left:1px;z-index:2;border-radius:3px;text-align:center;color:#9b9b9b;background:#fff}.weui_search_text span{display:inline-block;font-size:14px;vertical-align:middle}.weui_search_text .weui_icon_search{margin-right:5px}.weui_search_cancel{display:none;margin-left:10px;line-height:28px;color:#09bb07}.weui_search_input:not(:valid)~.weui_icon_clear{display:none}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration,input[type=search]::-webkit-search-results-button,input[type=search]::-webkit-search-results-decoration{display:none} --------------------------------------------------------------------------------