add cloudfunctions

master
educoder_weapp 5 years ago
parent fc5d403db0
commit 906812f02c

11
.gitignore vendored

@ -10,14 +10,7 @@ $RECYCLE.BIN/
.TemporaryItems .TemporaryItems
.Trashes .Trashes
project.config.json project.config.json
app.js
towxml api_docs/
we-cropper
components/weui
.idea .idea/
api_docs
# Node.js
node_modules/

@ -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: 'fQCxN98zS5thYY3AceKdI8Pj-MdYXbMMI',
appKey: 'Tdi1DcLlVYrTabFiBaA00pjj',
});
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();
});
});
}
});

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

@ -0,0 +1,36 @@
// 云函数模板
// 部署:在 cloud-functions/login 文件夹右击选择 “上传并部署”
const cloud = require('wx-server-sdk')
// 初始化 cloud
cloud.init({
// API 调用都保持和云函数当前所在环境一致
env: cloud.DYNAMIC_CURRENT_ENV
})
/**
* 这个示例将经自动鉴权过的小程序用户 openid 返回给小程序端
*
* event 参数包含小程序端调用传入的 data
*
*/
exports.main = (event, context) => {
console.log(event)
console.log(context)
// 可执行其他自定义逻辑
// console.log 的内容可以在云开发云函数调用日志查看
// 获取 WX Context (微信调用上下文),包括 OPENID、APPID、及 UNIONID需满足 UNIONID 获取条件)等信息
const wxContext = cloud.getWXContext()
return {
event,
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"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 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,23 @@
# Windows
[Dd]esktop.ini
Thumbs.db
$RECYCLE.BIN/
# macOS
.DS_Store
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
project.config.json
app.js
towxml
we-cropper
components/weui
.idea
api_docs
# Node.js
node_modules/

@ -9,12 +9,10 @@
"pages/courses/courses", "pages/courses/courses",
"pages/exercise/exercise", "pages/exercise/exercise",
"pages/setting/setting", "pages/setting/setting",
"pages/classroom/classroom",
"pages/about/about", "pages/about/about",
"pages/test/test", "pages/test/test",
"pages/course/course", "pages/course/course",
"pages/files/files", "pages/files/files",
"pages/mark_detail/mark_detail",
"pages/reset_password/reset_password", "pages/reset_password/reset_password",
"pages/image_crop/image_crop", "pages/image_crop/image_crop",
"pages/question_setting/question_setting", "pages/question_setting/question_setting",
@ -23,6 +21,7 @@
"pages/exercise_result/exercise_result", "pages/exercise_result/exercise_result",
"pages/feedback/feedback", "pages/feedback/feedback",
"pages/agreement/agreement", "pages/agreement/agreement",
"pages/code_edit/code_edit",
"pages/contact/contact" "pages/contact/contact"
], ],
"window": { "window": {
@ -61,5 +60,8 @@
} }
] ]
}, },
"sitemapLocation": "sitemap.json" "sitemapLocation": "sitemap.json",
"navigateToMiniProgramAppIdList": [
"wx8c7028f9dcd29c99"
]
} }

@ -13,7 +13,7 @@
.arrow { .arrow {
width: 0; width: 0;
height: 0; height: 0;
margin-right: 120rpx; margin-right: 140rpx;
border-width: 20rpx; border-width: 20rpx;
border-style: solid; border-style: solid;
border-color: transparent transparent #fbbd08 transparent; border-color: transparent transparent #fbbd08 transparent;

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

@ -0,0 +1,38 @@
// components/course/course.js
Component({
/**
* 组件的属性列表
*/
properties: {
name:{
type: String
},
teacher:{
type:Object
},
visits:{
type: Number
},
members_count:{
type: Number
},
homework_commons_count:{
type:Number
},
can_visited:{
type: Boolean
},
is_public:{
type: Boolean
}
},
data: {
},
methods: {
}
})

@ -0,0 +1,5 @@
<view class="square">
<view>
</view>
</view>

@ -0,0 +1 @@
/* components/course/course.wxss */

@ -27,8 +27,5 @@ Component({
* 组件的方法列表 * 组件的方法列表
*/ */
methods: { methods: {
onClick() {
this.triggerEvent('click');
}
} }
}) })

@ -0,0 +1,5 @@
<view
class="custom-class iconfont icon-{{ type }}"
style="{{ color ? 'color: ' + color + ';' : '' }}{{ size ? 'font-size: ' + size + 'px;' : '' }}">
<view wx:if="{{ info !== null }}" class="icon__info">{{ info }}</view>
</view>

File diff suppressed because one or more lines are too long

@ -0,0 +1,31 @@
Component({
/**
* 组件的属性列表
*/
properties: {
addGlobalClass: true
},
externalClasses: ['custom-class'],
properties: {
info: null,
type: String,
size: String,
color: String
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
}
})

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

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

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

@ -65,6 +65,7 @@ export class Client{
console.log(cookie); console.log(cookie);
cookie.save(); cookie.save();
} }
} }
onRequireLogin(cd){ onRequireLogin(cd){
this.callback.require_login = cd; this.callback.require_login = cd;
@ -166,6 +167,25 @@ export class Client{
}) })
}) })
} }
attendance({success, fail, complete}={}){
return new Promise((resolve, reject)=>{
Account.attendance({
session: this.session,
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}={}){ get_user_info({success, fail, complete}={}){
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
Account.get_user_info({session: this.session, complete: complete, Account.get_user_info({session: this.session, complete: complete,
@ -178,6 +198,7 @@ export class Client{
} }
resolve(res); resolve(res);
this.save_current_user(); this.save_current_user();
this.save_user_info()
}, },
fail: error=>{ fail: error=>{
this.callback.require_login(); this.callback.require_login();
@ -189,6 +210,24 @@ export class Client{
}) })
}); });
} }
save_user_info(){
const app = getApp();
const db = wx.cloud.database();
if(!app.globalData.openid || !this.current_user.login){
console.warn("save user info", "undifined data", app.globalData.openid, this.current_user.login)
return;
}
console.log("saving user info")
return db.collection("user").doc(this.current_user.login + "_" + app.globalData.openid).set({
data: {
user_info: this.current_user,
updatedAt: new Date(),
cookies: this.session.cookies
}
}).then(res => {
console.log("save user success", res);
}).catch(console.error);
}
get_verification_code_for_register({login, success, fail, complete}){ get_verification_code_for_register({login, success, fail, complete}){
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
Account.get_verification_code({ Account.get_verification_code({

@ -72,9 +72,10 @@ export class Account{
static attendance({session, success, fail, complete}){ static attendance({session, success, fail, complete}){
session.request({ session.request({
url: api_base_url + "/users/attendance.json", url: api_base_url + "/users/attendance.json",
method:"POST",
success(res) { success(res) {
if("status" in res.data && res.data.status<0){ if("status" in res.data && res.data.status<0){
fail(new Error(res.data.status)); fail(new Error(res.data.message));
return; return;
} }
if(typeof success == "function"){ if(typeof success == "function"){

@ -0,0 +1,4 @@
export const courses = [
{ "id": 3604, "name": "试用课程测试", "members_count": 1, "homework_commons_count": 0, "attachments_count": 0, "visits": 74, "first_category_url": "/courses/3604/shixun_homeworks/43557", "is_public": 0, "can_visited": true, "teacher": { "id": 116553, "real_name": "学生", "avatar_url": "avatars/User/116553?t=1573710191", "school_name": "国防科技大学" } },
{ "id": 3518, "name": "试用课程演示课堂", "members_count": 6, "homework_commons_count": 5, "attachments_count": 7, "visits": 628, "first_category_url": "/courses/3518/informs", "is_public": 0, "can_visited": true, "teacher": { "id": 42749, "real_name": "金柯", "avatar_url": "avatars/User/42749?t=1573398243", "school_name": "国防科技大学" } }]

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before

Width:  |  Height:  |  Size: 947 B

After

Width:  |  Height:  |  Size: 947 B

Before

Width:  |  Height:  |  Size: 968 B

After

Width:  |  Height:  |  Size: 968 B

Before

Width:  |  Height:  |  Size: 997 B

After

Width:  |  Height:  |  Size: 997 B

Before

Width:  |  Height:  |  Size: 719 B

After

Width:  |  Height:  |  Size: 719 B

Before

Width:  |  Height:  |  Size: 604 B

After

Width:  |  Height:  |  Size: 604 B

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

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

@ -0,0 +1,66 @@
// pages/code_edit/code_edit.js
Page({
/**
* 页面的初始数据
*/
data: {
code: '\n/**\n * @todo: Error类, to be finished\n */\n\nexport default class EduError extends Error{\n constructor({message=null, code=-1}){\n super(message);\n this.code = code;\n }\n}'
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})

@ -0,0 +1 @@
<textarea class="code-editor" value="{{code}}" ></textarea>

@ -0,0 +1,6 @@
.code-editor{
width: 100%;
height: 100%;
background: grey;
color: whitesmoke;
}

@ -51,7 +51,7 @@
<image class="rightimg" src="/images/right.png" /> <image class="rightimg" src="/images/right.png" />
</view> </view>
</view> </view>
<view class="card" bindtap="copy" data-copy="{{email}}" data-name="email"> <view class="card" bindtap="copy" data-copy="{{email}}" data-name="邮箱">
<view class="card1"> <view class="card1">
<image class="wechatimg" src="/images/email.png" /> <image class="wechatimg" src="/images/email.png" />
</view> </view>

@ -7,38 +7,8 @@ Page({
*/ */
data: { data: {
course: {}, course: {},
show_classroom: new Date().getTime() < 1580820786606
}, },
enter_page({currentTarget:{dataset}}){
let time = new Date().getTime();
let url = dataset.url;
if ( time> 1577820786606){
wx.showModal({
title: '提示',
content: '该功能稳定性不佳已于2020年1月1日停用',
})
} else if (time > 1574320786656){
wx.showModal({
title: '提示',
content: '该功能稳定性不佳将于2020年1月1日停用按确认进入页面',
success: res=>{
if(res.confirm){
wx.navigateTo({
url: url,
})
}
}
})
}else{
wx.navigateTo({
url: url,
})
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) { onLoad: function (options) {
this.course_id = options.course_id; this.course_id = options.course_id;
this.setData({course_id: options.course_id}) this.setData({course_id: options.course_id})

@ -8,7 +8,6 @@
<block wx:for="{{course_modules}}" wx:for-item="course_module"> <block wx:for="{{course_modules}}" wx:for-item="course_module">
<navigator></navigator> <navigator></navigator>
</block> </block>
<view wxLif="{{show_classroom}}" data-url="../classroom/classroom?id={{course_id}}" bindtap="enter_page" class="nav course_module">进入教室</view>
<navigator url="../exercises/exercises?id={{course_id}}" class="nav course_module">试卷</navigator> <navigator url="../exercises/exercises?id={{course_id}}" class="nav course_module">试卷</navigator>
<navigator url="../files/files?id={{course_id}}" class="nav course_module">资源</navigator> <navigator url="../files/files?id={{course_id}}" class="nav course_module">资源</navigator>
</view> </view>

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

Loading…
Cancel
Save