smart_class
wu_shao_wei 5 years ago
parent 4d03f53a91
commit 0ae21cc29d

3
.gitignore vendored

@ -10,5 +10,8 @@ $RECYCLE.BIN/
.TemporaryItems
.Trashes
lib_refer
project.config.json
# Node.js
node_modules/

@ -1,12 +1,15 @@
//app.js
//导入leancloud库与后台交互
const AV = require('./utils/av-live-query-weapp-min');
const AV = require('./lib/av-live-query-weapp-min');
//初始化
AV.init({
appId: 'fQCxN98zS5thYY3AceKdI8Pj-MdYXbMMI',
appKey: 'Tdi1DcLlVYrTabFiBaA00pjj',
appId: 'eVHYNBdT5D2lDxNj5jtJXsWT-MdYXbMMI',
appKey: 'waM3bwf1ftpsMLjRBqqVyJIQ',
});
// test Appid
// appId: 'fQCxN98zS5thYY3AceKdI8Pj-MdYXbMMI',
// appKey: 'Tdi1DcLlVYrTabFiBaA00pjj',
App({
config: {

@ -7,7 +7,9 @@
"pages/websocket/websocket",
"pages/game/game",
"pages/shouquan/shouquan",
"pages/outside/outside"
"pages/outside/outside",
"pages/classes/classes",
"pages/classroom/classroom"
],
"window": {
"backgroundTextStyle": "light",

@ -1,6 +1,11 @@
page, .lab {
height: 100%;
}
.flex-wrap{
display: flex;
flex-direction: row;
}
.lab {
display: flex;
flex-direction: column;

@ -0,0 +1,25 @@
module.exports = (subscription, initialStats, onChange) => {
let stats = [...initialStats]
const remove = value => {
stats = stats.filter(target => target.id !== value.id)
return onChange(stats)
}
const upsert = value => {
let existed = false;
stats = stats.map(target => (target.id === value.id ? ((existed = true), value) : target))
if (!existed) stats = [value, ...stats]
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,48 @@
const AV = require('../lib/av-live-query-weapp-min');
const Presence = require("./presence")
class Class extends AV.Object{
get name(){
return this.get("name");
}
set name(value){
this.set("name", value);
}
get students(){
}
add_student(user){
}
del_student(user){
let students = this.get("students");
if(user.id in students){
this.remove(user.id);
}
}
enter() {
this.leave();
console.log("class.enter");
console.log(AV.User.current());
this._presence = new Presence({
user: AV.User.current(),
class: this
});
this._presence.save();
}
leave() {
if (this._presence != null) {
this._presence.destroy();
}
}
present_students() {
var query = new AV.Query("Presence");
//query.equalTo("class", this._class.id);
let students = query.find();
console.log(students);
return students;
}
}
AV.Object.register(Class, "Class_");
module.exports = Class;

@ -0,0 +1,43 @@
const AV = require('../lib/av-live-query-weapp-min');
//数据储存模板--分数
class Grade extends AV.Object {
get mark() {
return this.get('done');
}
set mark(value) {
this.set('mark', value);
}
get student(){
return this.get("student");
}
set student(value){
this.set("student". value);
}
get class() {
return this.get('class');
}
set class(value){
this.set("class", value);
}
get content(){
return this.get("content");
}
set content(value) {
this.set('content', value);
}
get date(){
return this.get("date");
}
set date(value){
this.set("date", value)
}
add_mark(value){
this.increment(value);
}
}
AV.Object.register(Grade, 'Grade');
module.exports = Grade;

@ -0,0 +1,25 @@
const AV = require('../lib/av-live-query-weapp-min');
class Presence extends AV.Object{
set user(value){
this.set("user", value);
}
get user(){
return this.get("user");
}
set class(value){
this.set("class", value);
}
get class(){
return this.get("class");
}
set isasking(value){
this.set("isasking", value);
}
get isasking(){
return this.get("isasking");
}
}
AV.Object.register(Presence, "Presence");
module.exports = Presence;

@ -0,0 +1,17 @@
const AV = require("../..//lib/av-live-query-weapp-min")
class StudentClassMap extends AV.Object{
get class(){
return this.get("class");
}
set class(value){
this.set("class", value);
}
get user(){
return this.get("user");
}
set user(value){
this.set("user", value);
}
}

@ -0,0 +1,11 @@
const AV = require("../lib/av-live-query-weapp-min")
function login() {
return AV.Promise.resolve(AV.User.current()).then(user =>
user ? (user.isAuthenticated().then(authed => authed ? user : null)) : null
).then(user => user ? user : AV.User.loginWithWeapp({
preferUnionId: true,
})).catch(error => console.error(error.message));
}
module.exports = login;

@ -0,0 +1,102 @@
const { Class } = require("../../model/class");
const login = require("../../model/user");
const AV = require("../../lib/av-live-query-weapp-min.js");
const { jsonify } = require('../../utils/leancloudutils');
// pages/class/class.js
const getDataForRender = class_ => ({
name: class_.get('name'),
objectId: class_.get('objectId')
});
Page({
/**
页面的初始数据data用于与.wxlm文件绑定
classes: 课程列表 type: Array
元素class: 课程信息 class.objectId 唯一
class.objectId 课程唯一主键
class.name 课程名称
*/
data: {
classes: [
]
},
classes: [],
enter_class: function(event){
console.log(event);
var class_id = event.currentTarget.dataset.class_id;
var class_name = event.currentTarget.dataset.class_name;
wx.navigateTo({
url: "../classroom/classroom?class_id="+class_id+"&"+"class_name="+class_name,
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
login().then((user)=>{
console.log(user);
console.log(AV.User.current());
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
var query = new AV.Query("Class_");
query.find().then((classes) => {
this.classes = classes
this.setData(jsonify({ classes }));
})
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
var query = new AV.Query("Class_");
query.find().then((classes) => {
this.classes = classes
this.setData(jsonify({classes}));
})
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})

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

@ -0,0 +1,14 @@
<!--
所有的页面数据见同目录classes.js中Page.data定义部分
classes: 课程列表 type: Array
元素class: 课程信息 class.objectId 唯一
class.objectId 唯一键
class.name 课程名称
-->
<view class="class-list">
<block wx:for="{{classes}}" wx:for-item="class" wx:key="objectId">
<view class="flex-wrap classroom" data-class_id="{{class.objectId}}" data-class_name="{{class.name}}" bindtap="enter_class">
<text>{{class.name}}</text>
</view>
</block>
</view>

@ -0,0 +1,28 @@
.header {
height: 54px;
align-items: center;
background-color: #FFF;
}
.class-list {
padding: 4px 6px 48px;
}
.classroom {
font-size: 18px;
height: 30px;
padding: 10px 12px;
overflow: hidden;
align-items: center;
background-color: #fff;
margin: 1px 0;
border-radius: 3px;
}
.classroom text {
white-space: nowrap;
display: block;
width: 100%;
height: 50px;
line-height: 17px;
}

@ -0,0 +1,180 @@
// pages/classroom/classroom.js
const AV = require("../../lib/av-live-query-weapp-min")
const { jsonify } = require('../../utils/leancloudutils');
const bind = require("../../lib/live-query-binding")
Page({
/**
页面的初始数据
students: 该课程所有的学生
元素student
student.objectId 该学生的主键唯一
student.username 学生的用户名是唯一就像微信号一样
student.name 学生的姓名
student.present 是否出席
student.isasking :0:没有提问,1:提问,2:请回答
*/
presence: null,
students: [],
class: null,
class_id: null,
data: {
//以下数据的同步由该代码文件实现数据的呈现由classroom.wxml实现
students: [], // 该课程所有的学生, 元素类型见上文中的student
current_user: null, //目前登陆的用户,类型为对象属性有objectIdusername, name
teacher: null,
//该课程教员,类型为对象
//请在进入界面时判断用户是否为教员以给与权限或更改界面
//属性如下objectId, username, name
class_name: "", //课程名称
class_id: "", //该课程的主键(编号)
},
sco1: function () {
//打1分的函数
},
sco2: function () {
//打2分的函数
},
sco3: function () {
//打3分的函数
},
ask: function () {
//学生提问的函数
},
rep: function () {
//点这个学生要回答他问题的函数
},
pull_present: function(){
var query = new AV.Query("Presence");
//console.log(this.class);
query.equalTo("class", this.class);
query.include("user");
set_present = this.set_present.bind(this);
return AV.Promise.all([query.find().then(set_present), query.subscribe()]).then(([presents, subscription])=>{
this.PresentSubscription = subscription;
if(this.presentUnbind) this.presentUnbind();
this.presentUnbind = bind(subscription, presents, set_present);
}).catch(error=> console.error(error.message));
},
set_present: function(presents){
//console.log("set_present");
//console.log(presents);
var is_asking={};
var present_stu_ids = presents.map((present)=>{
user = present.get("user");
is_asking[user.id]=present.get("isasking");
return user.id;
});
students = this.students.map((student)=>{
if(present_stu_ids.indexOf(student.id)>-1){
student.set("ispresent",true);
student.set("isasking", is_asking[student.id]);
}else{
student.set("ispresent",false);
student.set("isasking", 0);
}
return student;
})
this.setData(jsonify({students}));
return presents;
},
fetch_class: function(class_id){
this.class = AV.Object.createWithoutData("Class_", class_id);
return this.class.fetch().then((class_) => {
//console.log(this.class);
this.setData({
class_name: this.class.get("name"),
class_id: this.class.get("objectId")
});
}).catch(error=>console.error(error.message))
},
fetch_students: function(){
//console.log("fetch_students");
var query = new AV.Query("StudentClassMap");
query.equalTo("class", this.class);
query.include("user");
//console.log("fetch_students2")
return query.find();
},
set_students: function(studentClassMaps){
//console.log("set_students");
var students = []
studentClassMaps.forEach((scm, idx, a)=>{
console.log(a);
students.push(scm.get("user"));
});
this.students = students;
console.log("this.students");
console.log(students);
this.setData(jsonify((students)));
return students;
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.class_id = options.class_id;
//this.class = AV.Object.createWithoutData("Class_", this.class_id);
this.fetch_class.bind(this)(this.class_id).then(this.fetch_students).then(this.set_students.bind(this)).then(this.pull_present.bind(this));
console.log("onLoad");
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
console.log("onReady");
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
console.log("onShow");
console.log("enter class when onShow");
console.log(AV.User.current());
this.class.enter();
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
this.class.leave();
console.log("onHide");
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
this.class.leave();
console.log("onUnload");
this.PresentSubscription.unsubscribe();
this.presentUnbind();
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
this.pull_present.bind(this)();
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
console.log("onShareAppMessage");
}
})

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

@ -0,0 +1,29 @@
<!--pages/classroom/classroom.wxml-->
<view class="index">
<view class="grid-celll">
<view class="grid-view">
<view class="grid-cell" wx:for="{{students}}" wx:for-item="student" wx:key="objectId" bindtap="rep">
<view wx:if="{{student.isasking==0}}">
</view>
<view wx:elif="{{student.isasking==1}}">
<text class="reed">{{student.username}}</text>
</view>
<view wx:elif="{{student.isasking==2}}">
<text class="gre">{{student.username}}</text>
</view>
<text>{{student.username}}</text>
</view>
</view>
</view>
<!-- -->
<view wx:if="{{current_user.objectId==teacher.objectId}}">
<view class="sco">
<button bindtap="ask">提问</button>
</view>
</view>
<view wx:else>
<button data-id='{{students.username}}' bindtap="sco1">打1分</button>
<button data-id='{{students.username}}' bindtap="sco2">打2分</button>
<button data-id='{{students.username}}' bindtap="sco3">打3分</button>
</view>
</view>

@ -0,0 +1,62 @@
/* pages/classroom/classroom.wxss */
.grid-view {
display: -webkit-flex;
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
width: 100%;
}
/* 下面这个实现表格式的布局有多行多列width=25% 表示将会显示4列 */
.grid-celll
{
margin-left:auto;
margin-right:auto;
text-align: center;
width:230rpx;
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
.grid-cell {
padding: 30rpx 0;
border-bottom: 1rpx solid #eee;
position: relative;
}
.bla {
color:black;
}
.reed {
color: red;
position: absolute;
right: 3rpx;
top: 50%;
height: 40rpx;
line-height: 40rpx;
margin-top: -20rpx;
}
.gre {
color: greenyellow;
position: absolute;
right: 3rpx;
top: 50%;
height: 40rpx;
line-height: 40rpx;
margin-top: -20rpx;
}
.index {
font-family: 'PingFang SC';
}
.sco {
margin: 20rpx 50rpx;
}

@ -7,12 +7,14 @@ Page({
{ id: 'https', title: '实验一HTTPS' },
{ id: 'session', title: '实验二:会话' },
{ id: 'websocket', title: '实验三WebSocket' },
{ id: 'game', title: '实验四:剪刀石头布小游戏' }
{ id: 'game', title: '实验四:剪刀石头布小游戏' },
{ id: "classes", title: "进入教室"}
],
done: lab.getFinishLabs()
},
onShow() {
console.log({ done: lab.getFinishLabs() });
this.setData({ done: lab.getFinishLabs() });
},

@ -1,3 +1,3 @@
{
"navigationBarTitleText": "腾讯云实验室"
"navigationBarTitleText": "积分教室"
}

@ -4,8 +4,8 @@
"ignore": []
},
"setting": {
"urlCheck": false,
"es6": true,
"urlCheck": true,
"es6": false,
"postcss": true,
"minified": true,
"newFeature": true,
@ -23,7 +23,7 @@
"compileType": "miniprogram",
"libVersion": "2.8.3",
"appid": "wx2a901996834ef5fb",
"projectname": "miniprogram-3",
"projectname": "smart-class",
"debugOptions": {
"hidedInDevtools": []
},

@ -0,0 +1,20 @@
const isPlainObject = target =>
target &&
target.toString() == '[object Object]' &&
Object.getPrototypeOf(target) == Object.prototype;
const _jsonify = target => {
if (target && typeof target.toJSON === 'function') return target.toJSON();
if (Array.isArray(target)) return target.map(_jsonify);
return target;
};
exports.jsonify = target =>
isPlainObject(target)
? Object.keys(target).reduce(
(result, key) => ({
...result,
[key]: _jsonify(target[key])
}),
{}
)
: _jsonify(target);

@ -1,11 +1,15 @@
登录微信公众平台,前往 设置 > 开发设置 > 服务器配置 > 「修改」 链接,增加下述域名为白名单中的域名
# 后台数据交互部分日志
## 已完成-登录微信公众平台,前往 设置 > 开发设置 > 服务器配置 > 「修改」 链接,增加下述域名为白名单中的域名
request合法域名
fqcxn98z.api.lncldglobal.com
fqcxn98z.engine.lncldglobal.com
fqcxn98z.rtm.lncldglobal.com
fqcxn98z.push.lncldglobal.com
app-router.leancloud.cn
socket合法域名
us-w1-backend2.leancloud.cn
us-w1-backend3.leancloud.cn
us-w1-backend4.leancloud.cn
us-w1-backend5.leancloud.cn
us-w1-backend6.leancloud.cn
us-w1-backend6.leancloud.cn

Loading…
Cancel
Save