├── .gitignore ├── CHANGELOG.md ├── README.md ├── Screenshots ├── v0.1.0.jpg ├── v0.2.0.jpg ├── v0.3.0.jpg ├── v0.4.0.jpg ├── v0.5.0.jpg ├── v0.6.0.jpg ├── v1.0.0.jpg ├── v1.1.0.jpg ├── v1.2.0.jpg └── v1.3.0.jpg ├── app.js ├── app.json ├── app.wxss ├── component ├── schedule.js ├── schedule.json ├── schedule.wxml ├── schedule.wxs ├── schedule.wxss ├── schoolTime.js ├── schoolTime.json ├── schoolTime.wxml └── schoolTime.wxss ├── config-sample.js ├── images ├── about.jpg ├── avatar.png ├── bg.png ├── icon_arrow_down.svg ├── icon_edit.svg ├── icon_help.svg ├── service_bookshelf.svg ├── service_lightbulb.svg ├── service_money.svg ├── tabbar_me.png ├── tabbar_me_hl.png ├── tabbar_schedule.png ├── tabbar_schedule_hl.png ├── tabbar_services.png └── tabbar_services_hl.png ├── pages ├── card │ ├── index.js │ ├── index.json │ ├── index.wxml │ ├── index.wxss │ ├── record.js │ ├── record.json │ ├── record.wxml │ └── record.wxss ├── cet │ ├── ticket.js │ ├── ticket.json │ ├── ticket.wxml │ └── ticket.wxss ├── common │ ├── about.js │ ├── about.json │ ├── about.wxml │ ├── about.wxss │ ├── bind.js │ ├── bind.json │ ├── bind.wxml │ ├── bind.wxs │ ├── bind.wxss │ ├── guide.js │ ├── guide.json │ ├── guide.wxml │ ├── guide.wxss │ ├── setting.js │ ├── setting.json │ ├── setting.wxml │ ├── setting.wxss │ ├── tpl.wxml │ ├── tpl.wxss │ ├── webview.js │ ├── webview.json │ ├── webview.wxml │ └── webview.wxss ├── edu │ ├── schedule │ │ ├── custom.js │ │ ├── custom.json │ │ ├── custom.wxml │ │ ├── custom.wxss │ │ ├── update.js │ │ ├── update.json │ │ ├── update.wxml │ │ └── update.wxss │ ├── score.js │ ├── score.json │ ├── score.wxml │ └── score.wxss ├── elec │ ├── deposit.js │ ├── deposit.json │ ├── deposit.wxml │ ├── deposit.wxs │ ├── deposit.wxss │ ├── index.js │ ├── index.json │ ├── index.wxml │ ├── index.wxss │ ├── record.js │ ├── record.json │ ├── record.wxml │ ├── record.wxs │ ├── record.wxss │ ├── setting.js │ ├── setting.json │ ├── setting.wxml │ └── setting.wxss ├── opac │ ├── book.js │ ├── book.json │ ├── book.wxml │ ├── book.wxss │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss └── tabbar │ ├── discover.js │ ├── discover.json │ ├── discover.wxml │ ├── discover.wxss │ ├── me.js │ ├── me.json │ ├── me.wxml │ ├── me.wxss │ ├── schedule.js │ ├── schedule.json │ ├── schedule.wxml │ └── schedule.wxss ├── project.config.json ├── services ├── card.js ├── edu.js ├── elec.js ├── index.js ├── opac.js └── user.js └── utils ├── api.js ├── event.js ├── init.js ├── libs ├── countup.js ├── es6-promise.js ├── object-path.js ├── regenerator-runtime.js └── text-encoder-lite.js ├── locales ├── en_US.js ├── index.js └── zh_CN.js ├── request.js ├── tip.js └── util.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | 45 | # 以下是人工添加的内容 46 | config.js 47 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### v1.3.5 (2019-06-22) 2 | 3 | - **改善** 学期时间的计算 4 | 5 | ### v1.3.4 (2019-04-04) 6 | 7 | - **修复** 连上课程的判断 8 | - **改善** 小程序升级后强制刷新课表 9 | - **改善** 对不支持查询电费余量的宿舍的友好提示 10 | 11 | ### v1.3.3 (2019-03-27) 12 | 13 | - **新增** 隐藏自定义课程 14 | - **修复** 更新课表后不显示自定义课表 15 | - **修复** 同时间段连上课程的判断 16 | - **修复** 连上课程被其他课程遮住 17 | - **改善** 添加自定义课程时可直接设置持续节数 18 | - **改善** 支持 Face ID 19 | - **改善** 页面样式适配 iPhone 全面屏设备 20 | - **改善** iOS 设备上下拉页面时的窗口背景色 21 | - **改善** 书目检索封面懒加载 22 | - **改善** 移动赞赏入口 23 | - **改善** 细节调整 24 | 25 | ### v1.3.2 (2019-01-06) 26 | 27 | - **修复** 继教同学无法获取课表 28 | - **改善** 尚无课表时的提示 29 | 30 | ### v1.3.1 (2019-01-05) 31 | 32 | - **新增** 点亮课表 33 | - **修复** 课表头栏宽不一致 34 | 35 | ### v1.3.0 (2018-11-03) 36 | 37 | - **新增** 自定义课表 38 | - **改善** 课表的提示与指南 39 | - **改善** 降低免上课程的透明度 40 | - **改善** 开发模式下不上报错误 41 | 42 | ### v1.2.2 (2018-10-12) 43 | 44 | - **修复** 书目检索图书信息页链接地址复制无效 45 | - **修复** 生物认证接口在 FaceID 设备上的兼容 46 | - **修复** 生物认证接口出错时没有正确回退到密码认证 47 | - **改善** 生物认证接口的检测 48 | 49 | ### v1.2.1 (2018-10-06) 50 | 51 | - **修复** 学号长度限制放宽 52 | - **改善** 绑定文案 53 | - **改善** 错误上报的内容 54 | 55 | ### v1.2.0 (2018-10-05) 56 | 57 | - **新增** 语言设置 58 | - **新增** 教学日历(外链版) 59 | - **修复** 无课表数据时渲染课表错误 60 | - **修复** 获取不到页面元素高度时报错 61 | - **改善** 学号长度限制放宽 62 | - **改善** 错误上报的步骤和内容 63 | - **改善** 读取本地数据时不覆盖预设数据 64 | 65 | ### v1.1.4 (2018-09-22) 66 | 67 | - **修复** 不存在指定宿舍时多余的错误弹窗 68 | - **修复** 某些情况下渲染课表的错误 69 | - **改善** 过滤不必要的错误 70 | - **改善** 宿舍电费可用楼栋 71 | 72 | ### v1.1.3 (2018-09-09) 73 | 74 | - **修复** 无法查看第六大节课程的详情 75 | 76 | ### v1.1.2 (2018-09-09) 77 | 78 | - **新增** 错误警报机制 79 | - **修复** 周数为0时的错误判断 80 | 81 | ### v1.1.1 (2018-09-06) 82 | 83 | - **修复** 处于第0周时无法获取课表 84 | - **修复** 非期末时期获取课表页的学期值错误 85 | 86 | ### v1.1.0 (2018-08-20) 87 | 88 | - **新增** CET 准考证 89 | - **修复** 兼容教师帐户获取学年学期 90 | - **修复** 无网络状态下获取课表时显示两次错误弹窗 91 | - **改善** 细节优化 92 | 93 | ### v1.0.3 (2018-07-11) 94 | 95 | - **改善** 支持绑定教师帐户 96 | - **改善** 未绑定状态下可以使用宿舍电费的部分模块 97 | - **改善** 加快提示框的显示 98 | 99 | ### v1.0.2 (2018-07-08) 100 | 101 | - **新增** 微信登录 102 | - **新增** 可以选择隐藏非本周课程 103 | - **改善** 宿舍设置提示 104 | - **改善** 调整课程块背景颜色 105 | - **改善** 取消体育课的独立课程时间表 106 | - **改善** 更改赞赏位置 107 | 108 | ### v1.0.1 (2018-06-30) 109 | 110 | - **新增** 支持获取指定学期的课表 111 | - **新增** 公告模块 112 | - **改善** 课程成绩各科目结果增加绩点项 113 | - **改善** 细节优化 114 | 115 | ### v1.0.0 (2018-06-24) 116 | 117 | - 重构与优化 118 | - **新增** 课表页显示日期 119 | - **新增** 分享 120 | 121 | ### v0.6.7 (2018-04-22) 122 | 123 | - **新增** 电费充值支持指纹验证身份 124 | 125 | ### v0.6.6 (2018-04-21) 126 | 127 | - **改善** 调整用户微信资料的获取和授权操作 128 | 129 | ### v0.6.5 (2018-04-08) 130 | 131 | - **改善** 调整文案 132 | 133 | ### v0.6.4 (2018-04-06) 134 | 135 | - **修复** 课表为空时切换周数导致的错误 136 | - **改善** 调整引导文案 137 | - **改善** 获取用户信息时不携带登录态信息 138 | 139 | ### v0.6.3 (2018-04-01) 140 | 141 | - **新增** 课表周数切换 142 | 143 | ### v0.6.2 (2018-03-28) 144 | 145 | - **修复** iOS 不显示课表背景 146 | - **改善** 调整 iOS 上下拖动窗口时的背景颜色 147 | 148 | ### v0.6.1 (2018-03-27) 149 | 150 | - **新增** 课表背景设置 151 | - **新增** 新版特性提示 152 | 153 | ### v0.6.0 (2018-03-26) 154 | 155 | - **新增** 书目查询 156 | 157 | ### v0.5.1 (2018-03-07) 158 | 159 | - **改善** 显示课程时间 160 | - **改善** 调整课程块背景颜色 161 | 162 | ### v0.5.0 (2017-12-14) 163 | 164 | - **新增** 课程成绩查询 165 | 166 | ### v0.4.5 (2017-11-19) 167 | 168 | - **修复** 用户微信资料不能及时更新 169 | - **改善** 将「服务」页入口从点击头像改为独立的跳转按钮 170 | 171 | ### v0.4.4 (2017-11-17) 172 | 173 | - **改善** 不再自动跳转到「我的」 174 | - **新增** 使用引导页 175 | 176 | ### v0.4.3 (2017-11-12) 177 | 178 | - **修复** 属性「scope.userInfo」未定义 179 | - **修复** 「event」的一些未定义错误 180 | - **改善** 不选择房间的话无法使用电费服务 181 | 182 | ### v0.4.2 (2017-10-25) 183 | 184 | - **修复** 跟在连上课程后的普通课程受其影响而隐藏 185 | - **修复** 没有注销掉之前订阅的事件 186 | - **修复** 电费记录页面一个不知道是什么也不知道是如何产生的Bug 187 | - **更新** 支持第11-12小节课程 188 | - **更新** 新的课程块背景颜色 189 | - **更新** 第一次使用会有绑定服务的提示 190 | 191 | ### v0.4.1 (2017-10-18) 192 | 193 | - **修复** 购电成功时消息提示框一闪而过 194 | - **修复** 判断网络请求结果时的逻辑错误 195 | 196 | ### v0.4.0 (2017-10-16) 197 | 198 | - **修复** 第9-11小节课程块样式错误 199 | - **修复** 未绑定教务系统但显示了设置入口 200 | - **更新** 优化同一时间课程的显示方案 201 | - **更新** 优化网络请求的处理方案 202 | - **更新** 优化了部分样式和逻辑 203 | - **新增** 宿舍电费 204 | 205 | ### v0.3.1 (2017-09-04) 206 | 207 | - **修复** 「周几」标题错误 208 | - **修复** 没课的时候不显示「周几」 209 | - **修复** 用户状态更改时界面刷新出错 210 | - **更新** Tab栏改用原生上边框 211 | 212 | ### v0.3.0 (2017-09-03) 213 | 214 | - **更新** 修改获取用户信息的方案 215 | - **更新** 修改课表解析与渲染的方案 216 | - **更新** 取消隐藏因单双周而不用上的课程 217 | - **更新** 反馈途径更改为客服会话 218 | - **更新** 修改课表的存储格式 219 | - **更新** 优化了外观样式 220 | - **更新** 重构了程序 221 | 222 | ### v0.2.2 (2017-07-14) 223 | 224 | - **更新** 改进对连上课程的处理 225 | - **更新** 不再支持特殊课程 226 | 227 | ### v0.2.1 (2017-07-07) 228 | 229 | - **修复** 未在教学周期内时课程不显示 230 | - **修复** 存在连上的课程时导致课表无法显示 231 | 232 | ### v0.2.0 (2017-06-11) 233 | 234 | - **更新** 优化了外观样式 235 | - **更新** 重构了程序 236 | - **新增** 校园卡服务 237 | - **新增** 获取用户微信资料,显示头像和昵称 238 | - **新增** 多语言,当前支持简体中文和英文 239 | 240 | ### v0.1.0 (2017-04-01) 241 | 242 | - 项目发布 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 微吉风 2 | 3 | > 今天再大的事,到了明天就是小事;今年再大的事,到了明年就是故事。 4 | 5 | 实际效果可在微信中搜索同名小程序体验。 6 | 7 | 8 | ## 功能 9 | 10 | - [x] 课表 11 | - [x] 背景图 12 | - [x] 日期 13 | - [x] 自定义 14 | - [ ] 无课表 15 | - [ ] 云查询 16 | - [x] 校园卡 17 | - [x] 余额 18 | - [x] 消费记录 19 | - [x] 宿舍电费 20 | - [x] 余量 21 | - [x] 用电记录 22 | - [x] 购电记录 23 | - [ ] 余量提醒 24 | - [x] 教务成绩 25 | - [x] 教学日历(外链版) 26 | - [x] 图书馆 27 | - [x] 书目检索 28 | - [ ] 个人中心 29 | - [x] 公告 30 | - [ ] 体验帐号 31 | - [ ] 校园运动圈(?) 32 | - [ ] 电话本 33 | - [ ] 留言板(?) 34 | - [ ] 接入统一认证 35 | 36 | 什么时候会弃坑呢? 37 | 38 | 39 | ## 使用 40 | 41 | 将 `config-sample.js` 重命名为 `config.js`,即可在微信开发者工具中打开。 42 | 43 | 部分可能用得到的文档参见 [Wiki](https://github.com/choyri/WeGifun/wiki)。 44 | 45 | 46 | ## 开源类库 47 | 48 | 位于 [/utils/libs/](https://github.com/choyri/WeGifun/tree/master/utils/libs)。 49 | 50 | - [countup.js](https://github.com/wux-weapp/wux-weapp/blob/master/src/countup/index.js) 51 | - [es6-promise.js](https://github.com/stefanpenner/es6-promise) 52 | - [object-path.js](https://github.com/mariocasciaro/object-path) 53 | - [regenerator-runtime.js](https://github.com/facebook/regenerator/blob/master/packages/regenerator-runtime/runtime.js) 54 | - [text-encoder-lite.js](https://github.com/coolaj86/TextEncoderLite/blob/master/text-encoder-lite.js) 55 | 56 | 57 | ## 交流 58 | 59 | 欢迎加入 QQ 群:`window.atob('NjM2NTkzNzA1')`。 60 | 61 | 62 | ## 赞赏 63 | 64 | 愿意请我喝奶茶吗? :) 65 | 66 | 67 | 68 | 69 | ## 许可 70 | 71 | [WTFPL](https://zh.wikipedia.org/wiki/WTFPL) 72 | -------------------------------------------------------------------------------- /Screenshots/v0.1.0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/Screenshots/v0.1.0.jpg -------------------------------------------------------------------------------- /Screenshots/v0.2.0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/Screenshots/v0.2.0.jpg -------------------------------------------------------------------------------- /Screenshots/v0.3.0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/Screenshots/v0.3.0.jpg -------------------------------------------------------------------------------- /Screenshots/v0.4.0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/Screenshots/v0.4.0.jpg -------------------------------------------------------------------------------- /Screenshots/v0.5.0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/Screenshots/v0.5.0.jpg -------------------------------------------------------------------------------- /Screenshots/v0.6.0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/Screenshots/v0.6.0.jpg -------------------------------------------------------------------------------- /Screenshots/v1.0.0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/Screenshots/v1.0.0.jpg -------------------------------------------------------------------------------- /Screenshots/v1.1.0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/Screenshots/v1.1.0.jpg -------------------------------------------------------------------------------- /Screenshots/v1.2.0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/Screenshots/v1.2.0.jpg -------------------------------------------------------------------------------- /Screenshots/v1.3.0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/Screenshots/v1.3.0.jpg -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | import regeneratorRuntime from './utils/libs/regenerator-runtime' 2 | 3 | import config from './config'; 4 | import './utils/init' 5 | 6 | let appParams = {} 7 | 8 | appParams.onLaunch = async function (options) { 9 | if (options.query && options.query.dev) { 10 | console.log('真机预览模式') 11 | wx.ooCache.dev = true 12 | } 13 | 14 | for (const key of wx.getStorageInfoSync().keys) { 15 | const data = wx.getStorageSync(key) 16 | wx.ooCache[key] = !wx.ooUtil.isObject(data) ? data : Object.assign({}, wx.ooCache[key] || {}, data) 17 | } 18 | 19 | console.info('缓存', wx.ooCache) 20 | 21 | // 先检查更新 有新特性就弹窗 22 | await this.checkUpdate() 23 | 24 | // 然后再判断是否需要引导 25 | wx.ooTip.guide() 26 | 27 | // 最后再做其他事 28 | 29 | this.checkSoter() 30 | 31 | this.getNotice() 32 | 33 | this.wechatLogin() 34 | 35 | wx.ooCache.launched = true 36 | } 37 | 38 | appParams.onError = function (error) { 39 | if (wx.ooCache.systemInfo.platform === 'devtools') { 40 | return 41 | } 42 | 43 | // 过滤奇怪的错误 44 | if (error.indexOf('webview') !== -1 || error.indexOf('appServiceSDKScriptError') !== -1) { 45 | return 46 | } 47 | 48 | let content = [ 49 | `用户:${wx.ooCache.user && wx.ooCache.user.id} / ${wx.ooCache.openID}`, 50 | `版本:${wx.ooCache.version}`, 51 | `系统信息:${JSON.stringify(wx.ooCache.systemInfo)}`, 52 | `错误堆栈:${error}`, 53 | ] 54 | 55 | if (error.indexOf('schedule') !== -1) { 56 | content.push(`课程信息:${JSON.stringify(wx.ooCache.schedule)}`) 57 | } 58 | 59 | if (error.indexOf('split') !== -1) { 60 | content.push(`教务数据:${JSON.stringify(wx.ooCache.edu)}`) 61 | } 62 | 63 | wx.ooRequest.triggerAlarm(content.join('\n\n')) 64 | } 65 | 66 | appParams.checkUpdate = async function () { 67 | // 基础库 1.9.90 开始支持 68 | if (wx.getUpdateManager) { 69 | const updateManager = wx.getUpdateManager() 70 | 71 | updateManager.onUpdateReady(async function () { 72 | const modalRes = await wx.ooShowModal({ content: wx.ooString.global.updateManager }) 73 | 74 | if (modalRes.confirm) { 75 | updateManager.applyUpdate() 76 | } 77 | }) 78 | } 79 | 80 | if (wx.ooCache.version === config.version) { 81 | return 82 | } 83 | 84 | console.log('已更新到新版', config.version) 85 | 86 | // 新用户不显示新版特性 因此先判断缓存里是否存在版本项 87 | if (wx.ooCache.version && wx.ooString.feature) { 88 | await wx.ooShowModal({ 89 | title: wx.ooString.global.new_feature, 90 | content: wx.ooString.feature, 91 | }, false) 92 | } 93 | 94 | wx.ooCache.updateSucceed = true 95 | 96 | wx.ooSaveData({ version: config.version }) 97 | } 98 | 99 | appParams.checkSoter = async function () { 100 | const now = (new Date()).getTime() 101 | 102 | if (wx.ooCache.supportSoter !== undefined && wx.ooCache.supportSoterUpdatedAt && wx.ooCache.supportSoterUpdatedAt + 86400000 > now) { 103 | return 104 | } 105 | 106 | const isSupportSoter = async () => { 107 | let flag = false 108 | const is_iOS = wx.ooCache.systemInfo.system.toLowerCase().indexOf('ios') !== -1, 109 | isVersionSeven = wx.ooCache.systemInfo.version.startsWith('7.'), 110 | hasFaceIdOfIphone = wx.ooCache.systemInfo.model.toLowerCase().indexOf('iphone x') !== -1 111 | 112 | if (is_iOS) { 113 | // http://t.cn/EJ4o0kR 114 | if (isVersionSeven) { 115 | const data = await wx.ooIsSoterEnrolled('facial').then(ret => ret) 116 | if (data.isEnrolled || false) { 117 | return true 118 | } 119 | } 120 | 121 | if (hasFaceIdOfIphone) { 122 | return false 123 | } 124 | } 125 | 126 | const data = await wx.ooIsSoterEnrolled('fingerPrint').catch(ret => ret) 127 | return data.isEnrolled || false 128 | } 129 | 130 | wx.ooSaveData({ 131 | supportSoter: await isSupportSoter(), 132 | supportSoterUpdatedAt: now, 133 | }) 134 | } 135 | 136 | appParams.getNotice = async function () { 137 | wx.ooCache.notice = await wx.ooRequest.getNotice() 138 | } 139 | 140 | appParams.wechatLogin = async function () { 141 | const user = wx.ooService.user.getAccount() 142 | 143 | if (!user.id || wx.ooCache.openID) { 144 | return 145 | } 146 | 147 | const loginRet = await wx.ooPro.login() 148 | 149 | const openID = await wx.ooRequest.wechatLogin([loginRet.code, user.id]) 150 | wx.ooSaveData({ openID }) 151 | } 152 | 153 | App(appParams) 154 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/tabbar/schedule", 4 | "pages/tabbar/discover", 5 | "pages/tabbar/me", 6 | 7 | "pages/common/bind", 8 | "pages/common/setting", 9 | "pages/common/guide", 10 | "pages/common/about", 11 | "pages/common/webview", 12 | 13 | "pages/edu/schedule/update", 14 | "pages/edu/schedule/custom", 15 | "pages/edu/score", 16 | 17 | "pages/card/index", 18 | "pages/card/record", 19 | 20 | "pages/elec/index", 21 | "pages/elec/deposit", 22 | "pages/elec/record", 23 | "pages/elec/setting", 24 | 25 | "pages/opac/index", 26 | "pages/opac/book", 27 | 28 | "pages/cet/ticket" 29 | ], 30 | "window": 31 | { 32 | "navigationBarBackgroundColor": "#fff", 33 | "navigationBarTitleText": "微吉风", 34 | "navigationBarTextStyle": "black" 35 | }, 36 | "tabBar": 37 | { 38 | "color": "#999", 39 | "selectedColor": "#303030", 40 | "backgroundColor": "#fcfcfc", 41 | "borderStyle": "black", 42 | "list": [ 43 | { 44 | "pagePath": "pages/tabbar/schedule", 45 | "text": "课表", 46 | "iconPath": "images/tabbar_schedule.png", 47 | "selectedIconPath": "images/tabbar_schedule_hl.png" 48 | }, 49 | { 50 | "pagePath": "pages/tabbar/discover", 51 | "text": "发现", 52 | "iconPath": "/images/tabbar_services.png", 53 | "selectedIconPath": "images/tabbar_services_hl.png" 54 | }, 55 | { 56 | "pagePath": "pages/tabbar/me", 57 | "text": "我", 58 | "iconPath": "images/tabbar_me.png", 59 | "selectedIconPath": "images/tabbar_me_hl.png" 60 | }] 61 | }, 62 | "networkTimeout": 63 | { 64 | "request": 5000 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app.wxss: -------------------------------------------------------------------------------- 1 | page { 2 | /* 移动端字体设置 http://t.cn/Rad9zxW */ 3 | font-family: -apple-system, BlinkMacSystemFont, PingFang, 'Helvetica Neue', STHeiti, Tahoma, sans-serif; 4 | font-size: 32rpx; 5 | 6 | background-color: #fff; 7 | } 8 | 9 | page::before, 10 | ._page::before { 11 | position: fixed; 12 | z-index: 9999; 13 | top: -1px; 14 | 15 | width: 100%; 16 | height: 1rpx; 17 | 18 | content: ''; 19 | 20 | box-shadow: 0 1px 4px 0 rgba(0, 0, 0, .37); 21 | } 22 | 23 | button::after { 24 | /* 去除原生按钮边框 */ 25 | border: none; 26 | } 27 | 28 | input { 29 | height: 2em; 30 | } 31 | 32 | /* 默认 hover 样式 */ 33 | .navigator-hover, 34 | .view-hover, 35 | .oo-cell-hover { 36 | transition: all .2s; 37 | 38 | background-color: #d9d9d9 !important; 39 | } 40 | 41 | .oo-btn { 42 | margin: 3em 15px 1.17647059em; 43 | 44 | color: #fff; 45 | border-radius: 4px; 46 | background-color: #4285f4; 47 | box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.12), 0 1px 5px 0 rgba(0,0,0,.2); 48 | } 49 | 50 | /* 重写 button 默认 hover 样式 # 该样式得放在 oo-btn 后面 要不然无效 */ 51 | .button-hover { 52 | transition: all .2s; 53 | 54 | box-shadow: 0 8px 10px 1px rgba(0,0,0,.14), 0 3px 14px 2px rgba(0,0,0,.12), 0 5px 5px -3px rgba(0,0,0,.2); 55 | } 56 | 57 | .oo-btn.common { 58 | color: #000; 59 | background-color: #eee; 60 | } 61 | 62 | .oo-btn + .oo-btn.common { 63 | margin-top: 1.17647059em; 64 | } 65 | 66 | .oo-cells { 67 | line-height: 1.41176471; 68 | 69 | position: relative; 70 | 71 | margin-top: 1.17647059em; 72 | 73 | background-color: #fff; 74 | } 75 | 76 | .oo-label + .oo-cells { 77 | margin-top: 0; 78 | } 79 | 80 | .oo-cells:last-child { 81 | margin-bottom: 1.17647059em; 82 | } 83 | 84 | @supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) { 85 | .oo-cells:last-child { 86 | margin-bottom: constant(safe-area-inset-bottom); 87 | margin-bottom: env(safe-area-inset-bottom); 88 | } 89 | } 90 | 91 | .oo-cells-title { 92 | font-size: 28rpx; 93 | 94 | display: block; 95 | 96 | margin-top:.77em; 97 | margin-bottom:.3em; 98 | padding: 0 15px; 99 | 100 | color: #999; 101 | } 102 | 103 | .oo-cells-after-title { 104 | margin-top: 0; 105 | } 106 | 107 | .oo-cell { 108 | position: relative; 109 | 110 | display: flex; 111 | 112 | padding: 12px 15px; 113 | 114 | align-items: center; 115 | justify-content:space-between; 116 | } 117 | 118 | .oo-cell.thin { 119 | padding: 5px 15px; 120 | } 121 | 122 | .oo-cell::before { 123 | position: absolute; 124 | top: 0; 125 | right: 15px; 126 | left: 15px; 127 | 128 | content: ''; 129 | transform-origin: top; 130 | 131 | border-top: 1rpx solid #d9d9d9; 132 | } 133 | 134 | .oo-cell:first-child::before { 135 | display: none; 136 | } 137 | 138 | @media (min-resolution: 2dppx) { 139 | .oo-cell::before { 140 | transform: scaleY(.5); 141 | } 142 | } 143 | 144 | @media (min-resolution: 3dppx) { 145 | .oo-cell::before { 146 | transform: scaleY(.333333); 147 | } 148 | } 149 | 150 | .oo-cell.button { 151 | font-size: 32rpx; 152 | line-height: 1.41176471; 153 | 154 | border-radius: 0; 155 | background-color: transparent; 156 | } 157 | 158 | .oo-cell.button::after { 159 | display: none; 160 | } 161 | 162 | .oo-cell switch { 163 | position: absolute; 164 | right: 15px; 165 | } 166 | 167 | .oo-cell-label { 168 | width: 5em; 169 | } 170 | 171 | .oo-cell-edge { 172 | color: #999; 173 | } 174 | 175 | .oo-code { 176 | font-size: 90%; 177 | 178 | margin-right: 4px; 179 | padding: 2px 4px; 180 | 181 | word-break: break-all; 182 | 183 | color: #c7254e; 184 | border-radius: 4px; 185 | background-color: #f9f2f4; 186 | } 187 | 188 | .oo-footer { 189 | font-size: 24rpx; 190 | 191 | text-align: center; 192 | 193 | color: #999; 194 | } 195 | 196 | .oo-footer.fixed { 197 | position: fixed; 198 | bottom: .52em; 199 | 200 | width: 100%; 201 | } 202 | 203 | @supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) { 204 | .oo-footer.fixed { 205 | bottom: constant(safe-area-inset-bottom); 206 | bottom: env(safe-area-inset-bottom); 207 | } 208 | } 209 | 210 | .oo-footer.clickable { 211 | color: #586C94; 212 | } 213 | 214 | .oo-icon { 215 | width: 48rpx; 216 | height: 48rpx; 217 | } 218 | 219 | .oo-label { 220 | font-size: 28rpx; 221 | 222 | display: block; 223 | 224 | margin-top: 1em; 225 | margin-bottom: .3em; 226 | padding: 0 15px; 227 | 228 | color: #999; 229 | } 230 | 231 | .oo-loading { 232 | width: 100vw; 233 | height: 100vh; 234 | } 235 | 236 | .oo-record { 237 | display: flex; 238 | flex-direction: column; 239 | 240 | margin-top: 1.17647059em; 241 | padding: 5px 15px; 242 | 243 | background-color: #fff; 244 | } 245 | 246 | .oo-record:last-child { 247 | margin-bottom: 1.17647059em; 248 | } 249 | 250 | @supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) { 251 | .oo-record:last-child { 252 | margin-bottom: constant(safe-area-inset-bottom); 253 | margin-bottom: env(safe-area-inset-bottom); 254 | } 255 | } 256 | 257 | .oo-record > view { 258 | display: flex; 259 | 260 | padding: 5px 0; 261 | 262 | justify-content: space-between; 263 | } 264 | 265 | .oo-record .head { 266 | font-size: 36rpx; 267 | 268 | position: relative; 269 | 270 | margin-bottom: 5px; 271 | padding: 10px 0; 272 | } 273 | 274 | .oo-record .head::after { 275 | position: absolute; 276 | bottom: 0; 277 | 278 | width: 100%; 279 | 280 | content: ''; 281 | transform-origin: bottom; 282 | 283 | border-bottom: 1rpx dashed #d9d9d9; 284 | } 285 | 286 | .oo-record text:first-child { 287 | text-align: justify; 288 | text-align-last: justify; 289 | 290 | flex: 0 0 3em; 291 | } 292 | 293 | .oo-section { 294 | font-size: 32rpx; 295 | 296 | padding: 20px 15px; 297 | } 298 | 299 | .oo-section-h1, 300 | .oo-section-h2 { 301 | font-weight: bold; 302 | } 303 | 304 | .oo-section-h1 { 305 | font-size: 36rpx; 306 | 307 | display: block; 308 | 309 | margin-bottom: .8em; 310 | padding-bottom: .3em; 311 | 312 | border-bottom: 1rpx solid #eee; 313 | } 314 | 315 | .oo-section-h2 { 316 | font-size: 34rpx; 317 | 318 | margin-bottom: .5em; 319 | } 320 | 321 | .oo-section-p { 322 | line-height: 1.8em; 323 | 324 | display: block; 325 | 326 | margin-bottom: .8em; 327 | 328 | text-align: justify; 329 | } 330 | 331 | .oo-section-p:last-child { 332 | margin-bottom: 0; 333 | } 334 | 335 | .oo-select { 336 | line-height: 2em; 337 | 338 | position: relative; 339 | 340 | display: flex; 341 | 342 | margin: 0 15px; 343 | 344 | justify-content: space-between; 345 | } 346 | 347 | .oo-select.horizontal { 348 | text-align: center; 349 | } 350 | 351 | .oo-select-item { 352 | flex: 0 0 40%; 353 | } 354 | 355 | .oo-select-separator { 356 | flex: 0 0 20%; 357 | } 358 | 359 | .oo-shadow { 360 | border-radius: 8px; 361 | box-shadow: 0 1px 6px rgba(32, 33, 36, .28); 362 | } 363 | 364 | .oo-table { 365 | flex: auto; 366 | } 367 | 368 | .oo-table .tr { 369 | display: flex; 370 | 371 | padding: .5em 0; 372 | 373 | align-items: center; 374 | } 375 | 376 | .oo-table .tr:nth-child(odd) { 377 | background-color: #f3f3f7; 378 | } 379 | 380 | .oo-table .td { 381 | line-height: normal; 382 | 383 | display: flex; 384 | flex-direction: column; 385 | 386 | text-align: center; 387 | 388 | flex: auto; 389 | } 390 | 391 | .oo-tips { 392 | font-size: 28rpx; 393 | line-height: 1.2em; 394 | 395 | display: block; 396 | 397 | margin-top: .3em; 398 | padding: 0 15px; 399 | 400 | color: #999; 401 | } 402 | 403 | .oo-tips.center { 404 | margin-top: 1.17647059em; 405 | 406 | text-align: center; 407 | } 408 | 409 | .oo-underline::after { 410 | position: absolute; 411 | bottom: 0; 412 | left: 0; 413 | 414 | width: 100%; 415 | 416 | content: ''; 417 | transform-origin: bottom; 418 | 419 | border-bottom: 1rpx solid #d9d9d9; 420 | } 421 | 422 | @media (min-resolution: 2dppx) { 423 | .oo-underline::after { 424 | transform: scaleY(.5); 425 | } 426 | } 427 | 428 | @media (min-resolution: 3dppx) { 429 | .oo-underline::after { 430 | transform: scaleY(.333333); 431 | } 432 | } 433 | 434 | .service-index { 435 | margin-top: 2em; 436 | 437 | text-align: center; 438 | } 439 | 440 | .service-index image { 441 | width: 250rpx; 442 | height: 250rpx; 443 | 444 | vertical-align: top; 445 | } 446 | 447 | .service-index text { 448 | display: block; 449 | } 450 | 451 | .service-index .result { 452 | font-size: 72rpx; 453 | line-height: 1.4em; 454 | } 455 | -------------------------------------------------------------------------------- /component/schedule.js: -------------------------------------------------------------------------------- 1 | import regeneratorRuntime from '../utils/libs/regenerator-runtime' 2 | 3 | const _string = wx.ooString.component_schedule 4 | 5 | const renderCurrWeek = (value = 0) => _string.currWeek.replace('{0}', value) 6 | 7 | const getWeekTitle = (offsetWeeks = 0) => { 8 | const getOffsetDate = (date, offsetDays) => { 9 | return offsetDays ? new Date(date.getTime() + 86400000 * offsetDays) : date 10 | } 11 | 12 | const getDateOfWeek = (offsetWeeks = 0) => { 13 | const currDate = new Date() 14 | 15 | // 周日的值是 0 16 | const currDay = currDate.getDay() || 7 17 | 18 | // 将当前日期的星期减掉一 再乘以负一 即当周的第一天 19 | let firstDate = getOffsetDate(currDate, (currDay - 1) * -1) 20 | 21 | // 周数偏移 22 | firstDate = getOffsetDate(firstDate, 7 * offsetWeeks) 23 | 24 | let res = [] 25 | 26 | for (let i = 0; i < 7; i++) { 27 | let date = getOffsetDate(firstDate, i), 28 | currMonth = date.getMonth() 29 | 30 | res.push({ 31 | date: date.getDate(), 32 | month: currMonth, 33 | }) 34 | } 35 | 36 | return res 37 | } 38 | 39 | return getDateOfWeek(offsetWeeks).map((obj, index) => { 40 | obj.name = _string.weekName[index] 41 | return obj 42 | }) 43 | } 44 | 45 | let componentParams = { 46 | data: { 47 | currWeek: renderCurrWeek(), 48 | weekTitle: getWeekTitle(), 49 | }, 50 | methods: {}, 51 | properties: { 52 | bg: Object, 53 | showDate: { 54 | type: Boolean, 55 | value: true, 56 | }, 57 | value: { 58 | type: Array, 59 | observer: '_scheduleObserver', 60 | }, 61 | }, 62 | } 63 | 64 | let _tmp = {} 65 | 66 | componentParams.attached = function () { 67 | wx.ooEvent.on('updateCurrWeek', this._updateCurrWeek, this) 68 | this._updateCurrWeek() 69 | } 70 | 71 | componentParams.methods.renderPage = function () { 72 | this._updateCurrWeek(_tmp.currWeek) 73 | 74 | this.setData({ 75 | value: wx.ooService.edu.renderSchedule(null, _tmp.currWeek), 76 | weekTitle: getWeekTitle(_tmp.currWeek - _tmp.orginalCurrWeek), 77 | }) 78 | } 79 | 80 | componentParams.methods.showDetail = function (e) { 81 | const dataSet = e.currentTarget.dataset 82 | let course 83 | 84 | try { 85 | course = this.data.value[dataSet.day].data[dataSet.section].data[dataSet.course].data 86 | } catch (err) { 87 | console.log('无此课程', dataSet) 88 | return 89 | } 90 | 91 | const timeTable = wx.ooService.edu.getTimeTable(course) 92 | 93 | const weekState = [ '', ` # ${_string.detail_week[1]}`, ` # ${_string.detail_week[2]}` ], 94 | weekRange = course.week.split(':'), 95 | week = _string.detail_week[0].replace('{0}', `${weekRange[0]}-${weekRange[1]}`) + weekState[weekRange[2]] 96 | 97 | wx.ooShowModal({ 98 | content: [course.name, course.teacher, course.location, week, timeTable].join(' / '), 99 | }, false) 100 | 101 | wx.vibrateShort() 102 | } 103 | 104 | componentParams.methods.touchStart = function (e) { 105 | _tmp.touchStartX = e.touches[0].pageX 106 | _tmp.touchStartY = e.touches[0].pageY 107 | } 108 | 109 | componentParams.methods.touchMove = function (e) { 110 | _tmp.touchEndX = e.touches[0].pageX 111 | _tmp.touchEndY = e.touches[0].pageY 112 | } 113 | 114 | componentParams.methods.touchEnd = async function (e) { 115 | if (!this.data.value || !_tmp.touchEndX || !_tmp.touchEndY) { 116 | return 117 | } 118 | 119 | const x = _tmp.touchEndX - _tmp.touchStartX, 120 | y = _tmp.touchEndY - _tmp.touchStartY, 121 | widthRange = (wx.ooCache.systemInfo.screenWidth || 375) / 3 122 | 123 | if (Math.abs(x) < Math.abs(y) || Math.abs(x) < widthRange) { 124 | return 125 | } 126 | 127 | if (x > 0 && _tmp.currWeek === 1 || x < 0 && _tmp.currWeek === 20) { 128 | wx.ooShowToast({ title: _string.at_border }) 129 | return 130 | } 131 | 132 | if (_tmp.currWeek < 0) { 133 | _tmp.currWeek = 1 134 | } else { 135 | _tmp.currWeek += x < 0 ? 1 : -1 136 | } 137 | 138 | _tmp.touchEndX = _tmp.touchEndY = undefined 139 | 140 | this.renderPage() 141 | } 142 | 143 | componentParams.methods.tap = function (e) { 144 | const lastTapTimestamp = (_tmp.tapTimetamp = _tmp.tapTimetamp || 0) 145 | 146 | _tmp.tapTimetamp = e.timeStamp 147 | 148 | if (e.timeStamp - lastTapTimestamp > 250) { 149 | return 150 | } 151 | 152 | console.log('触发双击') 153 | 154 | if (_tmp.currWeek !== _tmp.orginalCurrWeek) { 155 | _tmp.currWeek = _tmp.orginalCurrWeek 156 | this.renderPage() 157 | } 158 | } 159 | 160 | componentParams.methods.longPress = function () { 161 | console.log('触发长按') 162 | 163 | wx.vibrateLong() 164 | wx.navigateTo({ url: '/pages/edu/schedule/custom' }) 165 | } 166 | 167 | componentParams.methods._scheduleObserver = function (newValue) { 168 | let flag, 169 | weekTitle = wx.ooUtil.copy(this.data.weekTitle) 170 | 171 | // 隐藏没课的周末 172 | for (let i = 5; i < 7; i++) { 173 | const _state = weekTitle[i].hidden 174 | 175 | weekTitle[i].hidden = !newValue[i] 176 | 177 | flag = flag || weekTitle[i].hidden !== _state 178 | } 179 | 180 | if (flag) { 181 | this.setData({ weekTitle }) 182 | } 183 | } 184 | 185 | componentParams.methods._updateCurrWeek = async function (currWeek) { 186 | if (!currWeek) { 187 | _tmp.orginalCurrWeek = currWeek = await wx.ooService.edu.getCurrWeek() 188 | } 189 | 190 | _tmp.currWeek = currWeek 191 | 192 | this.setData({ 193 | currWeek: renderCurrWeek(currWeek), 194 | }) 195 | } 196 | 197 | Component(componentParams) 198 | -------------------------------------------------------------------------------- /component/schedule.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true 3 | } 4 | -------------------------------------------------------------------------------- /component/schedule.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{ currWeek }} 8 | 9 | 10 | 13 | {{ item.name }} 14 | {{ item.date }} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 1 27 | 2 28 | 29 | 30 | 3 31 | 4 32 | 33 | 34 | 5 35 | 6 36 | 37 | 38 | 7 39 | 8 40 | 41 | 42 | 9 43 | 10 44 | 45 | 46 | 11 47 | 12 48 | 49 | 50 | 51 | 52 | 55 | 56 | 58 | 59 | 64 | {{ course.data.name }} 65 | {{ course.data.location }} 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /component/schedule.wxs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | isTransparent: function (bg) { 3 | return bg ? 'transparent' : '' 4 | }, 5 | 6 | isWeekTitleActive: function (target) { 7 | var date = getDate() 8 | return date.getDate() === target.date && date.getMonth() === target.month ? 'highlight' : '' 9 | }, 10 | 11 | renderBg: function (type) { 12 | if (type === 1) { 13 | return 'mask' 14 | } else if (type === 2) { 15 | return 'blur' 16 | } 17 | return '' 18 | }, 19 | 20 | renderCourse: function (course) { 21 | var ret = '' 22 | if (course.bg) { 23 | ret += 'background-color: ' + course.bg + ';' 24 | } else { 25 | ret += 'opacity: .5;' 26 | } 27 | if (course.height !== undefined) { 28 | ret += 'height: ' + course.height + 'rpx;' 29 | } 30 | if (course.top !== undefined) { 31 | ret += 'margin-top: ' + course.top + 'rpx;' 32 | } 33 | return ret 34 | }, 35 | 36 | renderDate: function (flag) { 37 | return flag ? 'with-date' : '' 38 | }, 39 | 40 | renderSection: function (sectionIndex) { 41 | return 'top: ' + sectionIndex * 210 + 'rpx;' 42 | }, 43 | } 44 | -------------------------------------------------------------------------------- /component/schedule.wxss: -------------------------------------------------------------------------------- 1 | .bg { 2 | position: fixed; 3 | z-index: -1; 4 | 5 | width: 100vw; 6 | height: 100vh; 7 | } 8 | 9 | .bg.mask { 10 | opacity: .3; 11 | } 12 | 13 | .bg.blur { 14 | filter: blur(3px); 15 | } 16 | 17 | .edge { 18 | font-size: 28rpx; 19 | 20 | text-align: center; 21 | 22 | color: #999; 23 | background-color: #fff; 24 | } 25 | 26 | .head { 27 | position: fixed; 28 | z-index: 100; 29 | top: -1rpx; 30 | 31 | display: flex; 32 | 33 | width: 100%; 34 | 35 | box-shadow: 0 2rpx 12rpx rgba(32, 33, 36, .28); 36 | } 37 | 38 | .curr-week { 39 | display: flex; 40 | 41 | align-items: center; 42 | justify-content: center; 43 | } 44 | 45 | .curr-week, 46 | .sidebar { 47 | width: 40rpx; 48 | } 49 | 50 | .curr-week-value { 51 | line-height: 30rpx; 52 | 53 | display: block; 54 | } 55 | 56 | .curr-week-value.with-date { 57 | line-height: 40rpx; 58 | } 59 | 60 | .week-title { 61 | display: flex; 62 | 63 | flex: auto; 64 | } 65 | 66 | .week-title-item { 67 | line-height: 60rpx; 68 | 69 | display: flex; 70 | flex-direction: column; 71 | 72 | flex: 1; 73 | } 74 | 75 | .week-title-item.with-date { 76 | padding: 10rpx 0 10rpx; 77 | } 78 | 79 | .week-title-item.with-date > .name { 80 | line-height: 40rpx; 81 | } 82 | 83 | .week-title-item > .date { 84 | font-size: 24rpx; 85 | line-height: 30rpx; 86 | } 87 | 88 | .week-title-item.highlight { 89 | color: #3367d6; 90 | } 91 | 92 | .body { 93 | display: flex; 94 | 95 | -webkit-overflow-scrolling: touch; 96 | } 97 | 98 | .body-item { 99 | padding: 70rpx 0 10rpx; 100 | } 101 | 102 | .body.with-date > .body-item { 103 | padding: 100rpx 0 10rpx; 104 | } 105 | 106 | .sidebar { 107 | display: flex; 108 | flex-direction: column; 109 | 110 | flex: none; 111 | } 112 | 113 | .sidebar-bg { 114 | position: fixed; 115 | z-index: -1; 116 | top: -50%; 117 | left: 0; 118 | 119 | width: 40rpx; 120 | height: 200%; 121 | } 122 | 123 | .sidebar-item { 124 | display: flex; 125 | flex-direction: column; 126 | 127 | height: 200rpx; 128 | margin-bottom: 10rpx; 129 | 130 | justify-content: space-around; 131 | } 132 | 133 | .sidebar-item.last { 134 | margin-bottom: 0; 135 | } 136 | 137 | .content { 138 | display: flex; 139 | 140 | flex: auto; 141 | } 142 | 143 | .content-column { 144 | position: relative; 145 | 146 | display: flex; 147 | flex-direction: column; 148 | 149 | margin: 0 6rpx; 150 | 151 | flex: auto; 152 | } 153 | 154 | .course-wrap { 155 | position: absolute; 156 | 157 | overflow: hidden; 158 | 159 | width: 100%; 160 | } 161 | 162 | .course { 163 | display: flex; 164 | flex-direction: column; 165 | 166 | box-sizing: border-box; 167 | height: 200rpx; 168 | padding: 2rpx; 169 | 170 | text-align: center; 171 | 172 | color: #fff; 173 | background-color: #b2b2b2; 174 | 175 | justify-content: space-around; 176 | } 177 | 178 | .course.transparent { 179 | opacity: .8; 180 | } 181 | 182 | .course > .value { 183 | font-size: 28rpx; 184 | line-height: 32rpx; 185 | 186 | display: -webkit-box; 187 | overflow: hidden; 188 | 189 | -webkit-box-orient: vertical; 190 | -webkit-line-clamp: 3; 191 | /* 多行溢出省略号 http://t.cn/zWUS0VF */ 192 | } 193 | -------------------------------------------------------------------------------- /component/schoolTime.js: -------------------------------------------------------------------------------- 1 | import regeneratorRuntime from '../utils/libs/regenerator-runtime' 2 | 3 | const EMOJI = ['😀', '😏', '😪', '😐', '😶'], 4 | PICKER_RANGE = [wx.ooString.component_schoolTime.grade, wx.ooString.component_schoolTime.semester] 5 | 6 | const renderSchoolTime = (grade, semester) => `${PICKER_RANGE[0][grade - 1]} ${PICKER_RANGE[1][semester - 1]}` 7 | 8 | let componentParams = { 9 | data: { 10 | picker: { 11 | range: PICKER_RANGE, 12 | value: [0, 0], 13 | }, 14 | _string: wx.ooString.component_schoolTime, 15 | }, 16 | methods: {}, 17 | properties: { 18 | forSchedule: { 19 | type: Boolean, 20 | value: false, 21 | }, 22 | }, 23 | } 24 | 25 | componentParams.attached = function () { 26 | const { grade, semester, gradeUpper } = wx.ooService.edu.getSchoolTime(this.data.forSchedule) 27 | 28 | let picker = wx.ooUtil.copy(this.data.picker) 29 | 30 | // 隐藏未达到的高年级 31 | picker.range[0] = picker.range[0].slice(0, gradeUpper) 32 | 33 | picker.value = [grade - 1, semester - 1] 34 | 35 | this._ooSetData({ 36 | picker, 37 | schoolTime: renderSchoolTime(grade, semester), 38 | }) 39 | 40 | this._ooTriggerEvent(picker.value) 41 | } 42 | 43 | componentParams.methods.bindPickerChange = function (e) { 44 | console.log('picker 新值为', e.detail.value) 45 | 46 | const [gradeIndex, semesterIndex] = e.detail.value 47 | 48 | this.setData({ 49 | schoolTime: renderSchoolTime(gradeIndex + 1, semesterIndex + 1), 50 | }) 51 | 52 | this._ooTriggerEvent(e.detail.value) 53 | } 54 | 55 | componentParams.methods._ooSetData = function (obj) { 56 | for (let key in obj) { 57 | // 同步更改 componentParams 里对应的值 这样重新打开本页时就可以保持最新状态 58 | wx.ooObjectPath.set(componentParams, `data.${key}`, obj[key]) 59 | 60 | this.setData({ [key]: obj[key] }) 61 | } 62 | } 63 | 64 | componentParams.methods._ooTriggerEvent = function (data) { 65 | this.triggerEvent('change', { 66 | value: { 67 | grade: data[0] + 1, 68 | semester: data[1] + 1, 69 | title: `${EMOJI[data[0]]} ${this.data.schoolTime}`, 70 | }, 71 | }) 72 | } 73 | 74 | Component(componentParams) 75 | -------------------------------------------------------------------------------- /component/schoolTime.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true 3 | } 4 | -------------------------------------------------------------------------------- /component/schoolTime.wxml: -------------------------------------------------------------------------------- 1 | {{ _string.label }} 2 | 3 | 5 | {{ schoolTime }} 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /component/schoolTime.wxss: -------------------------------------------------------------------------------- 1 | .label { 2 | font-size: 28rpx; 3 | 4 | display: block; 5 | 6 | margin-top: 1em; 7 | margin-bottom: .3em; 8 | padding: 0 15px; 9 | 10 | color: #999; 11 | } 12 | 13 | .select { 14 | line-height: 2.6em; 15 | 16 | position: relative; 17 | 18 | margin: 0 15px; 19 | } 20 | 21 | .select::after { 22 | position: absolute; 23 | bottom: 0; 24 | left: 0; 25 | 26 | width: 100%; 27 | 28 | content: ''; 29 | transform-origin: bottom; 30 | 31 | border-bottom: 1rpx solid #d9d9d9; 32 | } 33 | 34 | @media (min-resolution: 2dppx) { 35 | .select::after { 36 | transform: scaleY(.5); 37 | } 38 | } 39 | 40 | @media (min-resolution: 3dppx) { 41 | .select::after { 42 | transform: scaleY(.333333); 43 | } 44 | } 45 | 46 | .select-hover { 47 | transition: all .2s; 48 | 49 | background-color: #d9d9d9 !important; 50 | } 51 | 52 | .select-arrow { 53 | position: absolute; 54 | z-index: -1; 55 | top: 0; 56 | right: 0; 57 | bottom: 0; 58 | 59 | width: 48rpx; 60 | height: 48rpx; 61 | margin: auto; 62 | } 63 | -------------------------------------------------------------------------------- /config-sample.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | eduCalendarUrl: 'https://www.example.com/calendar', 3 | host: { 4 | dev: 'https://test.example.com', 5 | prod: 'https://www.example.com', 6 | }, 7 | qqGroup: '10000', 8 | rewardCodeImageUrl: 'https://www.example.com/a.jpg', 9 | shareImageUrl: 'https://www.example.com/b.jpg', 10 | shareTitle: [ 11 | '分享标题一', 12 | '分享标题二', 13 | ], 14 | version: 'v1.3.5 (190622)', 15 | } 16 | 17 | export default config 18 | -------------------------------------------------------------------------------- /images/about.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/images/about.jpg -------------------------------------------------------------------------------- /images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/images/avatar.png -------------------------------------------------------------------------------- /images/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/images/bg.png -------------------------------------------------------------------------------- /images/icon_arrow_down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /images/icon_edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /images/icon_help.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /images/service_bookshelf.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/service_lightbulb.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/service_money.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/tabbar_me.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/images/tabbar_me.png -------------------------------------------------------------------------------- /images/tabbar_me_hl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/images/tabbar_me_hl.png -------------------------------------------------------------------------------- /images/tabbar_schedule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/images/tabbar_schedule.png -------------------------------------------------------------------------------- /images/tabbar_schedule_hl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/images/tabbar_schedule_hl.png -------------------------------------------------------------------------------- /images/tabbar_services.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/images/tabbar_services.png -------------------------------------------------------------------------------- /images/tabbar_services_hl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/images/tabbar_services_hl.png -------------------------------------------------------------------------------- /pages/card/index.js: -------------------------------------------------------------------------------- 1 | import countUp from '../../utils/libs/countup' 2 | import regeneratorRuntime from '../../utils/libs/regenerator-runtime' 3 | 4 | let pageParams = { 5 | data: { 6 | balance: 'N/A', 7 | _string: wx.ooString.service_card, 8 | }, 9 | } 10 | 11 | pageParams.onLoad = async function () { 12 | const balance = await wx.ooService.card.fetchBalance() 13 | if (balance) { 14 | this.changeBalance(balance) 15 | } 16 | } 17 | 18 | pageParams.onReady = function () { 19 | wx.ooSetTitle(this.data._string.title) 20 | } 21 | 22 | pageParams.changeBalance = function (balance) { 23 | new countUp(0, balance, 2, .5, { 24 | printValue (balance) { 25 | this.setData({ balance: `¥${balance}` }) 26 | } 27 | }).start() 28 | } 29 | 30 | pageParams.navigateToRecord = function () { 31 | wx.navigateTo({ url: 'record' }) 32 | } 33 | 34 | Page(pageParams) 35 | -------------------------------------------------------------------------------- /pages/card/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "校园卡" 3 | } 4 | -------------------------------------------------------------------------------- /pages/card/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ _string.balance }} 4 | {{ balance }} 5 | 6 | 7 | 8 | 9 | {{ _string.witticism }} 10 | -------------------------------------------------------------------------------- /pages/card/index.wxss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/pages/card/index.wxss -------------------------------------------------------------------------------- /pages/card/record.js: -------------------------------------------------------------------------------- 1 | import regeneratorRuntime from '../../utils/libs/regenerator-runtime' 2 | 3 | let pageParams = { 4 | data: { 5 | picker: wx.ooService.card.getDatePicker(), 6 | _string: Object.assign({ 7 | btn: wx.ooString.global.btn_title, 8 | }, 9 | wx.ooString.service_card_record, 10 | ), 11 | }, 12 | } 13 | 14 | pageParams.onReady = function () { 15 | wx.ooSetTitle(this.data._string.title) 16 | } 17 | 18 | pageParams.bindClose = function () { 19 | this.setData({ showRecord: false }) 20 | } 21 | 22 | pageParams.bindDateChange = function (e) { 23 | // 结束日期选择器的开始日期 不能超过 开始日期选择器的结束日期 24 | 25 | let picker = wx.ooUtil.copy(this.data.picker) 26 | 27 | if (e.target.id === 'startPicker') { 28 | picker.from.value = picker.to.start = e.detail.value 29 | } else { 30 | picker.to.value = picker.from.end = e.detail.value 31 | } 32 | 33 | this.setData({ picker }) 34 | } 35 | 36 | pageParams.bindSubmit = async function () { 37 | const date = [ 38 | this.data.picker.from.value, 39 | this.data.picker.to.value, 40 | ] 41 | 42 | const recordData = await wx.ooService.card.fetchRecord(date) 43 | if (!recordData) { 44 | return 45 | } 46 | 47 | if (recordData.data.length === 0) { 48 | wx.ooShowToast({ title: this.data._string.noRecord }) 49 | return 50 | } 51 | 52 | const conclusion = this.data._string.result_conclusion 53 | .replace('{0}', recordData.data.length) 54 | .replace('{1}', recordData.amount) 55 | 56 | this.setData({ 57 | conclusion, 58 | recordData: recordData.data, 59 | showRecord: true, 60 | }) 61 | } 62 | 63 | Page(pageParams) 64 | -------------------------------------------------------------------------------- /pages/card/record.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "消费记录" 3 | } 4 | -------------------------------------------------------------------------------- /pages/card/record.wxml: -------------------------------------------------------------------------------- 1 | {{ _string.date_label }} 2 | 3 | 6 | {{ picker.from.value }} 7 | 8 | - 9 | 12 | {{ picker.to.value }} 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {{ conclusion }} 21 | 22 | 23 | 24 | 25 | {{ _string.result_label[0] }} 26 | ¥{{ item.money }} 27 | 28 | 29 | {{ _string.result_label[1] }} 30 | {{ item.area }} 31 | 32 | 33 | {{ _string.result_label[2] }} 34 | {{ item.platform }} 35 | 36 | 37 | {{ _string.result_label[3] }} 38 | {{ item.time }} 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /pages/card/record.wxss: -------------------------------------------------------------------------------- 1 | .record { 2 | position: fixed; 3 | top: 100%; 4 | left: 0; 5 | 6 | box-sizing: border-box; 7 | width: 100%; 8 | min-height: 100vh; 9 | padding: 15px; 10 | 11 | transition: all .8s cubic-bezier(.68, -.55, .27, 1.55); 12 | 13 | opacity: 0; 14 | background-color: #fff; 15 | 16 | -webkit-overflow-scrolling: touch; 17 | } 18 | 19 | @supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) { 20 | .record { 21 | padding-bottom: constant(safe-area-inset-bottom); 22 | padding-bottom: env(safe-area-inset-bottom); 23 | } 24 | } 25 | 26 | .record.show { 27 | position: absolute; 28 | top: 0; 29 | 30 | opacity: 1; 31 | } 32 | 33 | .record-title { 34 | display: flex; 35 | 36 | justify-content: space-between; 37 | } 38 | 39 | .oo-record { 40 | padding-top: 0; 41 | padding-bottom: 0; 42 | } 43 | 44 | .oo-record:last-child { 45 | margin-bottom: 0; 46 | } 47 | -------------------------------------------------------------------------------- /pages/cet/ticket.js: -------------------------------------------------------------------------------- 1 | import regeneratorRuntime from '../../utils/libs/regenerator-runtime' 2 | 3 | let pageParams = { 4 | data: { 5 | focusState: {}, 6 | user: {}, 7 | _string: Object.assign({ 8 | btn: wx.ooString.global.btn_title, 9 | }, 10 | wx.ooString.service_cet_ticket, 11 | ), 12 | }, 13 | _tmp: {}, 14 | } 15 | 16 | pageParams.onReady = function () { 17 | wx.ooSetTitle(this.data._string.title) 18 | wx.ooTip.cetFill() 19 | } 20 | 21 | pageParams.bindClearInput = function (e) { 22 | this.setData({ 23 | [`user.${e.currentTarget.dataset.target}`]: '', 24 | }) 25 | } 26 | 27 | pageParams.bindFocusBlur = function (e) { 28 | this.setData({ 29 | [`focusState.${e.currentTarget.id}`]: e.type === 'focus', 30 | }) 31 | } 32 | 33 | pageParams.bindHelp = function () { 34 | wx.ooShowModal({ 35 | content: wx.ooString.tip.cet_fill, 36 | }, false) 37 | } 38 | 39 | pageParams.bindInput = function (e) { 40 | this.setData({ 41 | [`user.${e.currentTarget.id}`]: e.detail.value, 42 | }) 43 | } 44 | 45 | pageParams.bindQuickFill = async function () { 46 | if (!wx.ooService.user.isBindEdu()) { 47 | wx.ooShowToast({ title: this.data._string.bind }) 48 | return 49 | } 50 | 51 | const ret = await wx.ooService.edu.fetchUserInfo() 52 | 53 | this.setData({ user: ret }) 54 | } 55 | 56 | pageParams.bindSubmit = async function () { 57 | const { idcard, name } = this.data.user 58 | 59 | if (!idcard || idcard.length !== 18 || !name || name.length < 2) { 60 | wx.ooShowToast({ title: this.data._string.error_input }) 61 | return 62 | } 63 | 64 | const ret = await wx.ooRequest.getCetTicket({ idcard, name }) 65 | if (!ret) { 66 | return 67 | } 68 | 69 | const content = this.data._string.result.replace('{0}', ret.id) 70 | await wx.ooShowModal({ content }, false) 71 | 72 | wx.setClipboardData({ data: ret.id }) 73 | } 74 | 75 | Page(pageParams) 76 | -------------------------------------------------------------------------------- /pages/cet/ticket.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "CET 准考证" 3 | } 4 | -------------------------------------------------------------------------------- /pages/cet/ticket.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ _string.page_title }} 4 | 5 | 6 | 8 | {{ _string.idcard }} 9 | 10 | 11 | 12 | 13 | 14 | 16 | {{ _string.name }} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /pages/cet/ticket.wxss: -------------------------------------------------------------------------------- 1 | @import '../common/bind.wxss'; 2 | 3 | .page_title { 4 | font-size:40rpx; 5 | 6 | display:block; 7 | 8 | margin: 30px 15px; 9 | } 10 | -------------------------------------------------------------------------------- /pages/common/about.js: -------------------------------------------------------------------------------- 1 | let pageParams = { 2 | data: { 3 | changelog: wx.ooString.changelog, 4 | qqGroup: wx.ooConfig.qqGroup, 5 | version: wx.ooConfig.version, 6 | _string: wx.ooString.common_about, 7 | }, 8 | _tmp: { 9 | devClickCount: 0, 10 | debugClickCount: 0, 11 | eggClickCount: 0, 12 | }, 13 | } 14 | 15 | pageParams.onReady = function () { 16 | wx.ooSetTitle(this.data._string.title) 17 | } 18 | 19 | pageParams.onPageScroll = function (e) { 20 | const { scrollTop } = e 21 | 22 | // 125 是头图的高度 # px 23 | let showPageShadow = scrollTop > 125 ? true : false 24 | 25 | if (showPageShadow !== this.data.showPageShadow) { 26 | this.setData({ showPageShadow }) 27 | } 28 | } 29 | 30 | pageParams.bindCopy = function (e) { 31 | wx.setClipboardData({ 32 | data: e.currentTarget.dataset.text || '', 33 | }) 34 | } 35 | 36 | pageParams.bindDebug = function () { 37 | if (++this._tmp.debugClickCount < 7) { 38 | return 39 | } 40 | 41 | if (this._tmp.debugClickCount === 7) { 42 | this._tmp.debugClickCount = 0 43 | const enableDebug = !wx.ooCache.enableDebug 44 | wx.setEnableDebug({ enableDebug }) 45 | wx.ooSaveData({ enableDebug }) 46 | } 47 | } 48 | 49 | pageParams.bindDev = function () { 50 | if (++this._tmp.devClickCount < 7) { 51 | return 52 | } 53 | 54 | if (this._tmp.devClickCount === 7) { 55 | this._tmp.devClickCount = 0 56 | const dev = !wx.ooCache.dev 57 | wx.ooSaveData({ dev }) 58 | wx.ooShowToast({ title: `${wx.ooString.global.dev} ${dev}` }) 59 | wx.vibrateLong() 60 | } 61 | } 62 | 63 | pageParams.bindEgg = function () { 64 | if (++this._tmp.eggClickCount < 5) { 65 | wx.vibrateShort() 66 | return 67 | } 68 | 69 | if (this._tmp.eggClickCount === 5) { 70 | wx.vibrateLong() 71 | this.setData({ showEgg: true }) 72 | } 73 | } 74 | 75 | pageParams.bindReward = function () { 76 | wx.previewImage({ 77 | urls: [wx.ooConfig.rewardCodeImageUrl] 78 | }) 79 | } 80 | 81 | Page(pageParams) 82 | -------------------------------------------------------------------------------- /pages/common/about.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "关于" 3 | } 4 | -------------------------------------------------------------------------------- /pages/common/about.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ _string.security }} 7 | {{ item }} 8 | 9 | 10 | 11 | {{ _string.changelog }} 12 | {{ item }} 13 | 14 | {{ _string.changelog_history }} 15 | http://url.cn/5JLgXbC 16 | 17 | 18 | 19 | 20 | {{ _string.open_source }} 21 | 22 | http://url.cn/5IJu6DP 23 | 24 | 25 | 26 | 27 | {{ _string.reward }} 28 | {{ _string.reward_content }} 29 | {{ _string.reward_ok }} 30 | 31 | 32 | 33 | {{ _string.communication }} 34 | 35 | {{ _string.communication_content }} 36 | {{ qqGroup }} 37 | 38 | 39 | 40 | 41 | 海畔吉风 42 | © 2019 菜头 {{ version }} 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /pages/common/about.wxss: -------------------------------------------------------------------------------- 1 | page::before { 2 | display: none; 3 | } 4 | 5 | ._page { 6 | transition: all .5s; 7 | 8 | opacity: 0; 9 | } 10 | 11 | ._page.show { 12 | opacity: 1; 13 | } 14 | 15 | .head-bg { 16 | width: 100%; 17 | height: 250rpx; 18 | 19 | vertical-align: bottom; 20 | } 21 | 22 | .changelog .oo-section-p { 23 | margin-bottom: .2em; 24 | } 25 | 26 | .oo-section-p.history { 27 | margin-top: .8em; 28 | } 29 | 30 | .oo-section-p.reward-ok { 31 | text-align: right; 32 | 33 | color: #4285f4; 34 | } 35 | 36 | text + .oo-code { 37 | margin-left: 4px; 38 | } 39 | 40 | .oo-footer { 41 | margin: 15px 15px 0; 42 | padding-bottom: 1.17647059em; 43 | } 44 | 45 | @supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) { 46 | .oo-footer { 47 | padding-bottom: constant(safe-area-inset-bottom); 48 | padding-bottom: env(safe-area-inset-bottom); 49 | } 50 | } 51 | 52 | .egg-bubbles { 53 | position: fixed; 54 | top: 0; 55 | left: 0; 56 | 57 | display: none; 58 | 59 | width: 100%; 60 | height: 100%; 61 | } 62 | 63 | .egg-bubbles.show { 64 | display: block; 65 | } 66 | 67 | .egg-bubbles view { 68 | font-size: 75rpx; 69 | line-height: 75rpx; 70 | 71 | position: absolute; 72 | bottom: -200rpx; 73 | 74 | width: 75rpx; 75 | height: 75rpx; 76 | 77 | animation: square 20s infinite; 78 | 79 | background-color: rgba(57, 58, 62, .15); 80 | } 81 | 82 | .egg-bubbles view:nth-child(1) { 83 | left: 5%; 84 | 85 | animation-delay: 2s; 86 | } 87 | 88 | .egg-bubbles view:nth-child(2) { 89 | left: 15%; 90 | 91 | width: 30rpx; 92 | height: 30rpx; 93 | 94 | animation-duration: 15s; 95 | } 96 | 97 | .egg-bubbles view:nth-child(3) { 98 | left: 25%; 99 | 100 | animation-duration: 25s; 101 | animation-delay: 6s; 102 | 103 | background-color: transparent; 104 | } 105 | 106 | .egg-bubbles view:nth-child(4) { 107 | left: 35%; 108 | 109 | width: 180rpx; 110 | height: 180rpx; 111 | 112 | animation-duration: 25s; 113 | animation-delay: 4s; 114 | 115 | background-color: rgba(57, 58, 62, .25); 116 | } 117 | 118 | .egg-bubbles view:nth-child(5) { 119 | left: 45%; 120 | } 121 | 122 | .egg-bubbles view:nth-child(6) { 123 | left: 55%; 124 | 125 | width: 110rpx; 126 | height: 110rpx; 127 | 128 | animation-delay: 6s; 129 | 130 | background-color: rgba(57, 58, 62, .2); 131 | } 132 | 133 | .egg-bubbles view:nth-child(7) { 134 | left: 65%; 135 | 136 | width: 120rpx; 137 | height: 120rpx; 138 | 139 | animation-duration: 30s; 140 | animation-delay: 15s; 141 | } 142 | 143 | .egg-bubbles view:nth-child(8) { 144 | font-size: 120rpx; 145 | line-height: 120rpx; 146 | 147 | left: 75%; 148 | 149 | width: 120rpx; 150 | height: 120rpx; 151 | 152 | animation-delay: 3s; 153 | 154 | background-color: transparent; 155 | } 156 | 157 | .egg-bubbles view:nth-child(9) { 158 | left: 85%; 159 | 160 | width: 90rpx; 161 | height: 90rpx; 162 | 163 | animation-duration: 35s; 164 | animation-delay: 2s; 165 | 166 | background-color: rgba(57, 58, 62, .3); 167 | } 168 | 169 | .egg-bubbles view:nth-child(10) { 170 | left: 95%; 171 | 172 | width: 30rpx; 173 | height: 30rpx; 174 | 175 | animation-delay: 10s; 176 | } 177 | 178 | @keyframes square { 179 | 0% { 180 | transform: translateY(0) rotate(0); 181 | } 182 | 100% { 183 | transform: translateY(-2000rpx) rotate(600deg); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /pages/common/bind.js: -------------------------------------------------------------------------------- 1 | import regeneratorRuntime from '../../utils/libs/regenerator-runtime' 2 | 3 | const userAccount = wx.ooService.user.getAccount() 4 | 5 | let pageParams = { 6 | data: { 7 | account: wx.ooService.user.getAccount(), 8 | editMode: wx.ooService.user.isLogin(), 9 | focusState: {}, 10 | _string: Object.assign({ 11 | btn: wx.ooString.global.btn_title, 12 | }, 13 | wx.ooString.common_bind, 14 | ), 15 | }, 16 | _tmp: { 17 | orginalInput: wx.ooService.user.getAccount(), 18 | }, 19 | } 20 | 21 | pageParams.onLoad = function () { 22 | if (wx.ooCache._logout) { 23 | this._processPageParams() 24 | this._tmp.orginalInput = wx.ooService.user.getAccount() 25 | this.setData({ 26 | account: wx.ooService.user.getAccount(), 27 | editMode: false, 28 | }) 29 | wx.ooCache._logout = null 30 | } 31 | } 32 | 33 | pageParams.onReady = async function () { 34 | wx.ooSetTitle(this.data._string.title) 35 | wx.ooTip.bind(this.data.editMode) 36 | } 37 | 38 | pageParams.bindFocusBlur = function (e) { 39 | this.setData({ 40 | [`focusState.${e.currentTarget.id}`]: e.type === 'focus', 41 | }) 42 | } 43 | 44 | pageParams.bindHelp = function () { 45 | wx.ooShowModal({ 46 | content: this.data.editMode ? wx.ooString.tip.bind_edit : wx.ooString.tip.bind_new, 47 | }, false) 48 | } 49 | 50 | pageParams.bindInput = function (e) { 51 | this.setData({ 52 | [`account.${e.currentTarget.id}`]: e.detail.value, 53 | }) 54 | } 55 | 56 | pageParams.bindSubmit = async function () { 57 | const { id, cardPwd, eduPwd } = this.data.account 58 | 59 | const isIdError = () => !id || id.length < 5 || id.length > 10 ? true : false, 60 | isPwdError = () => { 61 | const isCardPwdError = () => cardPwd && cardPwd.length !== 6 ? true : false, 62 | isEduPwdError = () => eduPwd && eduPwd.length < 6 ? true : false, 63 | isPwdEmpty = () => !cardPwd && !eduPwd ? true : false 64 | return isPwdEmpty() || isCardPwdError() || isEduPwdError() 65 | } 66 | 67 | if (isIdError()) { 68 | wx.ooShowToast({ title: this.data._string.error_id }) 69 | return 70 | } 71 | 72 | if (isPwdError()) { 73 | wx.ooShowToast({ title: this.data._string.error_pwd }) 74 | return 75 | } 76 | 77 | if (cardPwd && cardPwd !== this._tmp.orginalInput.cardPwd && !this._tmp.cardFlag) { 78 | console.log('一卡通鉴权开始') 79 | 80 | const authData = await wx.ooRequest.cardAuth(id, cardPwd) 81 | 82 | if (authData === false) { 83 | wx.ooShowToast({ title: this.data._string.error_card }) 84 | return 85 | } 86 | 87 | console.log('一卡通鉴权成功') 88 | wx.ooService.user.saveCardAccount(id, cardPwd) 89 | 90 | // 成功的话设置 flag 那么如果遇到其他密码错误 重试时就不会进入该鉴权 91 | this._tmp.cardFlag = true 92 | } 93 | 94 | if (eduPwd && eduPwd !== this._tmp.orginalInput.eduPwd && !this._tmp.eduFlag) { 95 | console.log('教务鉴权开始') 96 | 97 | const authData = await wx.ooRequest.eduAuth(id, eduPwd) 98 | 99 | if (authData === false) { 100 | wx.ooShowToast({ title: this.data._string.error_edu }) 101 | return 102 | } 103 | 104 | console.log('教务鉴权成功') 105 | wx.ooService.user.saveEduAccount(id, eduPwd) 106 | 107 | // 成功的话设置 flag 那么如果遇到其他密码错误 重试时就不会进入该鉴权 108 | this._tmp.eduFlag = true 109 | } 110 | 111 | if (!this._tmp.cardFlag && !this._tmp.eduFlag) { 112 | wx.ooShowToast({ title: this.data._string.unchange }) 113 | return 114 | } 115 | 116 | wx.ooEvent.emit('bindSuccess') 117 | this._processPageParams() 118 | 119 | // 如果是绑定教务 询问是否获取课表 120 | if (this._tmp.eduFlag) { 121 | const modalRes = await wx.ooShowModal({ content: this.data._string.get_schedule }) 122 | 123 | if (modalRes.confirm) { 124 | wx.ooService.edu.fetchSchedule() 125 | } 126 | } 127 | 128 | wx.navigateBack() 129 | } 130 | 131 | pageParams.clearInput = function (e) { 132 | this.setData({ 133 | [`account.${e.currentTarget.dataset.target}`]: '', 134 | }) 135 | } 136 | 137 | pageParams._processPageParams = function () { 138 | pageParams.data.account = wx.ooService.user.getAccount() 139 | pageParams.data.editMode = wx.ooService.user.isLogin() 140 | pageParams._tmp.orginalInput = wx.ooService.user.getAccount() 141 | } 142 | 143 | Page(pageParams) 144 | -------------------------------------------------------------------------------- /pages/common/bind.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "绑定" 3 | } 4 | -------------------------------------------------------------------------------- /pages/common/bind.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | {{ _string.id }} 8 | 9 | 10 | 11 | 12 | 13 | 15 | {{ _string.pwd_edu }} 16 | 17 | 18 | 19 | 20 | 21 | 23 | {{ _string.pwd_card }} 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /pages/common/bind.wxs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | hasValue: function (state) { 3 | return state ? 'value' : '' 4 | }, 5 | 6 | isDisabled: function (state) { 7 | return state ? 'disabled' : '' 8 | }, 9 | 10 | isFocus: function (state) { 11 | return state ? 'focus' : '' 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /pages/common/bind.wxss: -------------------------------------------------------------------------------- 1 | .input-wrap { 2 | position: relative; 3 | 4 | display: flex; 5 | 6 | margin: 30px 15px; 7 | padding-top: .5em; 8 | 9 | align-items: center; 10 | } 11 | 12 | .input-wrap::after { 13 | position: absolute; 14 | bottom: 0; 15 | left: 0; 16 | 17 | width: 100%; 18 | 19 | content: ''; 20 | transform-origin: bottom; 21 | 22 | border-bottom: 1rpx solid #d9d9d9; 23 | } 24 | 25 | @media (min-resolution: 2dppx) { 26 | .input-wrap::after { 27 | transform: scaleY(.5); 28 | } 29 | } 30 | 31 | @media (min-resolution: 3dppx) { 32 | .input-wrap::after { 33 | transform: scaleY(.333333); 34 | } 35 | } 36 | 37 | input { 38 | height: 2.6em; 39 | 40 | flex: auto; 41 | } 42 | 43 | input.disabled { 44 | opacity: .5; 45 | } 46 | 47 | .placeholder { 48 | position: absolute; 49 | left: 0; 50 | 51 | transition: all .2s cubic-bezier(.4, 0, .2, 1); 52 | transform-origin: bottom left; 53 | 54 | color: #757575; 55 | } 56 | 57 | .focus .placeholder, 58 | .value .placeholder { 59 | transform: scale(.75) translateY(-2.5em); 60 | } 61 | 62 | icon { 63 | opacity: 0; 64 | 65 | flex: 0 0 50rpx; 66 | } 67 | 68 | .input-wrap.focus.value icon { 69 | opacity: 1; 70 | } 71 | 72 | .underline { 73 | position: absolute; 74 | z-index: 1; 75 | bottom: 0; 76 | left: 0; 77 | 78 | width: 100%; 79 | height: 2px; 80 | 81 | transform: scaleX(0); 82 | animation: quantumWizPaperInputRemoveUnderline .2s cubic-bezier(.4, 0, .2, 1); 83 | 84 | background-color: #4285f4; 85 | } 86 | 87 | .focus .underline { 88 | transform: scaleX(1); 89 | animation: quantumWizPaperInputAddUnderline .2s cubic-bezier(.4, 0, .2, 1); 90 | } 91 | 92 | @keyframes quantumWizPaperInputAddUnderline { 93 | 0% { 94 | transform: scaleX(0) 95 | } 96 | 97 | to { 98 | transform: scaleX(1) 99 | } 100 | } 101 | 102 | @keyframes quantumWizPaperInputRemoveUnderline { 103 | 0% { 104 | transform: scaleX(1); 105 | 106 | opacity: 1 107 | } 108 | 109 | to { 110 | transform: scaleX(1); 111 | 112 | opacity: 0 113 | } 114 | } 115 | 116 | .help { 117 | display: flex; 118 | 119 | margin-right: 15px; 120 | margin-bottom: 5em; 121 | 122 | justify-content: flex-end; 123 | } 124 | -------------------------------------------------------------------------------- /pages/common/guide.js: -------------------------------------------------------------------------------- 1 | let pageParams = { 2 | data: { 3 | _string: wx.ooString.common_guide, 4 | }, 5 | } 6 | 7 | pageParams.onReady = function () { 8 | wx.ooSetTitle(this.data._string.title) 9 | } 10 | 11 | Page(pageParams) 12 | -------------------------------------------------------------------------------- /pages/common/guide.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "使用指南" 3 | } 4 | -------------------------------------------------------------------------------- /pages/common/guide.wxml: -------------------------------------------------------------------------------- 1 | 2 | {{ item }} 3 | 4 | -------------------------------------------------------------------------------- /pages/common/guide.wxss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/pages/common/guide.wxss -------------------------------------------------------------------------------- /pages/common/setting.js: -------------------------------------------------------------------------------- 1 | import regeneratorRuntime from '../../utils/libs/regenerator-runtime' 2 | 3 | let pageParams = { 4 | data: { 5 | setting: wx.ooCache.setting, 6 | scheduleBg: wx.ooService.edu.getScheduleBg(), 7 | _string: wx.ooString.common_setting, 8 | }, 9 | _tmp: {}, 10 | } 11 | 12 | pageParams.onLoad = function () { 13 | this._ooSetData({ 14 | isLogin: wx.ooService.user.isLogin(), 15 | }) 16 | } 17 | 18 | pageParams.onReady = function () { 19 | wx.ooSetTitle(this.data._string.title) 20 | } 21 | 22 | pageParams.onUnload = function () { 23 | if (!this._tmp.logout && this.data.setting.showScheduleBg && !wx.ooService.edu.getScheduleBg().path) { 24 | this._ooSetData({ 'setting.showScheduleBg': false }) 25 | this._updateSetting('showScheduleBg', false) 26 | wx.ooShowToast({ title: this.data._string.schedule_bg_exit }) 27 | } 28 | } 29 | 30 | pageParams.bindLogout = async function () { 31 | const modalRes = await wx.ooShowModal({ content: this.data._string.logout_tip }) 32 | 33 | if (modalRes.confirm) { 34 | this._tmp.logout = true 35 | wx.ooLogout() 36 | wx.navigateBack() 37 | } 38 | } 39 | 40 | pageParams.bindSetLanguage = async function () { 41 | const ret = await wx.ooPro.showActionSheet({ itemList: this.data._string.language_list }) 42 | const index = ret.tapIndex 43 | 44 | this._ooSetData({ 'setting.languageIndex': index }) 45 | this._updateSetting('languageIndex', index) 46 | 47 | wx.ooShowModal({ content: this.data._string.language_tip }, false) 48 | } 49 | 50 | pageParams.bindSetScheduleBg = async function (e) { 51 | const oldFilePath = this.data.scheduleBg.path 52 | 53 | let res = await wx.ooPro.chooseImage({ count: 1 }) 54 | res = await wx.ooPro.saveFile({ tempFilePath: res.tempFilePaths[0] }) 55 | 56 | this._ooSetData({ 'scheduleBg.path': res.savedFilePath }) 57 | wx.ooService.edu.saveScheduleBg({ path: res.savedFilePath }) 58 | 59 | wx.removeSavedFile({ filePath: oldFilePath }) 60 | } 61 | 62 | pageParams.bindSetScheduleBgStyle = async function () { 63 | const res = await wx.ooPro.showActionSheet({ itemList: this.data._string.schedule_bg_style }) 64 | 65 | this._ooSetData({ 'scheduleBg.style': res.tapIndex }) 66 | wx.ooService.edu.saveScheduleBg({ style: res.tapIndex }) 67 | } 68 | 69 | pageParams.bindSwitchScheduleBg = function (e) { 70 | const value = e.detail.value 71 | 72 | this._ooSetData({ 'setting.showScheduleBg': value }) 73 | this._updateSetting('showScheduleBg', value) 74 | } 75 | 76 | pageParams.bindSwitchScheduleDate = function (e) { 77 | const value = e.detail.value 78 | 79 | this._ooSetData({ 'setting.showScheduleDate': value }) 80 | this._updateSetting('showScheduleDate', value) 81 | } 82 | 83 | pageParams.bindSwitchScheduleHideCourse = function (e) { 84 | const value = e.detail.value 85 | 86 | this._ooSetData({ 'setting.hideCourse': value }) 87 | this._updateSetting('hideCourse', value) 88 | 89 | wx.ooService.edu.updateSchedule(true) 90 | } 91 | 92 | pageParams.bindSwitchScheduleHideCustomCourse = function (e) { 93 | const value = e.detail.value 94 | 95 | this._ooSetData({ 'setting.hideCustomCourse': value }) 96 | this._updateSetting('hideCustomCourse', value) 97 | 98 | wx.ooService.edu.updateSchedule(true) 99 | } 100 | 101 | pageParams.bindSwitchScheduleLightUp = function (e) { 102 | const value = e.detail.value 103 | 104 | this._ooSetData({ 'setting.lightUpSchedule': value }) 105 | this._updateSetting('lightUpSchedule', value) 106 | 107 | wx.ooService.edu.updateSchedule(true) 108 | } 109 | 110 | pageParams._ooSetData = function (obj) { 111 | for (let key in obj) { 112 | // 同步更改 pageParams 里对应的值 这样重新打开本页时就可以保持最新状态 113 | wx.ooObjectPath.set(pageParams, `data.${key}`, obj[key]) 114 | 115 | this.setData({ [key]: obj[key] }) 116 | } 117 | } 118 | 119 | pageParams._updateSetting = function (key, value) { 120 | wx.ooSaveData({ setting: { [key]: value } }) 121 | wx.ooEvent.emit('updateSetting', wx.ooCache.setting) 122 | } 123 | 124 | Page(pageParams) 125 | -------------------------------------------------------------------------------- /pages/common/setting.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "设置", 3 | "backgroundColor": "#ebebeb" 4 | } 5 | -------------------------------------------------------------------------------- /pages/common/setting.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ _string.schedule_date }} 4 | 5 | 6 | 7 | {{ _string.schedule_hide_course }} 8 | 9 | 10 | 11 | {{ _string.schedule_hide_custom_course }} 12 | 13 | 14 | 15 | {{ _string.schedule_light_up }} 16 | 17 | 18 | 19 | 20 | 21 | 22 | {{ _string.schedule_bg.title }} 23 | 24 | 25 | 26 | 27 | {{ _string.schedule_bg.choose }} 28 | 29 | 30 | 31 | {{ _string.schedule_bg.style }} 32 | {{ _string.schedule_bg_style[scheduleBg.style] }} 33 | 34 | 35 | 36 | 37 | 38 | 39 | {{ _string.language }} 40 | {{ _string.language_list[setting.languageIndex] }} 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 51 | 52 | {{ _string.logout }} 53 | 54 | 55 | -------------------------------------------------------------------------------- /pages/common/setting.wxss: -------------------------------------------------------------------------------- 1 | page { 2 | background-color: #ebebeb; 3 | } 4 | 5 | .preview-image { 6 | position: absolute; 7 | right: 15px; 8 | 9 | width: 2em; 10 | height: 2em; 11 | } 12 | 13 | .logout { 14 | color: #e64340; 15 | 16 | justify-content: center; 17 | } 18 | -------------------------------------------------------------------------------- /pages/common/tpl.wxml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 16 | -------------------------------------------------------------------------------- /pages/common/tpl.wxss: -------------------------------------------------------------------------------- 1 | .dorm-info { 2 | position: relative; 3 | 4 | display: flex; 5 | 6 | padding-right: 20px; 7 | 8 | align-items: center; 9 | flex: auto; 10 | justify-content: space-between; 11 | } 12 | 13 | .dorm-info::after { 14 | position: relative; 15 | position: absolute; 16 | top: -2px; 17 | top: 50%; 18 | right: 5px; 19 | 20 | display: inline-block; 21 | 22 | width: 6px; 23 | height: 6px; 24 | margin-top: -4px; 25 | 26 | content: ''; 27 | transform: matrix(.71, .71, -.71, .71, 0, 0); 28 | 29 | border-width: 2px 2px 0 0; 30 | border-style: solid; 31 | border-color: #bfbfbf; 32 | } 33 | 34 | .dorm-info .name { 35 | font-size: 48rpx; 36 | } 37 | 38 | .dorm-info .state { 39 | font-size: 26rpx; 40 | } 41 | 42 | .dorm-info .state.default { 43 | color: #4285f4; 44 | } 45 | 46 | .dorm-info .state.other { 47 | color: #e64340; 48 | } 49 | 50 | /********************************/ 51 | 52 | .gray { 53 | color: #ccc; 54 | } 55 | -------------------------------------------------------------------------------- /pages/common/webview.js: -------------------------------------------------------------------------------- 1 | let pageParams = {} 2 | 3 | pageParams.onLoad = function (options) { 4 | this.setData({ 5 | src: options.src || '', 6 | }) 7 | } 8 | 9 | Page(pageParams) 10 | -------------------------------------------------------------------------------- /pages/common/webview.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /pages/common/webview.wxml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /pages/common/webview.wxss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/pages/common/webview.wxss -------------------------------------------------------------------------------- /pages/edu/schedule/custom.js: -------------------------------------------------------------------------------- 1 | import regeneratorRuntime from '../../../utils/libs/regenerator-runtime' 2 | 3 | const weekNum = [...Array(19).keys()].slice(1), 4 | _string = wx.ooString.service_edu_schedule_custom 5 | 6 | let pageParams = { 7 | data: { 8 | hideSectionPicker: true, 9 | disableList: true, 10 | sectionPicker: { 11 | range: _string.form_duration_list, 12 | value: 0, 13 | }, 14 | timePicker: { 15 | range: [ 16 | wx.ooString.component_schedule.weekName, 17 | _string.form_section_list, 18 | _string.form_section_half_list, 19 | ], 20 | value: [0, 0, 0], 21 | }, 22 | weekPicker: { 23 | range: [weekNum, weekNum, _string.form_week_list], 24 | value: [0, 15, 0], 25 | }, 26 | _string: Object.assign({ 27 | btn: wx.ooString.global.btn_title, 28 | }, 29 | wx.ooString.service_edu_schedule_custom, 30 | ), 31 | }, 32 | _tmp: { 33 | ret: {}, 34 | duration: 0, 35 | }, 36 | } 37 | 38 | pageParams.onLoad = function () { 39 | const originCustomSchedule = [...(wx.ooCache.edu && wx.ooCache.edu.schedule_custom || [])] 40 | 41 | this.setData({ 42 | originCustomSchedule, 43 | disableList: originCustomSchedule.length === 0, 44 | sectionPickerText: this.data.sectionPicker.range[this.data.sectionPicker.value] 45 | }) 46 | } 47 | 48 | pageParams.onReady = function () { 49 | wx.ooSetTitle(this.data._string.title) 50 | } 51 | 52 | pageParams.bindTriggerList = function () { 53 | this.setData({ showList: !(this.data.showList || false) }) 54 | } 55 | 56 | pageParams.bindDeleteCourse = async function (e) { 57 | const modalRet = await wx.ooShowModal({ content: this.data._string.delete_confirm }) 58 | 59 | if (modalRet.cancel) { 60 | return 61 | } 62 | 63 | const { id } = e.currentTarget.dataset 64 | 65 | let schedule = wx.ooUtil.copy(this.data.originCustomSchedule) 66 | schedule.splice(id, 1) 67 | 68 | this.setData({ originCustomSchedule: schedule }, () => this.save()) 69 | } 70 | 71 | pageParams.bindSubmit = async function () { 72 | // name, teacher, location, week, day, section, offset 73 | const { name, teacher, location } = this._tmp.ret 74 | 75 | if (Object.keys(this._tmp.ret).length !== 7 || !name || !teacher || !location) { 76 | wx.ooShowToast({ title: _string.error_incomplete }) 77 | return 78 | } 79 | 80 | let newCourse = wx.ooUtil.copy(this._tmp.ret) 81 | 82 | if (newCourse.offset === 0) { 83 | wx.ooObjectPath.del(newCourse, 'offset') 84 | } 85 | 86 | const originSchedule = wx.ooService.edu._getSchoolSchedule() || [] 87 | 88 | const eq = (_old, _new) => { 89 | return _old.week === _new.week && _old.day === _new.day && _old.section === _new.section 90 | } 91 | 92 | for (const course of originSchedule) { 93 | if (eq(course, newCourse)) { 94 | wx.ooShowToast({ title: _string.error_repeat }) 95 | return 96 | } 97 | } 98 | 99 | let courses = [] 100 | 101 | newCourse.random = Math.random() 102 | courses.push(newCourse) 103 | 104 | for (let i = 1; i < this._tmp.duration; i++) { 105 | let course = wx.ooUtil.copy(newCourse) 106 | course.section = course.section + i 107 | course.random = Math.random() 108 | courses.push(course) 109 | } 110 | 111 | this.save(courses) 112 | 113 | wx.ooShowToast({ title: this.data._string.success }) 114 | await wx.ooSleep(1500) 115 | wx.switchTab({ url: '/pages/tabbar/schedule' }) 116 | } 117 | 118 | pageParams.bindInputBlur = function (e) { 119 | const type = e.currentTarget.dataset.type, 120 | value = e.detail.value 121 | 122 | this._tmp.ret[type] = value 123 | } 124 | 125 | pageParams.bindTimePickerChange = function (e) { 126 | console.log('picker 新值为', e.detail.value) 127 | 128 | const [dayIndex, sectionIndex, halfIndex] = e.detail.value 129 | 130 | let timePickerText = [ 131 | this.data.timePicker.range[0][dayIndex], 132 | this.data.timePicker.range[1][sectionIndex], 133 | ] 134 | 135 | let hideSectionPicker = false 136 | 137 | if (halfIndex !== 0) { 138 | hideSectionPicker = true 139 | timePickerText.push(this.data.timePicker.range[2][halfIndex]) 140 | } else { 141 | this._tmp.duration = this._tmp.duration || 1 142 | 143 | // 根据当前选中的节数次序更新持续节数中的可选项 144 | let sectionPicker = this.data.sectionPicker 145 | sectionPicker.range = _string.form_duration_list.slice(0, 6 - sectionIndex) 146 | if (sectionPicker.value > sectionPicker.range.length - 1) { 147 | sectionPicker.value = sectionPicker.range.length - 1 148 | } 149 | this.setData({ 150 | sectionPicker, 151 | sectionPickerText: sectionPicker.range[sectionPicker.value], 152 | }) 153 | } 154 | 155 | timePickerText = timePickerText.join(' # ') 156 | 157 | this.setData({ hideSectionPicker, timePickerText }) 158 | this._tmp.ret = Object.assign({}, this._tmp.ret, { 159 | day: dayIndex + 1, 160 | section: sectionIndex + 1, 161 | offset: halfIndex, 162 | }) 163 | } 164 | 165 | pageParams.bindWeekPickerChange = function (e) { 166 | console.log('picker 新值为', e.detail.value) 167 | 168 | // 下标从 0 开始 169 | const [start, end, weekState] = e.detail.value 170 | 171 | const detailWeek = wx.ooString.component_schedule.detail_week, 172 | weekNum = [ 173 | this.data.weekPicker.range[0][start], 174 | this.data.weekPicker.range[1][end], 175 | ] 176 | 177 | let weekPickerText = [ 178 | detailWeek[0].replace('{0}', weekNum.join('-')), 179 | ] 180 | 181 | if (weekState !== 0) { 182 | weekPickerText.push(detailWeek[weekState]) 183 | } 184 | 185 | weekPickerText = weekPickerText.join(' # ') 186 | 187 | this.setData({ weekPickerText }) 188 | this._tmp.ret.week = [...weekNum, weekState].join(':') 189 | } 190 | 191 | pageParams.bindWeekPickerColumnChange = function (e) { 192 | console.log('修改的列为', e.detail.column, '值为', e.detail.value) 193 | 194 | const { column, value } = e.detail 195 | 196 | let weekPicker = wx.ooUtil.copy(this.data.weekPicker) 197 | weekPicker.value[column] = value 198 | 199 | // 开始周改变时结束周需要跟着变 200 | if (column === 0) { 201 | weekPicker.range[1] = weekPicker.range[0].slice(value) 202 | weekPicker.value[1] = 0 203 | } 204 | 205 | // 是否是同一周 206 | const isOneWeek = weekPicker.range[0][weekPicker.value[0]] === weekPicker.range[1][weekPicker.value[1]] 207 | 208 | // 如果是同一周 就不显示单双 209 | weekPicker.range[2] = isOneWeek ? _string.form_week_list.slice(0, 1) : _string.form_week_list 210 | weekPicker.value[2] = isOneWeek ? 0 : weekPicker.value[2] 211 | 212 | this.setData({ weekPicker }) 213 | } 214 | 215 | pageParams.bindSectionPickerChange = function (e) { 216 | console.log('picker 新值为', e.detail.value) 217 | 218 | let sectionPicker = this.data.sectionPicker 219 | sectionPicker.value = e.detail.value 220 | 221 | this.setData({ 222 | sectionPicker, 223 | sectionPickerText: this.data.sectionPicker.range[e.detail.value], 224 | }) 225 | 226 | this._tmp.duration = parseInt(e.detail.value) + 1 227 | } 228 | 229 | pageParams.save = function (courses) { 230 | courses = courses || [] 231 | 232 | wx.ooSaveData({ edu: { schedule_custom: [...this.data.originCustomSchedule, ...courses] } }) 233 | wx.ooService.edu.saveSchedule(wx.ooService.edu.renderSchedule()) 234 | } 235 | 236 | Page(pageParams) 237 | -------------------------------------------------------------------------------- /pages/edu/schedule/custom.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "自定义课表" 3 | } 4 | -------------------------------------------------------------------------------- /pages/edu/schedule/custom.wxml: -------------------------------------------------------------------------------- 1 | {{ _string.form_title }} 2 | 3 | 4 | 5 | {{ _string.form_field[0] }} 6 | 7 | 9 | {{ timePickerText }} 10 | 11 | 12 | 13 | 14 | {{ _string.form_field[1] }} 15 | 16 | 18 | {{ weekPickerText }} 19 | 20 | 21 | 22 | 23 | {{ _string.form_field[2] }} 24 | 25 | 26 | 27 | {{ _string.form_field[3] }} 28 | 29 | 30 | 31 | {{ _string.form_field[4] }} 32 | 33 | 34 | 35 | 36 | 47 | 48 | 49 | 50 | 51 | 52 | {{ _string.list_title }} 53 | 54 | 55 | 57 | 58 | {{ item.name }} / {{ item.teacher }} 59 | {{ timePicker.range[0][item.day - 1] }} / {{ timePicker.range[1][item.section - 1] }} 60 | 61 | 62 | {{ _string.handle_delete }} 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /pages/edu/schedule/custom.wxss: -------------------------------------------------------------------------------- 1 | page { 2 | background-color: #ebebeb; 3 | } 4 | 5 | picker { 6 | flex: auto; 7 | } 8 | 9 | .value { 10 | flex: auto; 11 | } 12 | 13 | .oo-cell-label { 14 | line-height: 2em; 15 | 16 | width: 7em; 17 | } 18 | 19 | .oo-select { 20 | margin: 0; 21 | } 22 | 23 | .oo-select-text { 24 | display: flex; 25 | 26 | height: 2em; 27 | } 28 | 29 | .schedule-list { 30 | position: fixed; 31 | z-index: 999; 32 | top: 100%; 33 | left: 0; 34 | 35 | box-sizing: border-box; 36 | width: 100%; 37 | min-height: 100vh; 38 | margin-top: -50px; 39 | padding: 15px; 40 | 41 | transition: all .6s cubic-bezier(.68, -.55, .27, 1.55); 42 | 43 | border-top-left-radius: 15px; 44 | border-top-right-radius: 15px; 45 | background-color: #fff; 46 | } 47 | 48 | .schedule-list.show { 49 | position: absolute; 50 | top: 10px; 51 | 52 | min-height: calc(100% - 10px); 53 | margin-top: 0; 54 | } 55 | 56 | .schedule-list.disable { 57 | margin-top: 0; 58 | } 59 | 60 | .list-header { 61 | display: flex; 62 | 63 | margin-bottom: 30px; 64 | 65 | justify-content: space-between; 66 | } 67 | 68 | .list-item { 69 | display: flex; 70 | 71 | margin: 15px 0 0; 72 | padding: 12px 10px; 73 | 74 | align-items: center; 75 | justify-content: space-between; 76 | } 77 | 78 | .schedule-info { 79 | display: flex; 80 | flex-direction: column; 81 | } 82 | 83 | .schedule-info text:first-child { 84 | margin-bottom: 5px; 85 | } 86 | 87 | .item-handle text { 88 | margin-left: 10px; 89 | } 90 | -------------------------------------------------------------------------------- /pages/edu/schedule/update.js: -------------------------------------------------------------------------------- 1 | import regeneratorRuntime from '../../../utils/libs/regenerator-runtime' 2 | 3 | let pageParams = { 4 | data: { 5 | _string: Object.assign({ 6 | btn: wx.ooString.global.btn_title, 7 | }, 8 | wx.ooString.service_edu_schedule, 9 | ), 10 | }, 11 | } 12 | 13 | pageParams.onReady = function () { 14 | wx.ooSetTitle(this.data._string.title) 15 | } 16 | 17 | pageParams.bindSchoolTimeChange = function (e) { 18 | this.setData({ schoolTime: e.detail.value }) 19 | } 20 | 21 | pageParams.bindSubmit = async function () { 22 | const ret = await wx.ooService.edu.getCurrWeek(true) 23 | 24 | if (ret === undefined) { 25 | return 26 | } 27 | 28 | const scheduleData = await wx.ooService.edu.fetchSchedule(this.data.schoolTime) 29 | if (!scheduleData) { 30 | return 31 | } 32 | 33 | if (scheduleData.length === 0) { 34 | wx.ooShowToast({ title: this.data._string.empty_schedule }) 35 | return 36 | } 37 | 38 | wx.ooEvent.emit('updateCurrWeek') 39 | wx.switchTab({ url: '/pages/tabbar/schedule' }) 40 | } 41 | 42 | Page(pageParams) 43 | -------------------------------------------------------------------------------- /pages/edu/schedule/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "更新课表", 3 | "usingComponents": { 4 | "school-time": "../../../component/schoolTime" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /pages/edu/schedule/update.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /pages/edu/schedule/update.wxss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/choyri/WeGifun/68ffe6d650676f46dd5fa849a81f1e354d2ae64a/pages/edu/schedule/update.wxss -------------------------------------------------------------------------------- /pages/edu/score.js: -------------------------------------------------------------------------------- 1 | import regeneratorRuntime from '../../utils/libs/regenerator-runtime' 2 | 3 | let pageParams = { 4 | data: { 5 | _string: Object.assign({ 6 | btn: wx.ooString.global.btn_title, 7 | }, 8 | wx.ooString.service_edu_score, 9 | ), 10 | }, 11 | } 12 | 13 | pageParams.onReady = function () { 14 | wx.ooSetTitle(this.data._string.title) 15 | } 16 | 17 | pageParams.bindSchoolTimeChange = function (e) { 18 | this.setData({ schoolTime: e.detail.value }) 19 | } 20 | 21 | pageParams.bindClose = function () { 22 | this.setData({ showRecord: false }) 23 | 24 | this.setBackgroundColorTop('#ffffff') 25 | } 26 | 27 | pageParams.bindSubmit = async function () { 28 | const scoreData = await wx.ooService.edu.fetchScore(this.data.schoolTime) 29 | if (!scoreData) { 30 | return 31 | } 32 | 33 | if (scoreData.length === 0) { 34 | wx.ooShowToast({ title: this.data._string.noRecord }) 35 | return 36 | } 37 | 38 | this.setData({ 39 | showRecord: true, 40 | scoreData, 41 | }) 42 | 43 | this.setBackgroundColorTop('#4285f4') 44 | 45 | wx.ooTip.score() 46 | } 47 | 48 | pageParams.setBackgroundColorTop = function (value) { 49 | if (wx.setBackgroundColor) { 50 | wx.setBackgroundColor({ backgroundColorTop: value }) 51 | } 52 | } 53 | 54 | Page(pageParams) 55 | -------------------------------------------------------------------------------- /pages/edu/score.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "课程成绩", 3 | "usingComponents": { 4 | "school-time": "../../component/schoolTime" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /pages/edu/score.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ schoolTime.title }} 9 | 10 | 11 | 12 | 13 | 14 | {{ item.name }} 15 | {{ item.nature }} / {{ item.credit }} 16 | 17 | 18 | {{ item.score }} 19 | {{ item.gpa }} 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /pages/edu/score.wxss: -------------------------------------------------------------------------------- 1 | .record { 2 | position: fixed; 3 | top: 100%; 4 | left: 0; 5 | 6 | width: 100%; 7 | min-height: 100%; 8 | 9 | transition: all .8s cubic-bezier(.68, -.55, .27, 1.55); 10 | 11 | opacity: 0; 12 | background-color: #fff; 13 | 14 | -webkit-overflow-scrolling: touch; 15 | } 16 | 17 | .record.show { 18 | position: absolute; 19 | top: 0; 20 | 21 | opacity: 1; 22 | } 23 | 24 | .record-bg { 25 | height: 5em; 26 | margin-bottom: -4em; 27 | 28 | background-color: #4285f4; 29 | } 30 | 31 | .record-content { 32 | margin: 15px; 33 | padding: 10px; 34 | 35 | background-color: #fff; 36 | box-shadow: 0 1px 6px rgba(32, 33, 36, .28); 37 | } 38 | 39 | @supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) { 40 | .record-content { 41 | margin-bottom: constant(safe-area-inset-bottom); 42 | margin-bottom: env(safe-area-inset-bottom); 43 | } 44 | } 45 | 46 | .record-title { 47 | font-size: 40rpx; 48 | font-weight: lighter; 49 | 50 | display: flex; 51 | 52 | margin-bottom: 15px; 53 | 54 | align-items: center; 55 | justify-content: space-between; 56 | } 57 | 58 | .oo-table .tr { 59 | padding: .6em 0; 60 | } 61 | 62 | .oo-table .td { 63 | text-align: left; 64 | } 65 | 66 | .oo-table .td text:last-child { 67 | font-size: 24rpx; 68 | 69 | margin-top: 4px; 70 | } 71 | 72 | .course-describe { 73 | margin-left: 10px; 74 | } 75 | 76 | .course-describe text { 77 | line-height: 1.2em; 78 | } 79 | 80 | .course-score { 81 | font-size: 36rpx; 82 | 83 | margin-right: 10px; 84 | 85 | align-items: flex-end; 86 | } 87 | 88 | .oo-table .td.course-score { 89 | flex: 0 0 3em; 90 | } 91 | -------------------------------------------------------------------------------- /pages/elec/deposit.js: -------------------------------------------------------------------------------- 1 | import regeneratorRuntime from '../../utils/libs/regenerator-runtime' 2 | 3 | let pageParams = { 4 | data: { 5 | amountItems: wx.ooService.elec.getDepositAmountItems(), 6 | checkDorm: 'N/A', 7 | pwdIcon: { right: false, show: false }, 8 | supportSoter: false, 9 | _string: Object.assign({ 10 | btn: wx.ooString.global.btn_title, 11 | }, 12 | wx.ooString.service_elec_deposit, 13 | ), 14 | }, 15 | _tmp: {}, 16 | } 17 | 18 | pageParams.onLoad = function () { 19 | wx.ooEvent.on('dormRetrieve', this.renderPage, this) 20 | this.renderPage() 21 | 22 | if (wx.ooCache.supportSoter && wx.ooCache.supportSoter !== this.data.supportSoter) { 23 | this.setData({ supportSoter: wx.ooCache.supportSoter }) 24 | } 25 | } 26 | 27 | pageParams.onReady = function () { 28 | wx.ooSetTitle(this.data._string.title) 29 | } 30 | 31 | pageParams.onUnload = function () { 32 | wx.ooEvent.off(this) 33 | } 34 | 35 | pageParams.renderPage = async function (dormInfo) { 36 | dormInfo = dormInfo || wx.ooService.elec.getDormInfo() 37 | 38 | dormInfo = wx.ooService.elec.renderDormInfoWithState(dormInfo.id) 39 | this.setData({ dormInfo }) 40 | 41 | const checkDorm = await wx.ooService.elec.checkDeposit(dormInfo.id) 42 | if (checkDorm) { 43 | this.setData({ checkDorm }) 44 | } 45 | } 46 | 47 | pageParams.bindAmountChange = function (e) { 48 | const amount = parseInt(e.detail.value) 49 | this._tmp.amount = amount || this._tmp.customAmount 50 | 51 | let amountItems = wx.ooUtil.copy(this.data.amountItems) 52 | 53 | for (let item of amountItems) { 54 | item.checked = item.value === amount 55 | } 56 | 57 | this.setData({ amountItems }) 58 | } 59 | 60 | pageParams.bindAmountInput = function (e) { 61 | const amount = parseInt(e.detail.value || 0) 62 | this._tmp.amount = this._tmp.customAmount = amount 63 | } 64 | 65 | pageParams.bindPwdInput = function (e) { 66 | const pwd = e.detail.value 67 | 68 | let pwdIcon = {} 69 | 70 | if (pwd.length === 6) { 71 | const checkRes = wx.ooService.user.checkCardPwd(pwd) 72 | 73 | this._tmp.auth = pwdIcon.right = checkRes 74 | pwdIcon.show = true 75 | } else { 76 | this._tmp.auth = false 77 | pwdIcon.show = false 78 | } 79 | 80 | if (pwdIcon.show !== this.data.pwdIcon.show) { 81 | this.setData({ pwdIcon }) 82 | } 83 | } 84 | 85 | pageParams.bindSubmit = async function () { 86 | if (!this._tmp.amount) { 87 | wx.ooShowToast({ title: this.data._string.error_amount }) 88 | return 89 | } 90 | 91 | if (!this.data.supportSoter && !this._tmp.auth) { 92 | wx.ooShowToast({ title: this.data._string.error_auth }) 93 | return 94 | } 95 | 96 | const confirmContent = this.data._string.confirm 97 | .replace('{0}', this.data.checkDorm) 98 | .replace('{1}', this._tmp.amount) 99 | 100 | const modalRes = await wx.ooShowModal({ content: confirmContent }) 101 | 102 | if (!modalRes.confirm) { 103 | console.log('取消充值') 104 | return 105 | } 106 | 107 | if (this.data.supportSoter) { 108 | const soterRet = await wx.ooStartSoter({ 109 | challenge: this.data.checkDorm, 110 | authContent: this.data._string.prove_tip, 111 | }).catch(ret => { 112 | wx.ooSaveData({ supportSoter: false }) 113 | return ret 114 | }) 115 | 116 | console.log(soterRet) 117 | 118 | if (soterRet.errCode !== 0) { 119 | wx.ooShowModal({ content: this.data._string.soter_fail }, false) 120 | this.setData({ supportSoter: false }) 121 | return 122 | } 123 | } 124 | 125 | const depositRes = await wx.ooService.elec.deposit(this.data.dormInfo.id, this._tmp.amount) 126 | if (!depositRes) { 127 | return 128 | } 129 | 130 | wx.ooShowToast({ title: this.data._string.success, icon: 'success' }) 131 | await wx.ooSleep(1500) 132 | 133 | wx.navigateBack() 134 | } 135 | 136 | Page(pageParams) 137 | -------------------------------------------------------------------------------- /pages/elec/deposit.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "充值", 3 | "backgroundColor": "#ebebeb" 4 | } 5 | -------------------------------------------------------------------------------- /pages/elec/deposit.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |