Compare commits

..

51 Commits

Author SHA1 Message Date
educoder_weapp 4bfdae765b U 列表界面的图片懒加载
5 years ago
educoder_weapp 80b7d665e4 F 课堂邀请页面小程序码获取失败 F 每日签到完后按钮没有变灰
5 years ago
educoder_weapp ffbea46001 U 个人中心界面优化
5 years ago
educoder_weapp d5ff3a53eb * A 增加教学案例模块
5 years ago
educoder_weapp 268a355df2 U 优化分享封面图片
5 years ago
educoder_weapp 74bd7c5c9e A 增加在线竞赛模块、省流模式
5 years ago
educoder_weapp 222e364f48 A 分享朋友圈,添加收藏
5 years ago
educoder_weapp bf9169a08d A 开源版本库查看
5 years ago
educoder_weapp 7c07e356a3 U 优化登录注册的信息校验
5 years ago
educoder_weapp 2b2bebf70a U 优化分享
5 years ago
educoder_weapp 0995346d72 U 登录界面优化
5 years ago
educoder_weapp f3cb7b10ed U 管理界面浏览历史支持筛选
5 years ago
educoder_weapp 11478a31b2 A 使用特定功能时登录校验
5 years ago
educoder_weapp a9c184df1d A 管理界面浏览记录历史查看
5 years ago
educoder_weapp deb3c86769 * A 支持填入微信个人信息
5 years ago
educoder_weapp 946a2e57a7 F 用户信息未完善时加入课堂bug
5 years ago
educoder_weapp 0320a78d18 A 页面跟踪并提交索引
5 years ago
educoder_weapp 26ddc8d992 A 支持选取微信头像
5 years ago
educoder_weapp 9d1cbbeee4 U 优化信息安全检查速度
5 years ago
educoder_weapp fe06d3bbee A 头像安全检测
5 years ago
educoder_weapp 8dc7062136 A 试卷答题时网络不好情况处理
5 years ago
educoder_weapp f25c84d5e8 U 目录结构跳转,压缩主包
5 years ago
educoder_weapp c30432d745 A 网络请求缓存
5 years ago
educoder_weapp 03e67f288a A 支持选用实践课程
5 years ago
educoder_weapp 9b9053b5d4 A 发送实训至课堂
5 years ago
educoder_weapp 76c4242bc1 D 调试输出
5 years ago
educoder_weapp d7a7746840 A 实训项目多选功能
5 years ago
educoder_weapp 82569ce5a0 A 发现界面
5 years ago
educoder_weapp bfd7f13a58 A 试卷倒计时
5 years ago
educoder_weapp f62731e1a3 教师加入课堂审批
5 years ago
educoder_weapp c9202a7602 学生、教师管理模块,实训黑暗模式
5 years ago
educoder_weapp b42f4ca2e5 账号左划删除
5 years ago
educoder_weapp 57aa083a00 代码编辑器改用editor
5 years ago
educoder_weapp 3f45de366c 优化界面
5 years ago
educoder_weapp 5ffaf9ec41 课堂界面更改,使用we-ui
5 years ago
educoder_weapp 1f1d9ad6ea F个人信息性别设置,实训文件内容获取
5 years ago
educoder_weapp 78eb1192ff 增加试卷的分数查询及答题卡
5 years ago
educoder_weapp 37b6d4edc3 A 切换账号 学生课堂签到
5 years ago
educoder_weapp 8bc73d7bd8 增加信息修改界面
5 years ago
unknown da974380fb 增加实训作业模块与课堂动态模块
5 years ago
unknown 5119e4f865 优化登录界面、用户界面
5 years ago
educoder_weapp d99d73f1a1 迁移暂提交
5 years ago
educoder_weapp b686679013 删除普通作业模块、新建试卷界面
5 years ago
educoder_weapp ef84c44d00 增加实训模块
5 years ago
educoder_weapp 62e5403be7 Add 课堂-普通作业模块
5 years ago
yang 0b47aa0777 我的课堂页面样式更改
5 years ago
educoder_weapp c4f55879d4 拆分复杂界面为多个组件,修补头像上传bug
5 years ago
educoder_weapp 65d57faed8 添加分包,优化界面
5 years ago
educoder_weapp ea0ed0b11f 优化重写网络接口封装
5 years ago
educoder_weapp 906812f02c add cloudfunctions
5 years ago
educoder_weapp fc5d403db0 add service contact function
5 years ago

26
.gitignore vendored

@ -1,3 +1,18 @@
api_docs/
.idea/
.wechatide/
res/
docs/
pandocs
push-git.cmd
cloudfunctionTemplate/
# node
node_modules
# Windows
[Dd]esktop.ini
Thumbs.db
@ -9,15 +24,4 @@ $RECYCLE.BIN/
.Spotlight-V100
.TemporaryItems
.Trashes
project.config.json
app.js
towxml
we-cropper
components/weui-miniprogram
.idea
api_docs
# Node.js
node_modules/

@ -1,64 +1,6 @@
# 简介
educoder微信小程序帮助使用[educoder平台](https://www.educoder.net)的应用,方便在手机上使用。利用educoder网站的API搭建
educoder微信小程序帮助使用[educoder平台](https://www.educoder.net)的应用,方便在手机上使用。
## 源码
[https://github/jinke18/smart_class](https://github.com/jinke18/smart_class)
## 小程序码
![小程序码](/images/weapp_code_smart_class.png)
# 功能介绍
## 教室
- 学员可以输入邀请码进入课堂
- 进入教室界面会显示在位,头像为彩色,若退出课堂界面则会显示灰色头像
- 教员在教室界面中可以直观地看到学员在位情况,可以选择学员让其起立回答问题,并且对学员可以进行加分、减分操作
- 在分数列表中可以看到加减分记录(数据在后台可以导出)
- 学员可以收到教员让其起立提问、回答的提示,还可以点击“我要提问、回答”
- 教室内有讨论区,可以交流
## 课程资源
- 在课程界面进入“资源”可以查看本课堂的课程文件资源
- 支持打开ppt doc xls pdf文件
## 试卷作答
- 学员在课程内可以看到老师发布的试卷,并且回答
- 试卷截止后并且老师选择了公开答案,学生可以看到公布的答案
- 老师可以创建试卷,发布试卷,查看学员作答分数
## 其他
账号的注册、登陆、找回密码、头像更改等
# 实现
## educoder平台接入
使用HTTP与平台的api接口交互
接口列表如下
- 搜索课堂https://www.educoder.net/api/courses.json
- 查询用户的课堂https://www.educoder.net/api/users/<user_id>/courses.json
- 查询学校https://www.educoder.net/api/schools/school_list.json
- 新建课堂https://www.educoder.net/api/courses.json
- 加入课堂https://www.educoder.net/api/courses/apply_to_join_course.json
- 新建试卷https://www.educoder.net/api/courses/<course_id>/exercises/new.json
- 查询试卷https://www.educoder.net/api/courses/<course_id>/exercises.json
- 班级文件资源https://www.educoder.net/api/files.json
# 教室学员在位情况及分数的同步实现
使用了[leancloud](https://www.leancloud.cn/)提供的javascript开发包实现数据同步功能, 如学员在位情况、分数的同步, 其底部技术为websocket
![小程序码](/images/wxacode.jpg)

@ -1,64 +0,0 @@
//app.js
//导入leancloud库与后台交互
import {Session} from "./data/requests";
import {Client} from "./data/client";
import {Account, Course, Exercise, ExerciseQuestion} from "./data/eduapi"
const AV = require('./lib/av-live-query-weapp-min');
const Towxml = require('/towxml/main');
const login = require("./model/user");
AV.init({
appId: 'eVHYNBdT5D2lDxNj5jtJXsWT-MdYXbMMI',
appKey: 'waM3bwf1ftpsMLjRBqqVyJIQ',
});
App({
towxml: new Towxml(),
client: new Client(),
logining: false,
string_format_init: function(){
//使String类实现format方法
//@todo: 待测试
String.prototype.format = function (kwargs) {
return this.replace(/\{(\w+)\}/g, function (k, v) {
return kwargs[v]
});
};
},
require_login(){
if (!this.logining) {
this.logining = true;
wx.navigateTo({
url: "/pages/login/login"
});
}
},
onLaunch: function () {
this.client.onRequireLogin(() => {
this.require_login();
});
console.log("onLauch");
this.client.get_user_info({
success: res=>{
console.log("app.js: userinfo get success");
console.log(res);
if(res.data.user_id == 2){
this.client.callback.require_login();
}
},
fail:error=>{
console.warn("app.js: get user info fail");
}
}).then(()=>{
login().then(localuser=>{
//save for autologin
localuser.addUnique("edu_account_real_names", this.client.current_user.real_name);
localuser.addUnique("edu_account_names", this.client.current_user.name);
localuser.addUnique("edu_account_logins", this.client.current_user.login);
localuser.set("cookies", this.client.session.cookies).save();
});
});
}
});

@ -1,62 +0,0 @@
{
"pages": [
"pages/my_courses/my_courses",
"pages/findmore/findmore",
"pages/exercise_setting/exercise_setting",
"pages/login/login",
"pages/course_setting/course_setting",
"pages/exercises/exercises",
"pages/courses/courses",
"pages/exercise/exercise",
"pages/setting/setting",
"pages/classroom/classroom",
"pages/about/about",
"pages/course/course",
"pages/files/files",
"pages/mark_detail/mark_detail",
"pages/reset_password/reset_password",
"pages/image_crop/image_crop",
"pages/question_setting/question_setting",
"pages/exercise_grade/exercise_grade",
"pages/course_invite/course_invite",
"pages/exercise_result/exercise_result"
],
"window": {
"backgroundTextStyle": "dark",
"navigationBarTitleText": "积分教室",
"navigationBarTextStyle": "black",
"backgroundColor": "#f2f2f2",
"navigationBarBackgroundColor": "#fbfbfb"
},
"tabBar": {
"selectedColor": "#00C7B7",
"list": [
{
"pagePath": "pages/my_courses/my_courses",
"text": "我的课堂",
"iconPath": "images/tabbar-icon/tabbar_study_default.png",
"selectedIconPath": "images/tabbar-icon/tabbar_study_pressed.png"
},
{
"pagePath": "pages/courses/courses",
"text": "课程列表",
"iconPath": "images/tabbar-icon/tabbar_contact_default.png",
"selectedIconPath": "images/tabbar-icon/tabbar_contact_pressed.png"
},
{
"pagePath": "pages/findmore/findmore",
"text": "发现",
"iconPath": "images/tabbar-icon/tabbar_findmore_default.png",
"selectedIconPath": "images/tabbar-icon/tabbar_findmore_pressed.png"
},
{
"pagePath": "pages/setting/setting",
"iconPath": "images/tabbar-icon/tabbar_settings_default.png",
"selectedIconPath": "images/tabbar-icon/tabbar_settings_pressed.png",
"text": "设置"
}
]
},
"sitemapLocation": "sitemap.json"
}

@ -1,39 +0,0 @@
@import './components/weui-miniprogram/weui-wxss/dist/style/weui.wxss';
page {
height: 100%;
background-color: #f2f2f2;
}
.flex-wrap{
display: flex;
flex-direction: row;
}
.form-wrap {
border-top: 1px solid #bbb;
margin: 12px 0;
}
.tappable{
color: #1aad19;
padding: 0 12px;
}
.tappable:hover {
opacity: .6;
}
.container {
padding: 4px 12px;
}
.hint,
.error
.warning {
display: block;
margin: 7px 0;
color: #888;
font-size: 12px;
}
.error{
color:red;
}
.warning{
color: orange;
}

@ -0,0 +1,203 @@
## v0.19.3
* U 列表界面的图片懒加载
* F 修复带Cookie请求的bug
## v0.19.2
* F 课堂邀请页面小程序码获取失败
* F 每日签到完后按钮没有变灰
## v0.19.1
* U 个人中心界面优化
* U 主题色加深
## v0.19.0
* A 增加教学案例模块
* D 个人中心的EduCoder云网入口
## v0.18.1
* U 页面访问记录扩大页面范围
* U 分享图片封面
## v0.18.0
* A 添加在线竞赛模块
* A 添加省流模式
* A 增加设置界面
* U 完善markdown网址解析功能
## v0.17.1
* A 增加分享朋友圈
* A 添加收藏时的封面图
* A 配置代码懒注入
## v0.17.0
* A 增加开源版本库查看
## v0.16.10
* U 登录界面优化
* U 优化登录注册的信息校验
* U 优化分享
## v0.16.9
* A 管理界面浏览历史查看
* A 使用一些功能时登录校验
## v0.16.8
* A 支持填入微信个人信息
* F 使用用户名登录时账号判断错误而无法登录
## v0.16.7
* F 用户信息未完善时加入课堂bug
## v0.16.6
* A 支持选用微信头像
* F 兼容Promise.finally
* F 爬虫访问处理
## v0.16.5
* A 头像安全检查
* U 信息安全检查速度
## v0.16.4
* A 试卷答题时网络不好情况处理
* A 课堂界面兼容iphone X
## v0.16.3
* U 优化项目结构,主包压缩一半
## v0.16.2
* A 支持选用实践课程
* A 网络请求缓存
## v0.16.1
* A 发送单个实训至课堂
* A 探索的实训界面支持多选
## v0.16.0
* A 探索界面
* A 我的实践课程模块
## v0.15.1
* A 教师加入课堂审批
* A 熬夜睡觉提醒
* A markdown中URL链接解析与跳转
* F index-list滑动与窗口滚动冲突
* F 加入课堂时多身份bug
## v0.15.0
* A 实训关卡黑暗模式
* A 学生管理模块
* A 教师管理模块
## v0.14.5
* A 接入内容安全接口
* A 添加EduCoder云网推荐链接
* U 代码编辑器改用editor
* U 代码编辑器增加简单自动缩进
* F 学校没有子单位时无法选择单位
## v0.14.4
* A 使用we-ui扩展库
* U 课堂界面优化更改
* U 加入课堂对话框界面优化
## v0.14.3
* F 实训文件内容获取失败(文件path错误)
* F 个人信息性别设置bug
* D 删除实名认证界面
## v0.14.2
* F 签到界面无权限访问提示
* F 更改头像界面图片初次加载失败
* F 电脑端用户界面图标及文字位置不正确
## v0.14.1
* A 试卷体验升级,增加分数查看、答题卡
* A 签到界面增加教师的分享,以及二维码签到码查看
* U 切换账号提示更新,防止遮挡操作
## v0.14.0
* A 职业认证页面
* A 学生课堂签到模块
* A 切换账号功能
* U 个人中心增加图标
* U iconfont增加modal与toast提示方式
* U 图片资源转移服务器
* F 实名认证证件上传判断bug
## v0.13.3
* A 增加实名认证界面
* A 增加数据异常监测上报
* U iconfont组件优化
* F 登录Cookies解析失败
## v0.13.2
* A iconfont
* A 首页根据身份提供创建课程选项
* A 显示身份认证的状态
* F cookies失效登录失败时用户界面显示错误
* F 实训测试集解析错误
## v0.13.1
* A 修改个人信息界面
* F 头像更改界面高度异常
* F 我的实训初次显示不加载
## v0.13.0
* A 菜单的删除课堂
* A 课堂动态模块
* A 实训作业模块
* A 试卷封面,通知栏进入试卷
* A 体验版开发版可以切换切换开发环境与发布环境
* F 课堂邀请界面登录按钮bug
* F 微信灰度API promise化产生的bug
* F 使用style:v2后样式问题
* F 登录界面bug
## v0.12.2
* A 剪切板邀请码自动读取
* A 首页点击搜索自动进入所在模块相应类目
* U 登录界面、用户界面优化
* U 更新机制优化
* F 进入搜索页面nav标题错误
* F 登录后我的实训不自动刷新
## v0.12.1
* A 新通知红点标注,点击后跳转
* A 长段代码预览
* U 部分分包预加载
* U 优化分享
* U 优化体验
* F toast提示失效
* F 优化latex部分显示异常
* D 删除普通作业模块、新建试卷界面(审核不过)
## v0.12.0
* A 实训模块
* A 我参与的实训
* A 搜索模块
* A 修改密码模块
* A 消息中心分类
* A 加入课堂对话框增加扫码功能
* U 部分按钮样式更新
* U 我的课堂上拉加载更多
* F 加入课堂后Toast提示不消失
* F 进入app未登录时获取消息通知报错
## v0.11.1
* U error-page多按钮操作
* U 课堂错误处理界面返回主页操作
* F 非课程成员进入时弹出弹窗
* F 试卷填空题答题bug修补
## v0.11.0
* A 课程-普通作业模块
* A 页面不存在时进入主页
* A referrerInfo 分析
* A 主页课堂菜单操作
* A error-page组件错误处理界面
* U 加入课堂对话框功能升级
* U 课堂页面错误处理
* U 课程邀请界面改进,增加已停用时的图标
* U 改变小程序码生成接口为getWXACodeUnlimited
* F 试卷题目富文本显示异常
* D 隐藏发现页入口

@ -0,0 +1 @@
trigger_function/

@ -0,0 +1,7 @@
{
"permissions": {
"openapi": [
"subscribeMessage.send"
]
}
}

@ -0,0 +1,156 @@
const cloud = require('wx-server-sdk')
const sceneDescMap = {
[void 0]: "未知",
1000: "其他",
1001: "发现栏小程序主入口「最近使用」列表基础库2.2.4版本起包含「我的小程序」列表)",
1005: "微信首页顶部搜索框的搜索结果页",
1006: "发现栏小程序主入口搜索框的搜索结果页",
1007: "单人聊天会话中的小程序消息卡片",
1008: "群聊会话中的小程序消息卡片",
1011: "扫描二维码",
1012: "长按图片识别二维码",
1013: "扫描手机相册中选取的二维码",
1014: "小程序模板消息",
1017: "前往小程序体验版的入口页",
1019: "微信钱包微信客户端7.0.0版本改为支付入口)",
1020: "公众号profile 页相关小程序列表(已废弃)",
1022: "聊天顶部置顶小程序入口微信客户端6.6.1版本起废弃)",
1023: "安卓系统桌面图标",
1024: "小程序profile页",
1025: "扫描一维码",
1026: "发现栏小程序主入口,「附近的小程序」列表",
1027: "微信首页顶部搜索框搜索结果页「使用过的小程序」列表",
1028: "我的卡包",
1029: "小程序中的卡券详情页",
1030: "自动化测试下打开小程序",
1031: "长按图片识别一维码",
1032: "扫描手机相册中选取的一维码",
1034: "微信支付完成页",
1035: "公众号自定义菜单",
1036: "App分享消息卡片",
1037: "小程序打开小程序",
1038: "从另一个小程序返回",
1039: "摇电视",
1042: "添加好友搜索框的搜索结果页",
1043: "公众号模板消息",
1044: "带shareTicket的小程序消息卡片",
1045: "朋友圈广告",
1046: "朋友圈广告详情页",
1047: "扫描小程序码",
1048: "长按图片识别小程序码",
1049: "扫描手机相册中选取的小程序码",
1052: "卡券的适用门店列表",
1053: "搜一搜的结果页",
1054: "顶部搜索框小程序快捷入口微信客户端版本6.7.4起废弃)",
1056: "聊天顶部音乐播放器右上角菜单",
1057: "钱包中的银行卡详情页",
1058: "公众号文章",
1059: "体验版小程序绑定邀请页",
1064: "微信首页连Wi-Fi状态栏",
1067: "公众号文章广告",
1068: "附近小程序列表广告(已废弃)",
1069: "移动应用",
1071: "钱包中的银行卡列表页",
1072: "二维码收款页面",
1073: "客服消息列表下发的小程序消息卡片",
1074: "公众号会话下发的小程序消息卡片",
1077: "摇周边",
1078: "微信连Wi-Fi成功提示页",
1079: "微信游戏中心",
1081: "客服消息下发的文字链",
1082: "公众号会话下发的文字链",
1084: "朋友圈广告原生页",
1088: "会话中系统消息,打开小程序",
1089: "微信聊天主界面下拉「最近使用」栏基础库2.2.4版本起包含「我的小程序」栏)",
1090: "长按小程序右上角菜单唤出最近使用历史",
1091: "公众号文章商品卡片",
1092: "城市服务入口",
1095: "小程序广告组件",
1096: "聊天记录,打开小程序",
1097: "微信支付签约原生页,打开小程序",
1099: "页面内嵌插件",
1102: "公众号profile 页服务预览",
1103: "发现栏小程序主入口「我的小程序」列表基础库2.2.4版本起废弃)",
1104: "微信聊天主界面下拉「我的小程序」栏基础库2.2.4版本起废弃)",
1106: "聊天主界面下拉,从顶部搜索结果页,打开小程序",
1107: "订阅消息,打开小程序",
1113: "安卓手机负一屏,打开小程序(三星)",
1114: "安卓手机侧边栏,打开小程序(三星)",
1124: "扫“一物一码”打开小程序",
1125: "长按图片识别“一物一码”",
1126: "扫描手机相册中选取的“一物一码”",
1129: "微信爬虫访问",
1131: "浮窗打开小程序",
1135: "小程序资料页打开小程序",
1146: "地理位置信息打开出行类小程序",
1148: "卡包-交通卡,打开小程序",
1150: "扫一扫商品条码结果页打开小程序",
1153: '“识物”结果页打开小程序',
1154:'朋友圈内打开“单页模式”',
1155:'“单页模式”打开小程序',
1169:'发现栏小程序主入口,各个生活服务入口(例如快递服务、出行服务等)'
}
function getFormatTime(date){
date = date || new Date();
let hour = date.getHours();
let minu = date.getMinutes();
let sec = date.getSeconds();
if (hour < 10) hour = "0" + hour;
if (minu < 10) minu = "0" + minu;
if (sec < 10) sec = "0" + sec;
return hour + ":" + minu + ":" + sec;
}
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
});
const db = cloud.database();
const pageHistoryCollection = db.collection("pageHistory");
exports.main = async (event, context) => {
let {name, data} = event;
console.log(event);
const wxContext = cloud.getWXContext();
switch (name) {
case 'getOpenData': {
return cloud.getOpenData(data);
}
case "login":{
return {
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
env: wxContext.ENV,
}
}
case "reportPageHistory":{
console.log("reportPageHistory");
var now = new Date();
let res = await pageHistoryCollection.add({
data:{
...data,
isCrawl: data.scene==1129,
sceneDesc: sceneDescMap[data.scene],
time: now,
createdAt:now,
timestamp: Date.now(),
openid: wxContext.OPENID,
_openid: wxContext.OPENID
}
});
return res;
}
case "clearPageHistory":{
return pageHistoryCollection.where(data).remove();
}
default: {
return
}
}
}

@ -0,0 +1,14 @@
{
"name": "cloudapi",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"wx-server-sdk": "~2.0.2"
}
}

@ -0,0 +1,5 @@
{
"permissions": {
"openapi": []
}
}

@ -0,0 +1,23 @@
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
console.log("call api login", event);
return {
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
env: wxContext.ENV,
}
}

@ -0,0 +1,14 @@
{
"name": "login",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"wx-server-sdk": "latest"
}
}

@ -0,0 +1,12 @@
{
"permissions": {
"openapi": [
"wxacode.get",
"wxacode.getUnlimited",
"security.msgSecCheck",
"security.imgSecCheck",
"search.siteSearch",
"search.submitPages"
]
}
}

@ -0,0 +1,92 @@
const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV})
exports.main = async (event, context) => {
console.log("调用云函数openapi, 参数event", event);
let {name, data} = event;
switch (name||event.action) {
case "wxacode.getUnlimited":
case "getWXACodeUnlimited":{
return getWXACodeUnlimited(data)
}
case "wxacode.get":
case 'getWXACode': {
return getWXACode(data)
}
case "security.msgSecCheck":{
try{
console.log("检查文本安全, 参数:", data);
var res = await cloud.openapi.security.msgSecCheck(data);
console.log("返回结果: ", res);
return res;
}catch(e){
return e;
}
}
case "security.imgSecCheck":{
try{
console.log("检查图片安全, 参数:", data);
if(data.media)
var value = Buffer.from(data.media);
else if(data.fileID){
var {fileID} = data;
var res = await cloud.downloadFile({fileID});
var value = res.fileContent;
}
var res = await cloud.openapi.security.imgSecCheck({
media:{
contentType:"image/png",
value
}
});
console.log("返回结果: ", res);
return res;
}catch(e){
return e;
}
}
case "search.siteSearch":{
return cloud.openapi.search.siteSearch(data);
}
case "search.submitPages":{
return cloud.openapi.search.submitPages(data);
}
default: {
return
}
}
}
async function getWXACodeUnlimited(data){
let {scene, page} = data;
const wxacodeResult = await cloud.openapi.wxacode.getUnlimited({
scene, page
})
const fileExtensionMatches = wxacodeResult.contentType.match(/\/([^\/]+)/)
const fileExtension = (fileExtensionMatches && fileExtensionMatches[1]) || 'jpg'
return cloud.uploadFile({
cloudPath: `images/wxacode/${(page+"?"+scene).replace(/[\/?=&]/g, "_")}.${fileExtension}`,
fileContent: wxacodeResult.buffer,
})
}
async function getWXACode(data) {
let {path} = data;
const wxacodeResult = await cloud.openapi.wxacode.get({
path
})
const fileExtensionMatches = wxacodeResult.contentType.match(/\/([^\/]+)/)
const fileExtension = (fileExtensionMatches && fileExtensionMatches[1]) || 'jpg'
return cloud.uploadFile({
cloudPath: `images/wxacode/${path.replace(/[\/?=&]/g,"_")}.${fileExtension}`,
fileContent: wxacodeResult.buffer,
});
}

@ -0,0 +1,14 @@
{
"name": "openapi",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"wx-server-sdk": "latest"
}
}

@ -0,0 +1,20 @@
{
"permissions": {
"openapi": [
"search.submitPages"
]
},
"triggers": [
{
"name": "submitPages",
"type": "timer",
"config": "0 0 2 * * * *"
},
{
"name": "clearPageHistory",
"type":"timer",
"config":"0 0 1 1 * * *"
}
]
}

@ -0,0 +1,105 @@
const cloud = require('wx-server-sdk');
//const got = require("got");
//const rp = require("request-promise")
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
const config = {
submitPagesGapDay: 1,
clearPageHistoryGapDay: 30
};
const milliSecondsPerDay = 24*60*60*1000;
const collectionNameMap = {
pageHistory: "pageHistory"
}
const PagePathMap = {
shixun:"markdown/shixun/shixun/shixun",
path:"markdown/path/path/path",
mooc_case:"markdown/mooc_case/mooc_case/mooc_case",
competition:"markdown/competition/competition/competition"
}
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext();
console.log(event);
let {TriggerName, Time} = event;
switch(TriggerName){
case "submitPages":{
return submitPages({Time});
}
case "clearPageHistory":{
return clearPageHistory({Time});
}
case "attandance":{
return attandance({Time});
}
}
}
async function submitPage({route, timeStart, timeEnd}){
const db = cloud.database();
const _ = db.command;
const pageHistoryCollection = db.collection(collectionNameMap.pageHistory);
let data = await pageHistoryCollection.aggregate()
.match({
page: route,
status: 200,
isCrawl: false,
time: _.lt(timeEnd).and(_.gte(timeStart))
})
.group({
_id:"$options"
})
.end();
if(data.list.length==0)
return "empty history: " + route;
let pages = data.list.map(i=>{
let query = Object.keys(i._id).map(k=>`${k}=${i._id[k]}`).join("&");
return {
path: route,
query
}
})
let res = await cloud.openapi.search.submitPages({pages});
console.log("submit pages success", route, pages.length, res, pages);
return "submit success: " + route + " " + pages.length;
}
async function submitPages({Time}){
let timeEnd = new Date(Time);
let timeStart = new Date(timeEnd.getTime() - config.submitPagesGapDay*milliSecondsPerDay);
// pages to be submitted
let routes = [
"shixun", "path", "mooc_case", "competition"
]
let promises = routes.map(i=>{
return submitPage({route: PagePathMap[i], timeStart, timeEnd})
})
return Promise.all(promises)
}
async function clearPageHistory({Time}){
console.log("clearPageHistory", Time);
let now = new Date(Time);
let timeEnd = new Date(now.getTime() - config.clearPageHistoryGapDay*milliSecondsPerDay);
const db = cloud.database();
const _ = cloud.command;
const pageHistoryCollection = db.collection(collectionNameMap.pageHistory);
let res = await pageHistoryCollection.where({
time: _.lt(timeEnd)
}).remove();
console.log("remove page History result: ", res);
return {code: 0, message:"success", data:res};
}

@ -0,0 +1,14 @@
{
"name": "trigger",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"wx-server-sdk": "~2.0.2"
}
}

@ -1,34 +0,0 @@
// components/icon/myicon.js
Component({
/**
* 组件的属性列表
*/
properties: {
addGlobalClass: true
},
externalClasses: ['custom-class'],
properties: {
info: null,
type: String,
size: String,
color: String
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
onClick() {
this.triggerEvent('click');
}
}
})

@ -1,7 +0,0 @@
<!--components/icon/myicon.wxml-->
<view
class="custom-class iconfont myicon-{{ type }}"
style="{{ color ? 'color: ' + color + ';' : '' }}{{ size ? 'font-size: ' + size + 'px;' : '' }}"
bind:tap="onClick">
<view wx:if="{{ info !== null }}" class="myicon__info">{{ info }}</view>
</view>

@ -1,60 +0,0 @@
@font-face {font-family: "iconfont";
src: url('iconfont.eot?t=1572072148156'); /* IE9 */
src: url('iconfont.eot?t=1572072148156#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAc4AAsAAAAADRQAAAbrAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDbAqMLIofATYCJAMgCxIABCAFhG0HgQEbKgvIHodxLLIq1Ks3F1L/P27690UoTUggNTpnZlWlot9osYmlEFpWpAfCRGHzyrSUqQnyxb0EwOXuf7dMsmgTrwGSP0ZZnE1fFOEGrAECREQfrbX6qqG8zz806+XE1jC3BolM0kQkJFGPlAopETIlRTTCs6m0V4mbfTNAACh9jYJ6la/qQIrAMMKAqbWRB2kjFzHhR6QZkoET42a0CkjScDn2CABWZj+PfpCWFABDRsAO1PQrzKB+wufzcW+3FyYkOpA2VwwA9mkAOIBRAAiAisHSBYAXaFRGqKH3EQwAWFIYyRM+Zz7P/Dz78+rP53d30zRVTWWVCMv/yVMAiELAIDgJEqAUQGSG2dwWBT4HKVaUQTYQoJnIBgw0G9mAQKuQDThoNbJBAjofo4AEACBNTlAA6AvQH2AbYKwgQxgvJuFyKA2rynoOiqOMQ1EqLZPjssTE/gShYli59kEs18dp0NUbDVq9sU6nHx9u1wUX6EMdxshCezhsiESM6EkYpSn1kZDeG3wVQn+jYHDWnaAuFHrND+BdFxcM9t4c0kci/baEDV0bEPIpn0ZQODybPfWsN7b5eSbDI/rsy374lhdZLXsO+A627ffuG8QUxWg2tbPip5GBSdLWoG576LXg8E4A//62gwgdxCX8ypD+uV7Wuc+7n2baDvoOWEib+CJtIQgGrYrWTRQrOb/zEabWhK6e+MaD8bzfGzelczxmdEn88VxAGdWVoOicSXjjqLwr8lFUaxx4OtkwMqCU+0UU2GB3AH88HUDQ083eOLar+QCqQ/rrqR7fn73+sn2Zq73oQECJ0MmyiDEcnkAhGsmNjZdSKGQM+Lpa43QGNbLTG68H6lx0Y7/5c7aZd1zgoBJ1HWjdF3c8h+j8dJsysDGhtGvPrnXxVC7R1oPvWWOvMrDBXu/u0HWui/fvnt3PGNi4YY8zo3F9LePUnevjN5RVFoi77Jr9ItwaZDZvyNYW2dIXHkNB577WAwhRZ6eEptGFLokH+nw3xbWEXG4jy+BONJfigS1Fk9nncaQJn8OTpuBjQN48DO9YMA2o1xPmxZBtJa0a4lRg1lh8DL5uFn5KU9LaRg5xwx+D1LjmHQv1suFXtxFfD+UND/cmlyr76ZibnV+GjTpbuwatRknWKKzRzc0e+0mWOvaIPE8Fvt5thIz9vezZJaaMufQhw0TetF17FB7z6Fpd2ZT3kt5P+j7puylswQG9B/RdR8CDAIzLDdNYQ/OSugFcdhODCcuXC5ilvQPMtfizON1A2wpmHI5Hy+L6pbOY3NS+xBL100dmoqbia66u/4/lqnLFe4rkim/3rkYNDai6WjCvFEw1qIbnffLAYM6PAcKzwXqVmv7EZmM//TXywlcD3xx5dVECL326qId+GLWtaRulf2j6jxUZpEZUE0qJRCRJJan5VU1kEOpfNd3nMkjyV4mEI9SihuRIUqxwxtgXQHchYEuXYgLU19WaT1BH9KNVvRK5nmxPLrHXqkfRHegTTZoa6kNh6dI+E1YNXDVIHvJGR8Gz8vYmq06+n9Xx7AI6dr+83GptL39WsKgU1dlsdUpDFodtSq+Nf8YxrzFcb/zGe8mbFfTifbmzHs9a68ydPeBAdcLN5uVnblUr1SRF5xae2fxmJ5RXcOZhrw1unSnIoylSXa0sAsubJy3AN/sfUJhLG7nHs3L3LaYVmx/NznWuhfSf/39byUnRde7c0m0nSremRdtKTyJr3txSO4lNNyReuSrjenHM1Sus4WSv6Bg9U3cN5mOPwldYN903fDPeCACA77G/QfeO3Iwbs/Adv44DukyhErQ/8Et4/nd3JwiXnqqZLC/4TUr/nBe99TYd1MXVSxsBvoAUyNI3dVJmBAAgt47CkDSeBVIgqU3pK4/AKT8C9Nu3GIDi8wC+oZUx3t8kiycT+Dtg8RVgovQBnNQgPWEeBSRaBkhIFQNlpKrTabFccCiOBoARFmuAKBwFDOsq4BQec+Bgfg9ICb4DCUU4UAxB7AVpQ7JSZS6BFwWzqmGmympyOiyiQKTygfVCo8fGu+pKFjIILre1QZWalNLuqBIcgmsTh7inNaaJokVlcTntqkrtdoLN5lS1uJxTBZOY1CSKLXnJyZahQ5NMTjtQyrgIeGKS3Eylwcz0ViZODpaFRVTn++sJGnnY8FwTM4bpDQQubtb1q6RKkkLQVxEHacZDGe42TaM0ol5soZLjXZzsVCr1YoGNtzqptAxvN5XARJSkaYXmFnmS9bksVJK0e2a/p1cBQGFXlwYYwhGBSCRBUUiKokE2FO28LfMmvC41l7FZZ1h55wyPMNPDO8gmj9XMM82CyDsau9nM9KrM5nEGiLS2nmGV8mbzaIvVZgMA') format('woff2'),
url('iconfont.woff?t=1572072148156') format('woff'),
url('iconfont.ttf?t=1572072148156') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('iconfont.svg?t=1572072148156#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.myicon-absent_student:before {
content: "\e635";
}
.myicon-present_student:before {
content: "\e60d";
}
.myicon-answer:before {
content: "\e647";
}
.myicon-question:before {
content: "\e648";
}
.myicon-internet_class:before {
content: "\e67f";
}
.myicon-close:before {
content: "\e637";
}
.myicon-add:before {
content: "\e600";
}
.myicon__info {
color: #fff;
left: 100%;
top: -.5em;
font-size: 0.5em;
padding: 0 0.3em;
text-align: center;
min-width: 1.2em;
line-height: 1.2;
position: absolute;
border-radius: 0.6em;
box-sizing: border-box;
background-color: #f44;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
font-family: PingFang SC, Helvetica Neue, Arial, sans-serif;
}

@ -1,739 +0,0 @@
/**
* https://github.com/jinke18/educoder_weapp
* @licence GPL-3.0
* @author jinke18
*/
import {Account, Course, Exercise, Question, School, Ui} from "./eduapi"
import {Session} from "./requests";
import {Cookie} from "./cookie";
/**
* 客户端类其method实现客户端各种操作将接口Promise化
* 要在此处实现网络数据及cookie的缓存
*/
export class Client{
constructor({session}={}){
if(!session) {
this.session = new Session();
}else {
this.session = session;
}
this.load_cached_cookies();
this.current_user = {};
this.callback={require_login: ()=>{}};
this.load_current_user();
}
load_current_user(){
var value = wx.getStorageSync('current_user');
if(value) {
this.current_user = value;
console.log("read current_user from storage");
console.log(this.current_user);
}
}
save_current_user(){
console.log("save current user to storage");
console.log(this.current_user);
wx.setStorageSync("current_user", this.current_user);
}
clear_current_user(){
wx.setStorageSync("current_user", {});
}
load_cached_cookies(){
try {
var value = wx.getStorageSync('_educoder_session');
if (value) {
this.session.set_cookie(new Cookie(value))
}
var value = wx.getStorageSync("autologin_trustie");
if (value) {
this.session.set_cookie(new Cookie(value));
}
console.log("read cookies from wx.Storage:");
console.log(this.session.cookies);
} catch (e) {
// Do something when catch error
console.error("there is not cached cookie")
console.error(e);
}
}
save_cookies(){
for(var cookie of Object.values(this.session.cookies)){
console.log("client save new cookie");
console.log(cookie);
cookie.save();
}
}
onRequireLogin(cd){
this.callback.require_login = cd;
}
require_login(){
wx.redirectTo({
url: "../pages/login/login"
});
wx.showToast({
title:"请先登陆",
icon: "none",
duration: 4000
});
}
login({login, password, autologin, success, fail, complete}){
return new Promise((resolve, reject) => {
Account.login({
session: this.session, login: login, password: password, autologin: autologin,
fail: error => {
if(typeof fail == "function") {
fail(error);
}
reject(error);
},
complete: complete,
success: res => {
this.save_cookies();
this.current_user = {...this.current_user, ...res.data};
console.log("current_user info update");
console.log(this.current_user);
this.get_user_info();
if (typeof success == "function") {
success(res);
}
resolve(res);
}
});
});
}
logout({success, fail, complete}={}){
return new Promise((resolve, reject) => {
Account.logout({session: this.session,
success: res=>{
if(typeof success == "function") {
success(res);
}
this.get_user_info();
resolve(res);
}, fail: error=>{
if(typeof fail =="function"){
fail(error);
}
reject(error);
}, complete: complete});
})
}
register({login, password, code, success, fail, complete}){
return new Promise((resolve, reject) => {
Account.register({session: this.session, login:login, password:password, code:code,
complete:complete,
success: res=>{
this.save_cookies();
if(typeof success == "function"){
success(res);
}
resolve(res);
},
fail:error=>{
if(typeof fail == "function") {
fail(error);
}
reject(error)
}
});
});
}
reset_password({login, code, new_password, new_password_confirmation, success, fail, complete}){
return new Promise((resolve, reject) => {
Account.reset_password({
session: this.session,
login: login,
code: code,
new_password: new_password,
new_password_confirmation: new_password_confirmation,
complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
}
resolve(res);
},
fail: error=>{
if(typeof fail == "function"){
fail(error);
}
reject(error);
}
})
})
}
get_user_info({success, fail, complete}={}){
return new Promise((resolve, reject) => {
Account.get_user_info({session: this.session, complete: complete,
success: res=>{
this.current_user = {...this.current_user, ...res.data};
console.log("current_user info update");
console.log(this.current_user);
if(typeof success == "function"){
success(res);
}
resolve(res);
this.save_current_user();
},
fail: error=>{
this.callback.require_login();
if(typeof fail == "function"){
fail(error);
}
reject(error);
}
})
});
}
get_verification_code_for_register({login, success, fail, complete}){
return new Promise((resolve, reject) => {
Account.get_verification_code({
session: this.session, login: login, type: 1,
complete: complete,
success: res => {
if (typeof success == "function") {
success(res);
}
resolve(res);
},
fail: error => {
if (typeof fail == "function") {
fail(error);
}
reject(error);
}
});
})
}
get_verification_code_for_reset_password({login, success, fail, complete}){
return new Promise((resolve, reject) => {
Account.get_verification_code({
session: this.session, login: login, type: 2,
complete: complete,
success: res => {
if (typeof success == "function") {
success(res);
}
resolve(res);
},
fail: error => {
if (typeof fail == "function") {
fail(error);
}
reject(error);
}
});
})
}
upload_avatar({avatar_path, success, fail, complete}){
console.log("upload_avatar");
return new Promise((resolve, reject) => {
Account.upload_avatar({
session: this.session,
avatar_path: avatar_path,
login: this.current_user.login,
complete: complete,
success: res => {
if (typeof success == "function") {
success(res);
}
resolve(res);
},
fail: error => {
if (typeof fail == "function") {
fail(error);
}
reject(error);
}
})
});
}
search_courses({limit=20, page=1, order="all", search="", success, fail, complete}={}){
return new Promise((resolve, reject) => {
Course.search({session: this.session, page: page, order: order, search: search, complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
}
resolve(res);
},
fail: error=>{
if(typeof fail == "function"){
fail(error);
}
reject(error);
}
});
});
}
get_course_info({course_id, success, fail, complete}){
return new Promise((resolve, reject) => {
Course.get_course_info({
session: this.session,
course_id: course_id,
complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
}
resolve(res);
},
fail: error=>{
if(typeof fail == "function"){
fail(error);
}
reject(error);
}
})
});
}
get_user_courses({category="",status="",sort_by="",sort_direction="",page=1, per_page=16, success, fail, complete}={}){
return new Promise((resolve, reject) => {
Account.get_user_courses({
session: this.session,
category: category,
status: status,
sort_by: sort_by,
sort_direction: sort_direction,
page: page,
per_page: per_page,
login: this.current_user.login,
complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
}
resolve(res);
},
fail: error=>{
if(typeof fail == "function"){
fail(error);
}
reject(error);
}
});
});
}
create_course({data, success, fail, complete}){
return new Promise((resolve,reject)=>{
Course.create({
session: this.session,
data: data,
complete: complete,
success: res => {
if (typeof success == "function") {
success(res);
}
resolve(res);
},
fail: error => {
if (typeof fail == "function") {
fail(error);
}
reject(error);
}
})
});
}
join_course({invite_code, professor=null,assistant_professor=null,student=null, success, fail, complete}){
return new Promise((resolve, reject)=>{
Course.join({session: this.session, invite_code: invite_code, professor: professor, assistant_professor: assistant_professor, student: student, complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
}
resolve(res);
},
fail: error=>{
if(typeof fail == "function"){
fail(error);
}
reject(error);
}
})
});
}
get_course_files({course_id, page_size=15, page=1,search="",sort=0, sort_type="created_on", success, fail, complete}){
return new Promise((resolve, reject) => {
Course.get_files({
session: this.session,course_id:course_id, page_size: page_size, page:page, search: search, sort: sort, sort_type: sort_type, complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
};
resolve(res);
},
fail: error=>{
if(typeof success == "function"){
fail(error);
}
reject(error);
}
});
});
}
get_course_students({course_id, order=1,sort="asc",page=1, limit=20, course_group_id="", success, fail, complete}){
return new Promise((resolve, reject) => {
Course.get_students({
session: this.session,
course_id: course_id,
order: order,
sort: sort,
page: page,
limit: limit,
course_group_id: course_group_id,
complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
};
resolve(res);
},
fail: error=>{
if(typeof success == "function"){
fail(error);
}
reject(error);
}
})
})
}
search_exercises({course_id, type="", page=1, limit=15, success, fail, complete}){
return new Promise((resolve, reject) => {
Exercise.search({
session: this.session,
course_id: course_id,
type: type,
page: page,
limit: limit,
complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
};
resolve(res);
},
fail: error=>{
if(typeof fail == "function"){
fail(error);
}
reject(error);
}
})
});
}
start_answer_exercise({exercise_id, success, fail, complete}){
return new Promise((resolve, reject) => {
Exercise.start_answer({
session: this.session,
exercise_id: exercise_id,
login: this.current_user.login,
complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
};
resolve(res);
},
fail: error=>{
if(typeof fail == "function"){
fail(error);
}
reject(error);
}
})
});
}
answer_question({question_id, exercise_choice_id,answer_text ,success, fail, complete}){
return new Promise((resolve, reject) =>{
Question.answer({
session: this.session,
question_id: question_id,
answer_text: answer_text,
exercise_choice_id: exercise_choice_id,
complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
};
resolve(res);
},
fail: error=>{
if(typeof fail == "function"){
fail(error);
}
reject(error);
}
})
} );
}
create_question({exercise_id, data,success, fail, complete}){
return new Promise((resolve, reject) => {
Question.create({
session: this.session,
exercise_id,
data,
complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
}
resolve(res);
},
fail: error=>{
if(typeof fail == "function"){
fail(error);
}
reject(error);
}
});
});
}
update_question({question_id, data, success, fail, complete}){
return new Promise((resolve, reject) => {
Question.update({
session: this.session,
question_id,
data,
complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
}
resolve(res);
},
fail: error=>{
if(typeof fail == "function"){
fail(error);
}
reject(error);
}
});
});
}
create_exercise({course_id ,exercise_name, exercise_description, success, fail, complete}){
return new Promise((resolve, reject) => {
Exercise.create({
session: this.session,
course_id,
exercise_name,
exercise_description,
complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
}
resolve(res);
},
fail: error=>{
if(typeof fail == "function"){
fail(error);
}
reject(error);
}
});
});
}
update_exercise({exercise_id, exercise_name, exercise_description, success, fail, complete}){
return new Promise((resolve, reject) => {
Exercise.update({
session: this.session,
exercise_id,
exercise_name,
exercise_description,
complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
}
resolve(res);
},
fail: error=>{
if(typeof fail == "function"){
fail(error);
}
reject(error);
}
})
})
}
get_exercise_edit({exercise_id, success, fail, complete}){
return new Promise((resolve, reject) => {
Exercise.get_edit({
session: this.session,
exercise_id,
complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
}
resolve(res);
},
fail: error=>{
if(typeof fail == "function"){
fail(error);
}
reject(error);
}
})
});
}
publish_exercise({course_id, exercise_ids, end_time, success, fail, complete}){
return new Promise((resolve, reject) => {
Exercise.publish({
session: this.session,
course_id,
exercise_ids,
end_time,
complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
}
resolve(res);
},
fail: error=>{
if(typeof fail == "function"){
fail(error);
}
reject(error);
}
})
})
}
/**
* @todo change limit default 10
*/
get_exercise_result({ exercise_id, sort = "asc", page = 1, limit=50, success, fail, complete}){
return new Promise((resolve, reject)=>{
Exercise.get_result({
session: this.session,
exercise_id, sort, page, limit,
success: res => {
if (typeof success == "function") {
success(res);
}
resolve(res);
},
fail: error => {
if (typeof fail == "function") {
fail(error);
}
reject(error);
}
})
})
}
get_exercise_grade({exercise_id,order="end_at", search="", success, fail, complete}){
return new Promise((resolve, reject) => {
Exercise.get_grade({
session: this.session,
exercise_id,
order,
search,
complete: complete,
success: res => {
if (typeof success == "function") {
success(res);
}
resolve(res);
},
fail: error => {
if (typeof fail == "function") {
fail(error);
}
reject(error);
}
});
});
}
save_exercise({exercise_id, success, fail, complete}){
return new Promise((resolve, reject) => {
Exercise.save({
session: this.session,
exercise_id: exercise_id,
complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
};
resolve(res);
},
fail: error=>{
if(typeof success == "function"){
fail(error);
}
reject(error);
}
})
});
}
commit_exercise({exercise_id, commit_method=1, success, fail , complete}){
return new Promise((resolve, reject) => {
Exercise.commit({
session: this.session,
exercise_id: exercise_id,
commit_method: commit_method,
complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
};
resolve(res);
},
fail: error=>{
if(typeof success == "function"){
fail(error);
}
reject(error);
}
})
});
}
search_schools({search = "", success, fail, complete} = {}){
return new Promise((resolve, reject) => {
School.search({
session: this.session, search: search, complete: complete,
success: res => {
if (typeof success == "function") {
success(res);
}
resolve(res);
},
fail: error => {
if (typeof fail == "function") {
fail(error);
}
reject(error);
}
});
});
}
get_home_page({success, fail, complete}={}){
return new Promise((resolve, reject) => {
Ui.get_home_page({
session: this.session,
complete: complete,
success: res=>{
if(typeof success == "function"){
success(res);
};
resolve(res);
},
fail: error=>{
if(typeof success == "function"){
fail(error);
}
reject(error);
}
})
})
}
}

@ -1,49 +0,0 @@
/**
* https://github.com/jinke18/educoder_weapp
* @licence GPL-3.0
* @author jinke18
*/
/**
* Cookie类用于网络中cookie的各类操作
*/
export class Cookie{
/**
*
* example: "autologin_trustie=8acaa2b09d5056c0e9d82519052276b9d4a524e9; domain=.educoder.net ; path=/; expires=Fri, 29 Nov 2019 15:32:53 -0000; HttpOnly"
*/
constructor(str){
this.origin_str = str;
this.analyse_cookie(str);
}
analyse_cookie(str){
this.string = str.slice(0, str.indexOf(";"));
let idx = this.string.indexOf("=");
this.key = this.string.slice(0, idx);
this.value = this.string.slice(idx+1);
}
toString(){
return this.string;
//return this.key + "=" + this.value;
}
save(){
if(!this.key){
console.error("cookie保存时: 没有键值key");
}
wx.setStorageSync(this.key, this.origin_str);
}
static load(key){
return new Cookie(wx.getStorageSync(key));
}
}
function test() {
//used to test the class Cookie
cookie_str = "autologin_trustie=8acaa2b09d5056c0e9d82519052276b9d4a524e9; domain=.educoder.net ; path=/; expires=Fri, 29 Nov 2019 15:32:53 -0000; HttpOnly";
cookie = new Cookie(cookie_str);
}
//test();

@ -1,648 +0,0 @@
/**
* https://github.com/jinke18/educoder_weapp
* @licence GPL-3.0
* @author jinke18
*/
const api_base_url = "https://www.educoder.net/api";
/**
* https://www.educoder.net的api接口通过其实现用户与后端进行交互
*/
export class Account{
static register({session, login, password, code, success, fail, complete}){
let data = {login: login, password: password, code: code};
session.request({
url: api_base_url + "/accounts/register.json",
method: "POST",
data:data,
success(res) {
if("status" in res.data && res.data.status<0){
fail(new Error(res.data.message));
return;
}
success(res);
},
fail: fail,
complete: complete
})
}
static login({session, login, password, autologin, success, fail, complete}) {
console.log("login");
let data = {login: login, password: password, autologin: autologin};
session.request({
url: api_base_url + "/accounts/login.json",
method: "POST",
data: data,
header: {"content-type": "application/json"},
success: (res)=>{
if("status" in res.data && res.data.status<0){
fail(new Error(res.data.message));
return;
}
console.log("type of success:"+ typeof success);
if(typeof success == "function") {
success(res);
}
},
fail: fail,
complete: complete
});
}
static logout({session, success, fail, complete}){
session.request({
url: api_base_url + "/accounts/logout.json",
method: "GET",
success: success,
fail: fail,
complete: complete
})
}
static get_verification_code({session, login, type, success, fail, complete}){
let data = {login: login, type: type};
//@notice type: 验证码用途 type=1=> 注册 type=2 => 重置密码
session.request({
url: api_base_url + "/accounts/get_verification_code.json",
method: "GET",
data: data,
success: success,
fail: fail,
complete: complete
})
}
static attendance({session, success, fail, complete}){
session.request({
url: api_base_url + "/users/attendance.json",
success(res) {
if("status" in res.data && res.data.status<0){
fail(new Error(res.data.status));
return;
}
if(typeof success == "function"){
success(res);
}
},
fail: fail,
complete: complete
})
}
static get_user_info({session, success, fail, complete}){
let data = {school: 1}
session.request({
url: api_base_url + "/users/get_user_info.json",
method: "GET",
data: data,
success: success,
fail: fail,
complete: complete
})
}
static get_user_courses({session,login,category="",status="",sort_by="",sort_direction="",page=1, per_page=16, success, fail, complete}){
let data = {page: page, per_page: per_page, category:category, status: status, sort_by: sort_by, sort_direction: sort_direction};
return session.request({
url: api_base_url + "/users/"+login +"/courses.json",
method: "GET",
data: data,
success: success,
fail: fail,
complete: complete
})
}
static reset_password({session, login, code, new_password, new_password_confirmation, success, fail, complete}){
let data ={
login: login,
code: code,
new_password: new_password,
new_password_confirmation: new_password_confirmation
};
return session.request({
url: api_base_url + "/accounts/reset_password.json",
method: "POST",
data: data,
success(res) {
if("status" in res.data && res.data.status<0){
fail(new Error(res.data.message));
return;
}
success(res);
},
fail: fail,
complete: complete
})
}
static set_password({}){
}
static upload_avatar({session, login, avatar_path, success, fail, complete}){
var avatar_data=null;
wx.getFileSystemManager().readFile({
filePath: avatar_path,
encoding: "base64",
success:res=> {
console.log("in success");
console.log(res);
avatar_data ="data:image/jpeg;base64,"+res.data;
console.log("upload_avatar");
console.info(avatar_data);
session.request({
url: "https://www.educoder.net/api/users/accounts/"+login+"/avatar.json",
method: "PUT",
data: {image: avatar_data},
success: success,
fail: fail,
complete: complete
})
},
fail: error=>{
console.error(error);
}
});
}
}
export class School{
static search({ session, search = "", success, fail, complete}){
let data = {search: search};
session.request({
url: api_base_url + "/schools/school_list.json",
method: "GET",
data: data,
success: res => {
if ("status" in res.data && res.data.status < 0) {
fail(new Error(res.data.message));
return;
}
if (typeof success == "function") {
success(res);
}
},
fail: fail,
complete: complete
})
}
}
export class Course{
static create({session, data, success, fail, complete}){
return session.request({
url: api_base_url + "/courses.json",
method: "POST",
data: data,
success: res => {
if ("status" in res.data && res.data.status < 0) {
fail(new Error(res.data.message));
return;
}
if (typeof success == "function") {
success(res);
}
},
fail: fail,
complete: complete
})
}
static update({session, course_id, data, success, fail, complete}){
return session.request({
})
}
static search({session, limit=20, page=1, order="all", search="", success, fail, complete}){
let data = {limit: limit, page: page, order: order, search: search};
return session.request({
url: api_base_url + "/courses.json",
method: "GET",
data: data,
success: success,
fail: fail,
complete: complete
})
}
static join({session, invite_code, professor=null,assistant_professor=null,student=null, success, fail, complete}){
let data = {
invite_code: invite_code,
professor: professor,
assistant_professor: assistant_professor,
student: student
};
return session.request({
url: api_base_url + "/courses/apply_to_join_course.json",
method:"POST",
data: data,
success: res=>{
if ("status" in res.data) {
switch(res.data.status) {
case 402:
fail(new Error(res.data.message || "请完善个人信息后重试"));
return;
default:
if (res.data.status < 0) {
fail(new Error(res.data.message));
return;
}
}
if(typeof success == "function"){
success(res);
}
}
},
fail: fail,
complete: complete
})
}
static get_user_info({session, course_id, success, fail, complete}){
let data = {course_id: course_id, school: 1}
return session.request({
url: api_base_url + "/users/get_user_info.json",
method: "GET",
data: data,
success: success,
fail: fail,
complete: complete
})
}
static get_files({session, course_id, page_size=15, page=1,search="",sort=0, sort_type="created_on", success, fail, complete}){
let data = {course_id: course_id, page_size: page_size, page: page, search: search, sort: sort, sort_type: sort_type};
return session.request({
url: api_base_url + "/files.json",
method: "GET",
data: data,
success: success,
fail: fail,
complete: complete
})
}
static get_students({session, course_id, order=1,sort="asc",page=1, limit=20, course_group_id="", success, fail, complete}){
let data = {
order: order,
sort: sort,
page: page,
limit: limit,
course_group_id: course_group_id
};
return session.request({
url: api_base_url + "/courses/" + course_id + "/students.json",
method: "GET",
data: data,
success(res){
if("status" in res.data && "message" in res.data){
fail(new Error(res.data.message));
return;
}
success(res);
},
fail: fail,
complete: complete
})
}
static get_course_info({session, course_id, success, fail, complete}){
session.request({
url: api_base_url + "/courses/"+course_id+"/top_banner.json",
method : "GET",
success: success,
fail: fail,
complete: complete
})
}
}
export class Exercise{
static search({session,course_id, page=1, limit=15, success, fail, complete}){
let data = {page:page, limit:limit};
return session.request({
url: api_base_url + "/courses/"+course_id+"/exercises.json",
method: "GET",
data: data,
success(res) {
if ("status" in res.data ) {
if (res.data.status < 0){
fail(new Error(res.data.message));
return;
}
switch(res.data.status){
case 409:
fail(new Error(res.data.message));
return;
}
}
if (typeof success == "function") {
success(res);
}
},
fail:fail,
complete: complete
})
}
static start_answer({session, exercise_id, login, success, fail, complete}){
let data = {login: login};
return session.request({
url: api_base_url + "/exercises/" + exercise_id + "/start_answer.json",
method: "GET",
data: data,
success(res) {
if ("status" in res.data ) {
if (res.data.status < 0){
fail(new Error(res.data.message));
return;
}
switch(res.data.status){
case 409:
fail(new Error(res.data.message));
return;
}
}
if (typeof success == "function") {
success(res);
}
},
fail: fail,
complete: complete
})
}
static create({ session, course_id ,exercise_name, exercise_description, success, fail, complete}){
let data = {exercise_name:exercise_name, exercise_description: exercise_description};
return session.request({
url: api_base_url +`/courses/${course_id}/exercises.json`,
method: "POST",
data: data,
success(res) {
if ("status" in res.data) {
if (res.data.status < 0) {
fail(new Error(res.data.message));
return;
}
// switch (res.data.status) {
// case 409:
// fail(new Error(res.data.message));
// return;
// }
}
if (typeof success == "function") {
success(res);
}
},
fail: fail,
complete: complete
})
}
static update({session, exercise_id, exercise_name, exercise_description, success, fail, complete}){
let data = {session, exercise_id, exercise_name, exercise_description, success, fail, complete};
session.request({
url: api_base_url+`/exercises/${exercise_id}.json`,
method: "PUT",
data: data,
success(res) {
if ("status" in res.data) {
if (res.data.status < 0) {
fail(new Error(res.data.message));
return;
}
// switch (res.data.status) {
// case 409:
// fail(new Error(res.data.message));
// return;
// }
}
if (typeof success == "function") {
success(res);
}
},
fail: fail,
complete: complete
})
}
static get_edit({session, exercise_id, success, fail, complete}){
session.request({
url: api_base_url + `/exercises/${exercise_id}/edit.json`,
method: "GET",
success(res) {
if ("status" in res.data) {
if (res.data.status < 0) {
fail(new Error(res.data.message));
return;
}
}
if (typeof success == "function") {
success(res);
}
},
fail: fail,
complete: complete
})
}
/**@todo limit change */
static get_result({session, exercise_id, sort="asc", page=1, limit=50,success, fail, complete}){
let data={sort, page, limit};
session.request({
url: api_base_url +`/exercises/${exercise_id}/exercise_result.json`,
method: "GET",
data,
success(res) {
if ("status" in res.data) {
if (res.data.status < 0) {
fail(new Error(res.data.message));
return;
}
}
if (typeof success == "function") {
success(res);
}
},
fail: fail,
complete: complete
})
}
static publish({session,course_id, exercise_ids, end_time, success, fail, complete}){
let data = {check_ids: exercise_ids, end_time};
session.request({
url: api_base_url+ `/courses/${course_id}/exercises/publish.json`,
method: "POST",
data: data,
success(res) {
if ("status" in res.data) {
if (res.data.status < 0) {
fail(new Error(res.data.message));
return;
}
// switch (res.data.status) {
// case 409:
// fail(new Error(res.data.message));
// return;
// }
}
if (typeof success == "function") {
success(res);
}
},
fail: fail,
complete: complete
})
}
static save({session, exercise_id, success, fail, complete}){
return session.request({
url: api_base_url + "/exercises/"+exercise_id+"/begin_commit.json",
method: "GET",
success: success,
fail: fail,
complete: complete
})
}
static get_grade({session, exercise_id,order="end_at", search="", success, fail, complete}){
let data = {order, search};
session.request({
url: api_base_url + `/exercises/${exercise_id}/exercise_lists.json`,
method: "GET",
data: data,
success(res) {
if ("status" in res.data) {
if (res.data.status < 0) {
fail(new Error(res.data.message));
return;
}
// switch (res.data.status) {
// case 409:
// fail(new Error(res.data.message));
// return;
// }
}
if (typeof success == "function") {
success(res);
}
},
fail: fail,
complete: complete
})
}
static commit({session, exercise_id, commit_method=1, success, fail, complete}){
let data = {commit_method: commit_method};
return session.request({
url: api_base_url+ "/exercises/"+exercise_id+"/commit_exercise.json",
method: "POST",
data: data,
success: success,
fail: fail,
complete: complete
})
}
}
export class Question {
static create({session, exercise_id, data, success, fail, complete}){
let data_ = {...data, exercise_bank_id: exercise_id};
session.request({
url: api_base_url + `/exercises/${exercise_id}/exercise_questions.json`,
method: "POST",
data: data_,
success(res) {
if ("status" in res.data) {
if (res.data.status < 0) {
fail(new Error(res.data.message));
return;
}
// switch (res.data.status) {
// case 409:
// fail(new Error(res.data.message));
// return;
// }
}
if (typeof success == "function") {
success(res);
}
},
fail: fail,
complete: complete
})
}
static update({session, question_id, data, success, fail, complete}){
session.request({
url: api_base_url + `/exercise_questions/${question_id}.json`,
method: "PUT",
data: data,
success(res) {
if ("status" in res.data) {
if (res.data.status < 0) {
fail(new Error(res.data.message));
return;
}
// switch (res.data.status) {
// case 409:
// fail(new Error(res.data.message));
// return;
// }
}
if (typeof success == "function") {
success(res);
}
},
fail: fail,
complete: complete
})
}
static answer({session, question_id, exercise_choice_id, answer_text, success, fail, complete}){
let data = {};
if(exercise_choice_id){data["exercise_choice_id"] = exercise_choice_id;}
if(answer_text){data["answer_text"] = answer_text}
console.log("eduapi.js: request data", data, " url", api_base_url + "/exercise_questions/" + question_id + "/exercise_answers.json");
return session.request({
url: api_base_url + "/exercise_questions/" + question_id +"/exercise_answers.json",
method: "POST",
data: data,
success: res=>{
if("status" in res.data){
if(res.data.status<0){
fail(new Error(res.data.message));
return;
}
/*switch (res.data.status) {
case -1:
fail(new Error("???"));
return;
}*/
}
if(typeof success == "function") {
success(res);
}
},
fail: error=>{
// console.error("wx.request error:");
// console.log(error);
// console.log(error.toString());
// console.log(error.message);
// console.log(error.errMsg);
let e = error;
if(!("message" in error)){
if("errMsg" in error){
e = new Error(error.errMsg);
}
}
if(typeof fail == "function") {
fail(e);
}
},
complete: complete
})
}
}
export class Ui{
static get_home_page({session, success, fail, complete}){
return session.request({
session: this.session,
url: api_base_url + "/home/index.json",
method: "GET",
success: res=> {
if ("status" in res.data) {
if (res.data.status < 0){
fail(new Error(res.data.message));
return;
}
}
if (typeof success == "function") {
success(res);
}
},
fail:fail,
complete: complete
})
}
}

@ -1,11 +0,0 @@
/**
* @todo: Error类, to be finished
*/
export default class EduError extends Error{
constructor({message=null, code=-1}){
super(message);
this.code = code;
}
}

@ -1,88 +0,0 @@
/**
* https://github.com/jinke18/educoder_weapp
* @licence GPL-3.0
* @author jinke18
*/
import {Cookie} from "./cookie";
/**
* 微信网络请求接口的封装使其能够支持简单的cookie保存发送功能
*/
export class Session {
cookies = {}
constructor(){
}
set_cookie(cookie){
this.cookies[cookie.key] = cookie;
}
get cookie_str(){
let cookie_strs = Object.values(this.cookies).map(cookie=>cookie.toString());
return cookie_strs.join(";");
}
request({url, data, header, method, dataType, responseType, success, fail, complete}) {
console.log("request data and headers: ");
console.log({url: url, data: data, header: {...header, "Cookie": this.cookie_str}, method: method, dataType: dataType, responseType: responseType})
return wx.request({
url: url,
data: data,
header: {...header, "Cookie": this.cookie_str},
method: method,
dataType: dataType,
responseType: responseType,
success: (res) => {
let cookies =[];
if ("cookies" in res) {
cookies = res["cookies"];
} else if(res.header["Set-Cookie"]){
//@todo: better solution
cookies = res.header["Set-Cookie"].replace(", ", "@@").replace(",","||").replace("@@",", ").split("||");
}else{
console.warn("request.js: no cookies setted in this request");
}
console.log("raw_cookies", cookies);
for (var i = 0; i < cookies.length; i++) {
this.set_cookie(new Cookie(cookies[i]));
}
console.log("set new cookies:");
console.log(this.cookies);
if (typeof success == "function") {
success(res);
} else {
console.warn("requests.session.request.success is not function");
}
},
fail: fail,
complete: complete
});
}
uploadFile({url, filePath, name, header, formData, success, fail, complete}){
console.log("uploadFile");
console.log({
url: url,
filePath: filePath,
name: name,
header: {"content-type": "multipart/form-data", "Cookie": this.cookie_str, ...header},
formData: formData});
return wx.uploadFile({
url: url,
filePath: filePath,
name: name,
header: {"content-type": "multipart/form-data", "Cookie": this.cookie_str, ...header},
formData: formData,
success: success,
fail: fail,
complete: complete
})
}
downloadFile({url, header, filePath, success, fail, complete}){
return wx.downloadFile({
url: url,
header: {...header, "Cookie": this.cookie_str},
filePath: filePath,
success: success,
fail: fail,
complete: complete
})
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 943 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 773 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 719 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

File diff suppressed because one or more lines are too long

@ -1,28 +0,0 @@
module.exports = (subscription, initialStats, onChange) => {
console.log("query_bingding");
let stats = [...initialStats]
const remove = value => {
console.log("remove");
stats = stats.filter(target => target.id !== value.id)
return onChange(stats)
}
const upsert = value => {
console.log("upsert");
let existed = false;
stats = stats.map(target => (target.id === value.id ? ((existed = true), value) : target))
if (!existed) stats = [...stats,value]
return onChange(stats)
}
subscription.on('create', upsert)
subscription.on('update', upsert)
subscription.on('enter', upsert)
subscription.on('leave', remove)
subscription.on('delete', remove)
return () => {
subscription.off('create', upsert)
subscription.off('update', upsert)
subscription.off('enter', upsert)
subscription.off('leave', remove)
subscription.off('delete', remove)
}
}

@ -0,0 +1,15 @@
# Windows
[Dd]esktop.ini
Thumbs.db
$RECYCLE.BIN/
# macOS
.DS_Store
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
# Node.js
node_modules/

@ -0,0 +1,255 @@
import apiConfig from "../../apiConfig";
import {accountManager} from "../../../js/utils";
const app = getApp();
Page({
data: {
attachDir: global.config.attachDir,
action:"login",
msgType: "info",
code_button_text:"获取验证码",
action_text:{login:"登录",register:"注册", reset:"找回密码"},
pos:{login:1, register:2, reset:3}
},
cache:{},
showMsg({message, type="info", duration=2000}){
if(!message) return;
this.setData({
message,
msgType: type,
msgDuration: duration,
showMsg: false
});
wx.nextTick(()=>{
this.setData({showMsg: true});
})
},
hideMsg(){
this.setData({showMsg: false});
},
showError({message, duration}){
if(!message)
return
this.showMsg({message, duration, type:"error"});
},
onLogoLoad(){
this.setData({ logoLoaded: 1});
},
onLoginBlur({detail:{value:login}}){
this.validLogin(login);
},
validPhoneEmail: function ({login}) {
// a simple pre-check for input
let phoneReg = /^[1][0-9]{10}$/;
let emailReg = /.+@.+\..+/;
if(!phoneReg.test(login)&&!emailReg.test(login)){
return false;
}else
return true;
},
validLogin(login){
if(!login) {
return this.setData({showMsg:0, hasError:0});
}
let {action} = this.data;
if(this.cache.login==login&&this.cache.action==action)
return;
if(action=="login"){
// some use login to login, this case judge can be wrong
this.cache.action = action;
this.cache.login = login;
var api_name = 'weapps.check_account';
var type = 'login';
}else if(action=='register'){
var api_name = "weapps.check_account";
var type = "register";
if(!this.validPhoneEmail({login})){
this.cache.action = action;
this.cache.login = login;
this.showError({message:"请输入正确的邮箱或手机号", duration:0});
this.setData({hasError:0});
return;
}
}else if(action=='reset'){
var api_name = "accounts.valid_email_and_phone";
var type = 2;
if(!this.validPhoneEmail({login})){
this.cache.action = action;
this.cache.login = login;
this.showError({message:"请输入正确的手机号或邮箱", duration:0});
this.setData({hasError:0});
return;
}
}
app.api(api_name)({
login, type,
complete: res => {
this.cache.action = action;
this.cache.login = login;
}
})
.then(res=>{
this.setData({hasError:0});
this.hideMsg();
})
.catch(e=>{
var hasError = e.message.indexOf("网络")!=-1?2:1;
if(action=='login'){
hasError = e.message.indexOf('尚未注册')!=-1?1:0;
}
this.setData({ hasError});
if(hasError){
this.showError({message: e.message.replace(/\n/g," "), duration: 0});
}else{
this.hideMsg();
}
})
},
login_test(){
var data = global.accountManager.testAccount;
data.save_password = 1;
this.setData(data);
this.login(data);
// this.setStorage({...data,save_password:1});
},
login({ login, password, save_password, showToast=1}){
if(login==accountManager.testAccount.login)
password = accountManager.testAccount.password;
app.api("accounts.login")({login,password})
.then(res=>{
res.message="登录成功";
if (showToast)
this.showMsg(res);
let account = { ...res, login, password, save_password };
if(this.data.addaccount)
accountManager.addAccount(account,0);
accountManager.setCurrentAccount(account);
this.navBack();
})
.catch(e=>{
if (showToast){
this.showError(e);
}
})
},
register({login, password, code}){
app.api("accounts.register")({login, password, code})
.then(res=>{
this.showMsg(res);
this.navBack();
}).catch(e=>{
this.showError(e);
})
},
reset({ login, password: new_password, password_confirmation: new_password_confirmation, save_password ,code, no_login}){
app.api("accounts.reset_password")({login, new_password, new_password_confirmation, code})
.then(res=>{
res.message = "重置成功";
this.showMsg(res);
if(!no_login)
this.login({ login, password: new_password, save_password, showToast:0});
})
.catch(e=>{
this.showError(e);
})
},
getCode({login}){
if(this.data.action=="register")
var type = 1;
else if(this.data.action=="reset")
var type = 2;
else
return;
this.setData({code_status:2, code_button_text:"发送中"});
app.api("accounts.get_verification_code")({login, type})
.then(res=>{
res.message = "发送成功";
this.countDown();
this.showMsg(res);
}).catch(e=>{
this.showError(e);
this.setData({code_status:0, code_button_text:"获取验证码"})
})
},
clearCount(id){
clearInterval(id);
this.setData({code_status:0, code_button_text:"获取验证码"});
},
countDown(){
var count = 60;
this.setData({code_status: 1, code_button_text: count-- + "秒后重试"});
var id = setInterval(()=>{
if (count <= 0)
return this.clearCount(id);
this.setData({code_button_text:count-- + "秒后重试"})
}, 1000);
return id;
},
onSubmit({detail:{value, target}}){
if(!this.checkInput({value, action:target.id}))
return;
if (target.id == "code")
return this.getCode(value);
if (target.id != this.data.action){
this.setAction(target.id);
return this.validLogin(value.login);
}
this[target.id](value);
},
checkInput({value, action}){
if(action!=this.data.action && action!="code")
return true;
if (!value.login)
return this.showError({message: '请输入邮箱或手机号'}) && false;
if(action=="code")
return true;
if (!value.password)
return this.showError({message: '请输入密码'}) && false;
if(action=="login")
return true;
if(!value.code)
return this.showError({message: '请输入验证码' }) && false;
if(action=="register")
return true;
if(!value.password_confirmation)
return this.showError({message: '请再次输入密码'}) && false;
return true;
},
setAction(action){
let {pos} = this.data;
if(!(action in pos)||action==this.data.action)
return;
let tmp = pos[action];
pos[action] = pos[this.data.action];
pos[this.data.action] = tmp;
/*if(Math.random()>0.5){
for(var k in pos){
if(pos[k]!=1)
pos[k] = pos[k]==2?3:2;
}
}*/
this.setData({pos, action});
},
onLoad: function (options) {
let {action="login"} = options;
this.setAction(action);
this.setData(options);
if(!options.nostorage)
this.getStorage();
},
getStorage(){
let account = accountManager.getCurrentAccount();
if(account){
let {login, password, save_password=0} = account;
password = save_password?password:'';
this.setData({login, password, save_password});
}
},
navBack(){
setTimeout(()=>{
wx.navigateBack({
delta:1
});
}, 500);
}
})

@ -0,0 +1,8 @@
{
"usingComponents": {
"iconfont":"/components/iconfont/iconfont",
"mp-toptips":"/weui-miniprogram/toptips/toptips"
},
"navigationBarTitleText": "账号",
"navigationBarBackgroundColor": "#ffffff"
}

@ -0,0 +1,50 @@
<page-meta>
<navigation-bar title="{{action_text[action]}}" />
</page-meta>
<mp-toptips msg="{{message}}" type="{{msgType}}" show="{{showMsg}}" delay="{{msgDuration}}"/>
<view class="container">
<image class="logo {{logoLoaded?'':'hidden'}}" bindlongpress="login_test" src="{{attachDir}}751571" mode="aspectFit" bindload="onLogoLoad"></image>
<form class="account-form" bindsubmit="onSubmit">
<view class="inputs">
<view class="input-wrap">
<input name="login" bindblur="onLoginBlur" disabled="{{login_disabled}}" value="{{login}}" placeholder="{{action=='login'?'邮箱、手机号或用户名':'邮箱或手机号'}}">
</input>
</view>
<view class="input-wrap {{action!='login'?'':'hidden'}}">
<input name="code" placeholder="验证码">
</input>
<button id="code" type="secondary" size="mini" form-type="submit" loading="{{code_status==2}}" disabled="{{code_status||hasError==1}}" class="obtain-code">
{{code_button_text}}
</button>
</view>
<view class="input-wrap">
<input password name="password" value="{{password}}" placeholder="请输入密码">
</input>
</view>
<view class="input-wrap {{action=='reset'?'':'hidden'}}">
<input password name="password_confirmation" placeholder="请再次输入密码">
</input>
</view>
<view class="checkbox-wrap">
<switch type="checkbox" name="save_password" color="#00b0f0" hidden="{{hide_save}}" checked="{{save_password}}">保存密码<iconfont type="yiwen" info="密码将在本地加密保存" color="dimgrey" size="20" showtype="toast"/></switch>
<switch type="checkbox" class="no-login {{action=='reset'?'':'hidden'}}" name="no_login" color="#00b0f0">重置后不登录</switch>
</view>
</view>
<view class="actions">
<view class="pos{{pos.login}}">
<button id="login" disabled="{{hasError==1&&pos.login==1}}" form-type="submit">登录</button>
</view>
<view class="pos{{pos.register}}">
<button id="register" disabled="{{hasError==1&&pos.register==1}}" form-type="submit">注册</button>
</view>
<view class="pos{{pos.reset}}">
<button id="reset" disabled="{{hasError==1&&pos.reset==1}}" form-type="submit">{{pos.reset==1?'重置密码':'忘记密码?'}}</button>
</view>
</view>
</form>
</view>
<view class="foot">
<navigator class="agreement" hover-class="none" url="/markdown/account/agreement/agreement">
登录或注册即代表您同意 <text class="color-main agreement">用户协议</text>
</navigator>
</view>

@ -0,0 +1,118 @@
page{
position: relative;
background: white;
min-height: 100vh;
}
.logo{
width:54px;
height: 54px;
display: block;
padding: 48px 0 4px 0;
margin: 0 auto;
transition: 1s all ease;
}
.checkbox-wrap,.input-wrap{
transition: 1s all ease;
max-height: 50px;
display: flex;
margin: 5px 26px;
overflow: hidden;
}
.checkbox-wrap{
font-size: 14px;
margin: 0px 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.checkbox-wrap switch{
transform: scale(0.82);
}
.input-wrap>input{
padding: 8px 10px;
border-radius: 4px;
border: 1px #00b0f0 solid;
flex:auto;
}
.input-wrap>input[disabled]{
background: #dfdfdf;
}
.obtain-code{
margin-left: 12px!important;
white-space: nowrap;
display: flex!important;
justify-content: center;
align-items: center;
flex: none;
}
.input-wrap.hidden{
max-height: 0px;
}
image.hidden,switch.no-login.hidden{
opacity: 0;
}
switch.no-login{
float: left;
transition: 1s all ease;
}
.actions{
position: relative;
margin: 0 26px;
height: 160px;
}
.actions button{
transition: 1s all ease;
}
.actions>view{
width: 100%;
position: absolute;
transition: 1s all ease;
}
.actions>.pos1{
top: 26px;
}
.pos1>button{
background: #00b0f0;
color:white;
}
.pos1>button[disabled]{
background-color: #00b0f096!important;
color: white!important;
}
.actions>.pos2{
top:74px;
}
.pos2>button{
color: #00b0f0;
border:#00b0f0 1px solid;
background: white;
}
.actions>.pos3{
top: 0px;
}
.pos3>button{
font-size: 14px;
color: #00b0f0;
background: transparent;
transform: translateX(240rpx);
padding: 0px;
}
.pos3>button::after{
border: none!important;
}
.foot{
position: absolute;
bottom:0px;
width: 100%;
display: flex;
}
navigator.agreement{
margin: 10px auto;
font-size: 14px;
text-align: center;
color: dimgray;
}
text.agreement{
text-decoration: underline;
font-weight: bold;
}

@ -0,0 +1,158 @@
import {accountManager} from "../../../js/utils";
const app = getApp();
Page({
data: {
eduImgDir: global.config.eduImgDir,
buttons:[{text:"删除",type:"warn"}]
},
onLoad: function (options) {
console.log("Load", Date.now());
this.refresh()
.then(res=>{
this.loaded = 1;
})
},
onReady(){
app.reportPageHistory();
},
onShow(){
console.log("show", Date.now());
if(this.loaded)
this.refresh();
},
navBack(){
wx.navigateBack({
delta:1
});
},
refresh(){
console.log("start", Date.now());
this.setData({loading:1});
return app.syncUser()
.then(res=>{
if (this.oldNum!=null && accountManager.getAccounts().length>this.oldNum && !this.data.currentAccountSaved && this.currentAccount) {
// 增加了新账号,将未保存的老账号信息保存
accountManager.addAccount(this.currentAccount);
this.setData({ currentAccountSaved: 1 });
}
// 更新当前账号信息, 如果更改了信息,可以显示最新的信息 @todo 完善
let {user_id, image_url, show_name} = res.user;
if(show_name)
accountManager.updateAccount({user_id, image_url, name:show_name});
else
accountManager.updateAccount({ user_id, image_url });
let addedAccounts = accountManager.getAccounts();
var currentAccount = accountManager.getCurrentAccount();
var currentAccountSaved = 0;
if(res.user.user_id&&res.user.user_id!=2){
if(!currentAccount){
var currentAccount = { login: res.user.phone || res.user.email, password: "", save_password: 0, image_url: res.user.image_url, name: res.user.real_name||res.user.name, user_id: res.user.user_id};
accountManager.setCurrentAccount(currentAccount);
}
for (var account of addedAccounts){
if(account.user_id==currentAccount.user_id)
currentAccountSaved = 1;
}
if(!currentAccountSaved)
var accounts = [...addedAccounts, currentAccount];
else
var accounts = addedAccounts;
}else{
var accounts = addedAccounts;
}
this.accounts = accounts;
accounts = accounts.map(i=>{
return {user_id:i.user_id,name: i.name,login: i.login, image_url:i.image_url};
})
this.currentAccount = currentAccount;
this.setData({ accounts, user: {user_id }, currentAccountSaved,loading:0},()=>{
console.log("渲染完成", Date.now());
});
console.log("end", Date.now());
}).catch(e=>{
})
},
deleteAccount({user_id}){
accountManager.removeAccount({user_id});
if(this.data.user.user_id==user_id){
accountManager.clearCurrentAccount();
app.client.session.cookies = "";
app.client.synch = 0;
}
},
removeAccount2(e){
let { currentTarget: { dataset: { id: user_id } } } = e;
this.deleteAccount({user_id});
setTimeout(()=>{
this.refresh();
},300);
},
removeAccount1(e){
let { currentTarget: { dataset: { id: user_id } } } = e;
var info = this.data.user.user_id == user_id?'退出登录并移除':"移除此账号";
wx.showActionSheet({
itemList: [info],
success:res=>{
if(res.tapIndex==0){
this.deleteAccount({user_id});
this.refresh();
}
}
})
},
switchAccount(e){
let { currentTarget: {dataset: {id: user_id}}} = e;
if (user_id==this.data.user.user_id) return;
if (accountManager.getAccounts().length<5&&!this.data.currentAccountSaved&&this.currentAccount){
accountManager.addAccount(this.currentAccount);
this.setData({currentAccountSaved:1});
}
let account = this.accounts.filter(i=>i.user_id == user_id) [0];
if(!account.save_password){
this.navToAccount({login:account.login, message:"账号过期,需重新登录",login_disabled:1, msgType:"error"});
}else{
app.api("accounts.login")(account)
.then(res=>{
res.login = account.login; // 使用登录的手机号或邮箱而不是login字符
accountManager.updateAccount(res);
this.showMsg({message:"切换成功"});
accountManager.setCurrentAccount(account);
this.refresh();
}).catch(e=>{
accountManager.forgetPassword({user_id});
this.navToAccount({login:account.login, message:"账号过期,需重新登录",login_disabled:1, msgType:"error"});
})
}
},
navToAccount({login="", message="", msgType="info", login_disabled=""}={}){
wx.navigateTo({
url:`../account/account?nostorage=1&showMsg=1&message=${message}&msgType=${msgType}&msgDuration=0&&save_password=1&addaccount=1&login=${login}&login_disabled=${login_disabled}`
});
},
addAccount(){
this.oldNum = accountManager.getAccounts().length
if(this.oldNum>=5)
return wx.showToast({
title: '已达数量上限,无法添加',icon:"none"
})
this.navToAccount({message:"密码将在本地加密保存"});
},
showMsg({message,duration=1000}){
if(this.timeoutId)
clearTimeout(this.timeoutId);
this.setData({ message, showMessage:1});
this.timeoutId = setTimeout(()=>{
this.hideMsg()
},duration);
},
hideMsg(){
this.setData({showMessage:0});
this.timeoutId = 0;
}
})

@ -0,0 +1,7 @@
{
"usingComponents": {
"mp-slideview": "/weui-miniprogram/slideview/slideview",
"mp-toptips":"/weui-miniprogram/toptips/toptips"
},
"navigationBarTitleText": "切换账号"
}

@ -0,0 +1,34 @@
<page-meta>
<navigation-bar loading="{{loading}}" />
</page-meta>
<view>
<mp-toptips msg="{{message}}" type="info" show="{{message&&showMessage}}" delay="0"/>
<!--view class="message single-line {{showMessage&&message?'':'hidden'}}">
{{message}}
</view-->
<view class="empty" wx:if="{{accounts.length==0}}">
<text>暂无账号</text>
</view>
<view wx:else class="empty">
轻触切换账号
</view>
<mp-slideview wx:for="{{accounts}}" data-id="{{item.user_id}}" buttons="{{buttons}}"
bindtap="switchAccount" bindlongpress="removeAccount1" bindbuttontap="removeAccount2"
wx:key="user_id">
<view class="account" >
<image src="{{eduImgDir}}{{item.image_url}}" class="avatar"></image>
<view class="info">
<view class="name">{{item.name}}</view>
<view class="login">{{item.login}}</view>
</view>
<view class="status" wx:if="{{item.user_id==user.user_id}}">
<icon type="success_no_circle" color="#00b0f0"></icon>
</view>
</view>
</mp-slideview>
<button class="addAccount" type="main" bindtap="addAccount">添加账号</button>
<button class="nav-back" type="main" plain="1" bindtap="navBack">返回</button>
</view>

@ -0,0 +1,54 @@
.message{
position: fixed;
top: 0;
left: 0;
right: 0;
display: flex;
justify-content: center;
align-items: center;
height: 28px;
transition: all 0.26s ease;
background: #00b0f0;
color: white;
z-index: 100;
font-size: 14px;
}
.message.hidden{
transform: translateY(-32px);
}
.account{
display: flex;
padding: 12px;
margin-bottom: 1px;
background: white;
align-items: center;
}
.avatar{
border-radius: 50%;
height: 40px;
width: 40px;
margin: 0 12px 0 4px;
}
.info{
flex: auto;
}
.login{
font-size: 12px;
color: dimgray;
}
.addAccount{
margin-top: 24px;
}
.nav-back{
margin-top: 8px;
}
.empty{
text-align: center;
color: dimgray;
font-size: 13px;
margin: 8px 0;
}

@ -0,0 +1,50 @@
const app = getApp();
Page({
data: {
login: "***"
},
onFormReset(){
wx.navigateBack({
delta:1
});
},
changePassword({detail:{value}}){
if(app.user().user_id==2)
return wx.showToast({
title: '请登陆后操作哦', icon: "none"
});
if(!value.password||!value.old_password||!value.password_confirmation)
return wx.showToast({
title: '请输入完整哦', icon: "none"
});
if(value.password_confirmation!=value.password)
return wx.showToast({
title: '两次输入的新密码不一致哦',icon:"none"
});
if(value.old_password==value.password)
return wx.showToast({
title: '输入的新旧密码是一样的哦', icon: "none"
});
app.api("users.accounts.password")(value)
.then(res=>{
if(res.status==0)
res.message="修改成功";
app.showMsg(res);
setTimeout(()=>{
wx.navigateBack({
delta: 1
});
},1000);
})
.catch(e=>{
app.showError(e);
})
},
onLoad: function (options) {
app.checkLogin();
let {login} = global.accountManager.getCurrentAccount();
this.setData({login});
}
})

@ -0,0 +1,4 @@
{
"usingComponents": {},
"navigationBarTitleText": "修改密码"
}

@ -0,0 +1,20 @@
<view class="container">
<view class="tip">
您正在修改账号{{login}}的密码
</view>
<form bindsubmit="changePassword" bindreset="onFormReset">
<view class="input-wrap">
<input password auto-focus name="old_password" placeholder="原密码"/>
</view>
<view class="input-wrap">
<input password name="password" placeholder="新密码"/>
</view>
<view class="input-wrap">
<input password name="password_confirmation" placeholder="请再次输入新密码"/>
</view>
<view class="buttons">
<button form-type="submit" type="main">确认</button>
<button form-type="reset" type="main" plain>返回</button>
</view>
</form>
</view>

@ -0,0 +1,24 @@
page{
background: white;
}
.container{
padding-top: 20px;
}
.tip{
color: dimgray;
font-size: 12px;
text-align: center;
}
.input-wrap{
border-radius: 4px;
border: 1px solid #00b0f0;
margin: 16px 18px;
}
.input-wrap>input{
padding: 8px 10px;
}
.buttons>button{
margin-top: 10px;
}

@ -0,0 +1,27 @@
.form-item {
background: #fefefe;
display: flex;
justify-content: space-between;
align-items: center;
padding: 14px 12px;
margin-bottom: 1px;
}
.form-item>.value {
text-align: right;
flex: auto;
}
.form-item>.key {
display: inline-block;
flex: none;
}
.form-item .tip {
font-size: 12px;
color: dimgray;
}
.form-item>switch {
transform: scale(0.8);
}

@ -0,0 +1,106 @@
const app = getApp();
import {throttle} from "../../../../js/utils";
const risk_message = "检测到内容含有敏感词汇";
function msgSecCheck({name="", remarks=""}){
let content = name+","+ remarks;
console.log("msgsecCheck", content);
if(this.tmp_promise&&this.tmp_content==content)
return this.tmp_promise;
this.tmp_promise = app.openapi("security.msgSecCheck")({content});
this.tmp_content = content;
this.tmp_promise.then(res=>{
if(res.errCode==87014){
wx.showToast({
title: risk_message,icon:"none",duration:2600
})
}
})
return this.tmp_promise;
}
Component({
properties: {
show:{
type:Boolean,
value:false
},
school_name:String,
school_id:Number
},
data: {
buttons:[
{text:"取消"},
{text:"提交"}
],
name: "",
remarks:""
},
methods: {
cancel(){
this.setData({show:false});
},
updateName({detail:{value}}){
this.setData({name:value});
this.throttledMsgSecCheck(this.data);
},
updateRemarks({detail:{value}}){
this.setData({remarks:value});
this.throttledMsgSecCheck(this.data);
},
checkInput(){
if(!this.data.name)
return wx.showToast({
title: '请输入子单位名称',icon:"none"
})&&false;
return true;
},
throttledMsgSecCheck:throttle(msgSecCheck, 1000, {}),
msgSecCheck,
submit(){
if(!this.checkInput())
return;
let {remarks, name, school_id} = this.data;
wx.showLoading({
title: '检查内容中...'
})
this.msgSecCheck({name, remarks}).then(res=>{
if(res.errCode==0){
wx.showLoading({
title: '正在添加'
})
app.api("add_department_applies")({school_id, name, remarks})
.then(res=>{
this.triggerEvent("success",res);
wx.hideLoading();
wx.showToast({
title: '添加成功'
})
this.setData({show:false});
}).catch(e=>{
app.showError(e);
})
}else{
wx.hideLoading();
wx.showModal({
content:risk_message,
showCancel:false
})
}
}).catch(e=>{
//app.showError(e);
wx.showToast({
title: '出错了\n>_<',icon:"none"
})
throw e;
})
},
onTapButton({detail}){
if(detail.index==0)
this.cancel()
else if(detail.index==1)
this.submit()
}
}
})

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"mp-dialog": "/weui-miniprogram/dialog/dialog"
}
}

@ -0,0 +1,16 @@
<mp-dialog show="{{show}}" title="添加子单位" bindbuttontap="onTapButton" buttons="{{buttons}}">
<view wx:if="{{show}}">
<view class="input-wrap">
<text class="key">所属单位</text>
<input class="value disable" disabled="1" value="{{school_name}}"></input>
</view>
<view class="input-wrap">
<text class="key require">子单位名称</text>
<input class="value" bindinput="updateName"></input>
</view>
<view class="input-wrap">
<text class="key">说明</text>
<input class="value" bindinput="updateRemarks"></input>
</view>
</view>
</mp-dialog>

@ -0,0 +1,24 @@
.input-wrap{
display: flex;
align-items: center;
}
.key{
flex: 1;
font-size: 14px;
}
.key.require::before{
content: "*";
color: red;
}
.value.disable{
background: #eeeeee;
}
.value{
flex: 2;
border-radius: 4px;
padding: 4px 8px;
border: 1px lightgrey solid;
margin:4px;
text-align: left;
color: black;
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,333 @@
import {throttle} from "../../../js/utils";
const app = getApp();
var locationData = require("./data.js");
const risk_message = "检测到名称中含有敏感词汇";
function msgSecCheck({name, nickname}){
let content = name+","+nickname;
console.log("secCheck", content);
if(this.tmp_promise&&this.tmp_content==content)
return this.tmp_promise;
this.tmp_promise = app.openapi("security.msgSecCheck")({content});
this.tmp_content = content;
this.tmp_promise.then(res=>{
if(res.errCode==87014){
wx.showToast({
title: risk_message,icon:"none",duration:2600
})
}
}).catch(e=>{
this.tmp_promise = null;
})
return this.tmp_promise;
}
Page({
data: {
eduImgDir:global.config.eduImgDir,
locations:[],
cities:[],
identities:[{value:"teacher", text:"教师"},{value:"student",text:"学生"},{value:"professional", text:"专业人士"}],
technicalTitles:{
0:["教授","副教授","讲师","助教"],
2:["企业管理者","部门管理者","高级工程师","工程师","助理工程师"]
}
},
refresh(){
app.api("users.accounts")().then(res=>{
this.originInfo = res;
res.authen = res.authentication == "certified";
res.pro_authen = res.professional_certification == "certified";
this.setData(res);
this.setInfo(res);
}).catch(e=>{
this.showError(e);
});
},
msgSecCheck,
throttledMsgSecCheck: throttle(msgSecCheck, 1000, {}),
updateNickName({detail:{value}}){
this.throttledMsgSecCheck({...this.data, nickname: value});
},
updateName({detail:{value}}){
//this.setData({name: value});
this.throttledMsgSecCheck({...this.data, name: value});
},
onGetUserInfo(e){
// 填入微信个人信息
console.log(e);
let {detail:{userInfo}} = e;
if(!userInfo)
return wx.showToast({
title: '获取信息失败',icon:"none"
})
let {nickName:nickname, gender, province:location, city:location_city, avatarUrl} = userInfo;
if(gender)
gender = 1 - gender;
let location_index = this.data.locations.indexOf(location);
let cities = locationData[location] || locationData['北京'];
let city_index = cities.indexOf(location_city);
this.setData({nickname, gender, cities, location, location_city, city_index, location_index});
avatarUrl = "" //@todo
if(avatarUrl){
wx.showModal({
title:"提示",
content:"是否使用微信头像",
confirmText:"使用",
success: res=>{
if(res.confirm)
this.updateWechatAvatar({avatarUrl});
},
complete: ()=>{
wx.showToast({
title: '填入成功'
});
}
});
}else{
wx.showToast({
title: '填入成功'
});
}
},
updateWechatAvatar({avatarUrl}){
// @todo
},
setInfo(res){
let locations = Object.keys(locationData);
let location_index = locations.indexOf(res.location);
let cities = locationData[res.location] || locationData['北京'];
let city_index = cities.indexOf(res.location_city);
let identity_index = -1;
let { identities } = this.data;
for (var i = 0; i < identities.length; i++) {
if (identities[i].value == res.identity) {
identity_index = i;
break;
}
}
var technical_index = -1;
if (identity_index == 0 || identity_index == 2) {
let technicals = this.data.technicalTitles[identity_index];
for (var i = 0; i < technicals.length; i++) {
if (technicals[i] == res.technical_title) {
technical_index = i;
break;
}
}
}
this.setData({ locations, cities, location_index, city_index, identity_index, technical_index });
if (res.school_id) {
this.changeSchoolDepartment(res);
}
},
updateAccount(data){
app.api("users.accounts",{method:"PUT"})(data).then(res=>{
app.showMsg(res);
setTimeout(res=>{
wx.navigateBack({
delta:1
});
},400)
}).catch(e=>{
this.showError(e);
})
},
onGenderChange({detail:{value}}){
value = parseInt(value);
this.setData({gender:value});
},
onLocationChange(e){
let {detail:{value:[location_index, city_index]}} = e;
this.setData({location_index, city_index});
this.setData({cities:locationData[this.data.locations[location_index]]});
},
onCitiesChange(e){
let {detail:{value,column}} = e;
if(column==0)
this.setData({cities:locationData[this.data.locations[value]],location_index:value ,city_index:-1});
},
onIdentityChange(e){
let {detail:{value}} = e;
this.setData({identity_index:value, technical_index:-1});
},
onTechnicalChange(e){
let { detail: { value } } = e;
this.setData({technical_index: value});
},
onDepartmentChange(e){
let { detail: { value } } = e;
this.setData({department_index:value});
},
refreshDepartments({detail}){
this.setData({department_id: detail.id});
this.pullDepartments();
},
pullDepartments(){
let {school_id, department_id} = this.data;
app.api("schools.departments.for_option")({school_id})
.then(res=>{
let {departments} = res;
let department_index = -1;
for(var i=0; i<departments.length;i++){
if(departments[i].id==department_id){
department_index = i;
break;
}
}
this.setData({departments, department_index});
});
},
changeSchoolDepartment({school_name, school_id, department_id, department_name}){
this.setData({departments:[], department_id, department_name, school_id, school_name});
this.pullDepartments({school_id,department_id});
},
catchAvatar(){
/*wx.showActionSheet({
itemList: ["更改头像", "使用微信头像"],
})*/
app.navigateTo({url:"{image_crop}"});
},
addDepartment(){
this.setData({showAddDepartment:true});
},
onLoad: function (options) {
this.refresh();
},
onShow: function () {
if(this.secondShow){ //更新头像
app.api("users.accounts")()
.then(res=>{
let {avatar_url} = res;
this.setData({avatar_url});
})
} //@todo: better solution?
this.secondShow = 1;
let data = wx.getStorageSync("SET-SCHOOL-DEPARTMENT-KEY");
if(data){
this.changeSchoolDepartment(data);
wx.setStorageSync("SET-SCHOOL-DEPARTMENT-KEY",'');
}
},
checkProInfo(value){
let info = this.originInfo;
if (info.identity != value.identity)
return true;
if(info.identity=='student'){
if(info.student_id!=value.student_id)
return true;
}else{
if (info.technical_title!=value.technical_title)
return true;
}
if(info.school_id!=value.school_id||info.department_id!=value.department_id)
return true
return false;
},
onSubmit(e){
let {detail:{value}} = e;
if(!this.checkInput(value)) return;
if(this.data.pro_authen&&this.checkProInfo(value)){
wx.showModal({
title: '确认修改',
content: '您修改了职业信息,保存后认证信息会清空,需重新进行职业认证,\n是否保存',
confirmText:"保存",
success:res=>{
if(res.confirm)
this.saveInfo(value);
}
});
}else{
this.saveInfo(value);
}
},
checkInput(value){
var showTip = key=>{
this.showError({ message: `请填写${key}`});
return false;
}
if(!value.nickname)
return showTip("昵称");
if(!value.name)
return showTip("姓名");
if(value.gender==null)
return showTip("性别");
if (!value.location)
return showTip("所在地");
if (!value.location_city)
return showTip("所在地");
if(!value.identity)
return showTip("职业");
if(value.identity=='student'){
if(!value.student_id)
return showTip("学号");
}else{
if (!value.technical_title)
return showTip("职称");
}
if(!value.school_id)
return showTip("学校/单位");
if (!value.department_id)
return showTip("院系/部门");
return true;
},
saveInfo(value){
wx.showLoading({
title: '提交中'
});
value.gender = parseInt(value.gender);
this.msgSecCheck(value).then(res=>{
if(res.errCode==87014){
wx.hideLoading();
wx.showModal({
content: risk_message,showCancel:false
})
}else{
app.api("users.accounts", { method: "PUT" })(value)
.then(res => {
app.syncUser({refresh:1});//更新用户信息
res.message = "更新成功";
wx.hideLoading();
app.showMsg(res);
setTimeout(() => {
wx.navigateBack({
delta: 1
})
}, 420);
}).catch(e => {
wx.hideLoading();
this.showError(e);
})
}
}).catch(e=>{
wx.hideLoading();
wx.showToast({
title: '信息检查时出错了\n︿',icon:"none"
});
throw e;
})
},
onTap(e){
let { target: { id } } = e;
if (id) {
if ((id == 'name' || id == "gender") && this.data.authen)
this.showError({ message: `请到官网重新实名认证以更改${id == "name" ? "姓名" : id == 'gender' ? "性别" : '此信息'}`, during: 1400 });
this.setData({edited:1});
}
},
showError(e){
let {message:error, during=2000} = e;
if(error){
error = error.replace(/\n/g, "");
this.setData({showError:1, error});
setTimeout(()=>{
this.setData({showError:0});
}, during);
}
}
})

@ -0,0 +1,6 @@
{
"usingComponents": {
"add-department":"./add-department/add-department"
},
"navigationBarTitleText": "个人信息"
}

@ -0,0 +1,74 @@
<view class="profile">
<form bindtap="onTap" bindsubmit="onSubmit" bindreset="refresh">
<view class="sticky">
<view class="header">
<view class="error {{showError?'show':''}}"><text class="single-line">{{error}}</text></view>
<button wx:if="{{!base_info_completed}}" open-type="getUserInfo" bindgetuserinfo="onGetUserInfo" type="main" size="mini" lang="zh_CN">填入微信个人信息</button>
<view wx:elif="{{!edited}}">点击相应项编辑信息</view>
<button wx:else form-type="reset" size="mini" type="main">重置修改</button>
<button class="submit" form-type="submit" size="mini" type="main">
<icon type="success_no_circle" color="white"></icon>
<text>保存</text>
</button>
</view>
</view>
<view class="form-item">
<text class="key">昵称头像</text>
<input class="value" id="nickname" placeholder="输入昵称" name="nickname" value="{{nickname}}" bindinput="updateNickName"></input>
<image class="avatar" bindtap="catchAvatar" src="{{eduImgDir}}{{avatar_url}}"></image>
</view>
<view class="form-item">
<view class="key">
<text>姓名</text>
</view>
<input class="value" id="name" placeholder="输入姓名" bindinput="updateName" disabled="{{authen}}" name="name" value="{{name}}"></input>
</view>
<view class="form-item">
<text class="key">显示姓名
<text class="tip">关闭后将展示您的昵称</text>
</text>
<switch color="#00b0f0" id="show_realname" name="show_realname" checked="{{show_realname}}"></switch>
</view>
<view class="form-item">
<text>性别</text>
<picker class="value" id="gender" name="gender" range="{{['男','女']}}" bindcolumnchange="log" value="{{gender}}" disabled="{{authen}}" bindchange="onGenderChange">{{gender==0?'男':gender==1?'女':'未设置'}}</picker>
</view>
<view class="form-item">
<text>所在地</text>
<picker class="value" id="location-city" bindcolumnchange="onCitiesChange" bindchange="onLocationChange" range="{{[locations,cities]}}" value="{{[location_index, city_index]}}" mode="multiSelector">{{locations[location_index]||location||''}} {{cities[city_index]||location_city||'请选择'}}</picker>
<input hidden="1" disabled="1" name="location" value="{{locations[location_index]||location||''}}"></input>
<input hidden="1" disabled="1" name="location_city" value="{{cities[city_index]||location_city||''}}"></input>
</view>
<view class="gap"></view>
<view class="form-item">
<text class="key">职业</text>
<picker class="value" id="identity" range="{{identities}}" bindchange="onIdentityChange" range-key="text" value="{{identity_index}}">{{identities[identity_index].text||'请选择'}}</picker>
<input hidden="1" name="identity" disabled="1" value="{{identities[identity_index].value}}"/>
</view>
<view wx:if="{{identity_index==1}}" class="form-item">
<text class="key">学号</text>
<input class="value" id="school_id" type="number" name="student_id" placeholder="输入学号" cursor-spacing="100" value="{{student_id}}"></input>
</view>
<view wx:elif="{{identity_index==0||identity_index==2}}" class="form-item">
<text class="key">职称</text>
<picker class="value" id="technical_title" bindchange="onTechnicalChange" range="{{technicalTitles[identity_index]}}" value="{{technical_index}}">{{technicalTitles[identity_index][technical_index]||'请选择'}}</picker>
<input hidden="1" disabled="1" name="technical_title" value="{{technicalTitles[identity_index][technical_index]}}"></input>
</view>
<view class="form-item">
<text class="key">{{identity_index<0?'学校/单位':identity_index==2?'单位':'学校'}}</text>
<input hidden="1" disabled="1" name="school_id" type="number" value="{{school_id}}"></input>
<navigator class="value" id="school" hover-class="none" url="/account/pages/profile/school_select/school_select">{{school_name||'请选择'}}</navigator>
</view>
<view class="form-item" wx:if="{{school_id}}">
<text class="key">{{identity_index<0?'院系/部门':identity_index==2?'部门':'院系'}}</text>
<picker wx:if="{{departments.length>0}}" class="value" id="department" range="{{departments}}" range-key="name" bindchange="onDepartmentChange" value="{{department_index}}">{{departments[department_index].name||'请选择'}}</picker>
<view wx:else class="value none" bindtap="addDepartment">暂无子单位,点击申请新增</view>
<input hidden="1" disabled="1" name="department_id" type="number" value="{{departments[department_index].id}}"></input>
</view>
</form>
<view class="footer">
<text>*EduCoder将确保您所提供的信息均处于严格保密状态不会泄露</text>
</view>
</view>
<button wx:if="{{false}}" open-type="getUserInfo" bindgetuserinfo="onGetUserInfo" lang="zh_CN" type="secondary">使用微信信息</button>
<add-department bindsuccess="refreshDepartments" school_id="{{school_id}}" school_name="{{school_name}}" show="{{showAddDepartment}}"/>

@ -0,0 +1,67 @@
@import "../form-item-common.wxss";
.form-item>.value.none{
color: dimgray;
}
.profile, page {
height: 100%;
}
.sticky{
top: 0;
position: sticky;
z-index: 10000;
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
background: #00b0f0;
color: white;
padding: 0 8px 0 16px;
position: relative;
}
.header>.error {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
transition: 0.72s all ease;
background: #fa5151;
color: white;
padding: 0 20px;
display: flex;
justify-content: center;
align-items: center;
z-index: -1;
opacity: 0;
}
.error.show {
opacity: 1;
z-index: 100;
}
.header>button {
margin: 2px 0;
display: flex;
align-items: center;
}
.gap {
height: 3px;
}
.avatar {
border-radius: 50%;
height: 30px;
width: 30px;
margin: 0 14px;
}
.footer {
font-size: 24rpx;
text-align: center;
margin: 12px 0;
color: grey;
}

@ -0,0 +1,44 @@
const app = getApp();
Component({
properties: {
_id: Number,
name: String
},
data: {
departments: []
},
methods: {
addDepartment(){
this.setData({showAddDepartment:true});
},
onTapHeader() {
if (!this.data.departments || this.data.departments.length == 0) {
this.getDepartments();
}
this.setData({ showDepartments: !this.data.showDepartments });
},
getDepartments(){
app.api("schools.departments.for_option")({ school_id: this.data._id})
.then(res => {
let { departments } = res;
if(departments.length==0)
var no_department = true;
else
var no_department = false
this.setData({departments, no_department});
});
},
onTapDepartment(e) {
;
let { target: { dataset: { name: department_name, id: department_id } } } = e;
if (!department_id) return;
let { _id: school_id, name: school_name } = this.data;
let data = { school_id, school_name, department_id, department_name };
wx.setStorageSync("SET-SCHOOL-DEPARTMENT-KEY", data);
wx.navigateBack({
delta:1
});
}
}
})

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"add-department":"../../add-department/add-department"
}
}

@ -0,0 +1,13 @@
<view class="school">
<view class="header" bindtap="onTapHeader">
<view class="triangle {{showDepartments?'rotate':''}}"></view>
<view>{{name}}</view>
</view>
<view class="departments {{showDepartments?'':'hidden'}}" bindtap="onTapDepartment">
<view class="department" wx:for="{{departments}}" data-id="{{item.id}}" data-name="{{item.name}}">{{item.name}}</view>
<block wx:if="{{departments.length==0&&no_department}}">
<view class="department" data-id="-1" data-name="" bindlongpress="addDepartment">暂无子单位,长按申请新增</view>
</block>
</view>
</view>
<add-department show="{{showAddDepartment}}" school_id="{{_id}}" school_name="{{name}}" bindsuccess="getDepartments" wx:if="{{departments.length==0&&no_department}}"/>

@ -0,0 +1,36 @@
.school{
background: white;
border-radius: 4px;
padding: 8px 12px;
}
.header{
display: flex;
align-items: center;
font-weight: bold;
}
.triangle{
height: 0px;
width: 0px;
border-top: solid 4px transparent;
border-bottom: solid 4px transparent;
border-left: solid 5px #222222;
transition: 1s ease all;
margin: 2px 7px 2px 5px;
}
.rotate{
transform: rotate(90deg);
}
.departments{
overflow: scroll;
max-height: 210px;
transition: 1px all ease;
}
.departments.hidden{
max-height: 0px;
}
.department{
padding: 4px;
padding-left: 22px;
line-height: 20px;
height: 20px;
}

@ -0,0 +1,44 @@
const app = getApp();
Page({
data: {
schools:[]
},
onConfirm({detail:{value}}){
;
if(!value) return;
this.search({keyword:value});
},
onSubmit({detail:{value}}){
;
if(!value.keyword) return;
this.search({keyword:value.keyword});
},
search({keyword}){
return app.api("schools.for_option")({keyword})
.then(res => {
let { schools } = res;
if (schools.length > 100) {
schools = schools.slice(0, 100);
}
this.setData({ schools });
if(schools.length==0)
wx.showToast({
title:"没有相关单位",icon:"none"
})
})
},
clearKeyword(){
this.setData({schools:[],_keyword:'',keyword:''});
},
onInput({detail:{value}}){
this.setData({_keyword: value})
},
onLoad: function (options) {
let {keyword} = options;
if(keyword){
this.setData({keyword, _keyword: keyword});
this.search({keyword})
}
}
})

@ -0,0 +1,6 @@
{
"usingComponents": {
"school-item": "./school-item/school-item"
},
"navigationBarTitleText": "选择单位"
}

@ -0,0 +1,17 @@
<view class="body">
<form bindsubmit="onSubmit">
<view class="search">
<view class="search-bar">
<icon type="search"></icon>
<input placeholder="学校/单位" auto-focus="1" name="keyword" bindinput="onInput" value="{{keyword}}" bindconfirm="onConfirm"></input>
<icon wx:if="{{_keyword}}" type="clear" bindtap="clearKeyword"></icon>
</view>
<button form-type="submit" size="mini" type="main">搜索</button>
</view>
</form>
<scroll-view class="schools" scroll-y="1">
<view class="school-wrap" wx:for="{{schools}}" wx:key="id">
<school-item _id="{{item.id}}" name="{{item.name}}"/>
</view>
</scroll-view>
</view>

@ -0,0 +1,34 @@
.body{
display: flex;
flex-direction: column;
height: 100vh;
}
.search{
display: flex;
padding: 0 12px 2px 12px;
background: white;
flex: none;
}
.search-bar{
display: flex;
padding: 0 8px;
border-radius: 4px;
border: 1px solid #00b0f0;
flex: auto;
align-items: center;
margin-right: 16px;
}
.search-bar>input{
flex: auto;
padding: 3px;
}
.search>button{
flex: none;
}
.schools{
flex: 1 1 1px;
height: 1px;
}
.school-wrap{
margin-bottom: 1px;
}

@ -0,0 +1,22 @@
const app = getApp();
Page({
data: {
info:'获取中...'
},
onLoad: function (options) {
this.setData({info:"获取中"})
wx.cloud.callFunction({name:"login"})
.then(res=>{
this.setData(res.result);
}).catch(e=>{
this.setData({info:"获取失败"});
})
},
copy(){
if(this.data.openid)
wx.setClipboardData({data: "openid: " + this.data.openid});
else
this.onLoad();
}
})

@ -0,0 +1,6 @@
<view>
<view>
openid: {{openid||info}}
</view>
<button class="copy" bindtap="copy" type="main" size="mini">{{openid?'复制':'重新获取'}}</button>
</view>

@ -0,0 +1,3 @@
button.copy{
margin: 20px 12px;
}

@ -0,0 +1,30 @@
Component({
properties: {
data:Object
},
data: {
},
methods: {
onTapBody(){
let {options, page} = this.data.data;
let query = Object.keys(options).map(i=>`${i}=${options[i]}`).join("&");
let url = "/"+page + "?" + query;
console.log(url)
wx.navigateTo({
url
})
},
onTapKey(e){
let {currentTarget:{dataset:{key}}} = e;
console.log(e);
let data = this.data.data[key];
let detail = {[key]: data};
console.log(detail);
this.triggerEvent("condition", detail, {bubbles: true});
}
}
})

@ -0,0 +1,16 @@
<view class="history {{data.isCrawl?'crawl':''}}" bindtap="onTapBody">
<view class="header" >
<text>{{data.time_show}} </text> {{data.isCrawl?'爬虫访问':""}}
</view>
<view class="body">
<text data-key="page" catchtap="onTapKey">{{data.page_show}} </text>
<text data-key="options" catchtap="onTapKey"> {{data.options_show}}</text>
</view>
<view class="single-line" data-key="scene" catchtap="onTapKey">
{{data.scene}} {{data.sceneDesc}}
</view>
<view class="detail" data-key="openid" catchtap="onTapKey">
{{data.openid}}
</view>
</view>

@ -0,0 +1,19 @@
.history{
background: white;
padding: 12px;
border-radius: 8px;
}
.history.crawl{
border: 2px green solid;
}
.header,
.detail{
color: dimgray;
font-size: 12px;
}
.single-line{
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
}

@ -0,0 +1,147 @@
import {checkAuth} from "../../utils";
import {getFormatDatetime} from "../../../js/utils"
const app = getApp();
const db = wx.cloud.database();
const pageHistoryCollection = db.collection("pageHistory");
Page({
data: {
data:[]
},
subscribe(){
wx.requestSubscribeMessage({
tmplIds: ["atZ4ZFfGIPxTiFGTCtkwvnfqjBA-fM7o1p5OiJQA_0Y"],
fail:console.error
})
},
onLoad: function (options) {
this.options = {page:1, limit:20};
if(checkAuth()){
this.refresh({refresh:1});
pageHistoryCollection.count()
.then(res=>{
console.log(res);
this.setData({total: res.total});
})
}else{
wx.showModal({
title:"提示",
content:"您没有权限访问",
success: res=>{
wx.navigateBack({
delta: 1
});
}
})
}
},
async refresh({refresh=0}={}){
if(!checkAuth())
return;
if(refresh){
this.options.page = 1;
}else{
this.options.page ++
}
let {page,limit} = this.options;
let skip = (page-1)*limit;
console.log(skip, limit);
let query = pageHistoryCollection;
if(this.data.conditions){
query = query.where(this.data.conditions);
}
if(refresh){
query.count().then(res=>{
console.log(res);
this.setData({total: res.total});
})
}
let res = await query.orderBy("time", "desc").skip(skip).limit(limit).get();
let data = res.data.map(i=>{
i.time_show = getFormatDatetime(i.time);
i.options_show = JSON.stringify(i.options);
i.page_show = i.page.match(/\/([^\/]*$)/)[1];
return i;
});
console.log(data);
if(!refresh){
data = this.data.data.concat(data)
}
this.setData({data});
},
deleteMine(){
app.cloudapi("clearPageHistory")({
openid: app.globalData.openid
}).then(res=>{
console.log(res);
wx.showToast({
title: res.errMsg,
})
this.refresh({refresh:1});
}).catch(e=>{
wx.showToast({
title: e.errMsg,icon:"none"
})
})
},
onConditionChange(e){
let {detail} = e;
console.log(e);
console.log(detail);
let conditions = {...this.data.conditions, ...detail};
this.setData({conditions});
this.refresh({refresh:1})
.then(res=>{
wx.showToast({
title:"筛选成功"
})
})
},
deleteCondition(e){
console.log(e);
let {currentTarget:{dataset:{key}}} = e;
let conditions = this.data.conditions;
delete conditions[key];
this.setData({conditions});
this.refresh({refresh:1}).then(res=>{
wx.showToast({
title: '删除筛选成功'
})
})
},
onReady: function () {
},
onShow: function () {
},
onHide: function () {
},
onUnload: function () {
},
onPullDownRefresh: function () {
this.setData({conditions:""});
this.refresh({refresh:1}).then(res=>{
wx.showToast({
title: '刷新成功'
})
})
},
onReachBottom: function () {
this.refresh({refresh:0});
},
onShareAppMessage: function () {
}
})

@ -0,0 +1,8 @@
{
"usingComponents": {
"history-item":"./history-item/history-item"
},
"enablePullDownRefresh": true,
"onReachBottomDistance": 260,
"navigationBarTitleText": "访问记录"
}

@ -0,0 +1,15 @@
<view class="top-sticky flex-row">
<view class="item" wx:for="{{conditions}}" data-key="{{index}}" bindtap="deleteCondition">
{{index}}
</view>
</view>
<view class="list">
<view class="history-item-wrp" wx:for="{{data}}" bindcondition="onConditionChange">
<history-item data="{{item}}"/>
</view>
</view>
<view class="footer">
<button wx:if="{{false}}" type="main" bindtap="subscribe">订阅({{total}})</button>
<button type="main" bindtap="onPullDownRefresh">刷新({{total}})</button>
<button type="secondary" bindtap="deleteMine">删除我的</button>
</view>

@ -0,0 +1,33 @@
.top-sticky{
position: sticky;
top: 0;
overflow-x: scroll;
background: white;
}
.top-sticky>.item{
margin: 4px;
padding: 4px 8px;
background: #00b0f0;
color: white;
font-size: 12px;
border-radius: 50px;
}
.list{
padding-bottom: 40px;
}
.history-item-wrp{
margin: 3px 12px 7px 12px;
}
.footer{
display: flex;
bottom: 0;
position: fixed;
right: 0;
left: 0;
}
.footer>button{
flex: 1;
border-radius: 0;
}

@ -0,0 +1,3 @@
export function checkAuth(){
return getApp().globalData.openid == "oqugK431bepFwW6TGrHpQTerPkI0"
}

@ -0,0 +1,314 @@
import config from "./config";
import { client } from "./js/client";
wx.cloud.init({
traceUser: true,
env: "educoder",
success:console.log,
fail: console.error
});
App({
globalData: {
openid:wx.getStorageSync('openid'),
isCrawl: false, //是否是爬虫
scene: -1
},
client,
openapi(name){
return ({success, fail, complete, ...data}={})=>{
return this.callOpenapi({name, data, success,fail, complete});
}
},
cloudapi(name){
return ({success, fail, complete, ...data}={})=>{
return this.callCloudfunction({
name:"cloudapi", data:{name, data}, success, fail, complete
});
}
},
callOpenapi({name, data, success, fail, complete}){
return this.callCloudfunction({name:"openapi", data:{name, data}, success, fail, complete});
},
cloudfunction(name){
return ({success, fail, complete, ...data}={})=>{
return this.callCloudfunction({name, data, success, fail, complete});
}
},
callCloudfunction({name, data, success, fail, complete}){
return new Promise((resolve, reject)=>{
return wx.cloud.callFunction({name, data,
success:res=>{
success&&success(res.result);
resolve(res.result);
complete&&complete(res.result);
}, fail:e=>{
fail&&fail(e);
reject(e);
complete&&complete(e);
}
});
});
},
reportPageHistory:function({page, route, scene, status, options}={}){
let data = {}
page = page||getCurrentPages()[getCurrentPages().length-1];
if(page){
Object.assign(data,
{
route: page.route,
page: page.route, //@desprate
scene: this.globalData.scene,
status: 200,
options: page.options
})
}
if(route){
data.page = data.route = route;
}
if(scene)
data.scene = scene;
if(status)
data.status = status;
if(options)
data.options = options;
this.cloudapi("reportPageHistory")(data).catch(e=>{
global.realTimeLog.error("report history error", e);
wx.reportMonitor('2', 1);
})
},
realTimeLog:global.realTimeLog,
api(name, config) { return client.api(name, config) },
callApi(options) { return client.callApi(options) },
user() { return client.user },
syncUser(options) { return client.syncUser(options) },
updateUserInfo(info){return client.updateUserInfo(info)},
onLaunch: function (options) {
this.globalData.scene = options.scene;
if(options.scene==1129){
this.globalData.isCrawl = true;
// this.cloudfunction("login")({mpcrawl: true});
this.api("accounts.login")(global.accountManager.testAccount)
.then(res=>{
let account = { ...res, ...global.accountManager.testAccount};
global.accountManager.setCurrentAccount(account);
});
/* 访
wx.reportMonitor('1', 1);
global.realTimeLog.info("爬虫访问", options);
global.realTimeLog.setFilterMsg("mpcrawl");
*/
}else if(!this.globalData.openid){
this.cloudfunction("login")()
.then(res=>{
this.globalData.openid = res.openid;
wx.setStorage({
data: res.openid,
key: 'openid',
})
})
}
if (options.referrerInfo && options.referrerInfo.appId) {
var db = wx.cloud.database();
let { appId, extraData } = options.referrerInfo;
let { scene, path } = options;
db.collection("referrer_info")
.add({
data: {
appId, extraData, scene, path,
createdAt: db.serverDate()
}
})
}
/**
* 系统升级提醒
this.api("users.system_update")().then(res => {
if (res.system_update) {
let { subject = "升级服务通知", system_score} = res;
if(!system_score){
let {end_time} = res;
if(end_time){
const {getFormatDatetime} = require('./js/utils');
system_score = "为了给大家提供更优质的体验,平台正在对系统进行升级,期间系统响应会有一定延迟。系统将于"+getFormatDatetime(new Date(end_time))+"恢复正常。敬请谅解";
}else{
system_score = "为了给大家提供更优质的体验,平台正在对系统进行升级,期间系统响应会有一定延迟,敬请谅解";
}
}
wx.showModal({
title: subject,
content: system_score,
showCancel: false
})
}
}).catch(e=>{});
*/
if(global.config.saveFlow){
wx.showToast({
title: '省流模式已开启,默认不加载图片',icon:"none",duration:2600
})
}
},
onShow() {
if (client.user_id && client.user_id != 2)
client.getTidingInfo();
},
onPageNotFound(res) {
this.realTimeLog.error("page not find!! redirect", res);
this.reLaunch({ url: "{main}" });
},
showError(e, duration = 1500) {
wx.showToast({
title: e.message,
icon: "none",
duration
})
},
showMsg({message=""}, duration = 1500) {
wx.showToast({
title: message, duration
})
},
getPageUrl(url, root = "/") {
return url.replace(/{(.*)}/, function (match, name) {
for (var u of __wxConfig.pages) {
if (u.endsWith("/" + name))
return root + u;
}
for (var u of config.pages) {
if (u.endsWith("/" + name))
return root + u;
}
return match;
});
},
reLaunch({ url, success, fail, complete }) {
wx.reLaunch({ url: this.getPageUrl(url), success, fail, complete })
},
redirectTo({ url, success, fail, complete }) {
wx.redirectTo({ url: this.getPageUrl(url), success, fail, complete })
},
navigateTo({ url, success, fail, complete }) {
wx.navigateTo({ url: this.getPageUrl(url), success, fail, complete })
},
checkLogin({showModal=1, content="您需要登陆后才能使用该功能"}={}){
if(client.user.user_id==2){
if(showModal){
wx.showModal({
confirmText:"现在登录",
title:"提示",
content,
success:res=>{
if(res.confirm)
this.navigateTo({url:"{account}"})
}
})
}
return false;
}else
return true;
},
addToFavorites({title, query, imageUrl}={}){
let db = wx.cloud.database();
let data = {
type:"addToFavorites",
title,
imageUrl,
query,
scene: this.globalData.scene,
version: config.version,
time: db.serverDate()
}
db.collection("shareInfo").add({
data
});
return {
title, query, imageUrl
}
},
shareTimeline({title, query, imageUrl}={}){
let db = wx.cloud.database();
let data = {
type:"shareTimeline",
title,
imageUrl,
query,
scene: this.globalData.scene,
version: config.version,
time: db.serverDate()
}
db.collection("shareInfo").add({
data
});
return {
title, query, imageUrl
}
},
shareApp({ imageUrl, path, title="EduCoder教学"}={}) {
let db = wx.cloud.database();
let shareInfo = {
title,
imageUrl,
path
}
if (config.envVersion == 'release'&&!this.globalData.isCrawl) {
if (!path) {
let pages = getCurrentPages();
let page = pages[pages.length - 1];
let options = page.options;
let route = page.route;
let query = Object.keys(options).map(i => `${i}=${options[i]}`).join("&");
path = "/" + route + (query ? ('?' + query) : '');
}
let data = {
type:"shareAppMessage",
title,
imageUrl,
path,
scene: this.globalData.scene,
version: config.version,
time: db.serverDate()
}
console.log("upload", data);
db.collection("shareInfo").add({
data
});
}
return shareInfo;
}
});
// 兼容Promise.finally
if(!Promise.prototype.finally){
Promise.prototype.finally = function (callback) {
let P =this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => {throw reason })
);
};
}
/*
function getCurrentPath() {
let pages = getCurrentPages();
return pages[pages.length - 1].route;
}
function toAbsPath(current, path) {
if (path.startsWith("/"))
current = ".";
else
current = current + "/../";
path = current + path;
while (path.indexOf("..") != -1) {
path = path.replace(/[^\/]*\/\.\.\//g, "")
}
while (path.indexOf(".") != -1) {
path = path.replace(/\.\//, "");
}
return path;
}
*/

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save