增加试卷的分数查询及答题卡

master
educoder_weapp 5 years ago
parent 37b6d4edc3
commit 78eb1192ff

3
.gitignore vendored

@ -1,6 +1,9 @@
api_docs/ api_docs/
.idea/ .idea/
res/ res/
docs/
pandocs
# Windows # Windows
[Dd]esktop.ini [Dd]esktop.ini

@ -1,39 +1,6 @@
# 简介 # 简介
educoder微信小程序帮助使用[educoder平台](https://www.educoder.net)的应用,方便在手机上使用。 educoder微信小程序帮助使用[educoder平台](https://www.educoder.net)的应用,方便在手机上使用。
## 源码
## 小程序码 ## 小程序码
![小程序码](/images/weacode.jpg) ![小程序码](/images/weacode.jpg)
# 功能介绍
## 教室
- 学员可以输入邀请码进入课堂
- 进入教室界面会显示在位,头像为彩色,若退出课堂界面则会显示灰色头像
- 教员在教室界面中可以直观地看到学员在位情况,可以选择学员让其起立回答问题,并且对学员可以进行加分、减分操作
- 在分数列表中可以看到加减分记录(数据在后台可以导出)
- 学员可以收到教员让其起立提问、回答的提示,还可以点击“我要提问、回答”
- 教室内有讨论区,可以交流
## 课程资源
- 在课程界面进入“资源”可以查看本课堂的课程文件资源
- 支持打开ppt doc xls pdf文件
## 试卷作答
- 学员在课程内可以看到老师发布的试卷,并且回答
- 试卷截止后并且老师选择了公开答案,学生可以看到公布的答案
- 老师可以创建试卷,发布试卷,查看学员作答分数
## 其他
账号的注册、登陆、找回密码、头像更改等

@ -1,3 +1,21 @@
##v0.14.2
* 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 ## v0.13.3
* A 增加实名认证界面 * A 增加实名认证界面
* A 增加数据异常监测上报 * A 增加数据异常监测上报

@ -15,6 +15,7 @@ exports.main = (event, context) => {
return { return {
event, event,
context,
openid: wxContext.OPENID, openid: wxContext.OPENID,
appid: wxContext.APPID, appid: wxContext.APPID,
unionid: wxContext.UNIONID, unionid: wxContext.UNIONID,

@ -110,6 +110,9 @@ switch.no-login{
transform: translateX(240rpx); transform: translateX(240rpx);
padding: 0px; padding: 0px;
} }
.pos3>button::after{
border: none!important;
}
.foot{ .foot{
position: fixed; position: fixed;
bottom:0px; bottom:0px;

@ -21,11 +21,19 @@ Page({
app.syncUser() app.syncUser()
.then(res=>{ .then(res=>{
if (this.oldNum!=null && accountManager.getAccounts().length>this.oldNum && !this.data.currentAccountSaved && this.data.currentAccount) { if (this.oldNum!=null && accountManager.getAccounts().length>this.oldNum && !this.data.currentAccountSaved && this.data.currentAccount) {
// 增加了新账号,将未保存的老账号信息保存
console.error("add current") console.error("add current")
console.log(this.data.currentAccount); console.log(this.data.currentAccount);
accountManager.addAccount(this.data.currentAccount); accountManager.addAccount(this.data.currentAccount);
this.setData({ currentAccountSaved: 1 }); 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(); let addedAccounts = accountManager.getAccounts();
var currentAccount = accountManager.getCurrentAccount(); var currentAccount = accountManager.getCurrentAccount();
var currentAccountSaved = 0; var currentAccountSaved = 0;
@ -84,9 +92,9 @@ Page({
}else{ }else{
app.api("accounts.login")(account) app.api("accounts.login")(account)
.then(res=>{ .then(res=>{
wx.showToast({ res.login = account.login; // 使用登录的手机号或邮箱而不是login字符
title: '切换成功' accountManager.updateAccount(res);
}); this.showMsg({message:"切换成功"});
accountManager.setCurrentAccount(account); accountManager.setCurrentAccount(account);
this.refresh(); this.refresh();
}).catch(e=>{ }).catch(e=>{
@ -102,5 +110,18 @@ Page({
title: '已达数量上限,无法添加',icon:"none" title: '已达数量上限,无法添加',icon:"none"
}) })
wx.navigateTo({ url:"../account/account?nostorage=1&error=密码将加密保存至本地&save_password=1&addaccount=1"}); wx.navigateTo({ url:"../account/account?nostorage=1&error=密码将加密保存至本地&save_password=1&addaccount=1"});
},
showMsg({message,duration=800}){
console.log(message);
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;
} }
}) })

@ -3,6 +3,9 @@
</page-meta> </page-meta>
<view> <view>
<view class="message single-line {{showMessage&&message?'':'hidden'}}">
{{message}}
</view>
<view wx:for="{{accounts}}" class="account" data-id="{{item.user_id}}" bindtap="switchAccount" bindlongpress="removeAccount" wx:key="user_id"> <view wx:for="{{accounts}}" class="account" data-id="{{item.user_id}}" bindtap="switchAccount" bindlongpress="removeAccount" wx:key="user_id">
<image src="{{eduImgDir}}{{item.image_url}}" class="avatar"></image> <image src="{{eduImgDir}}{{item.image_url}}" class="avatar"></image>
<view class="info"> <view class="info">

@ -1,3 +1,22 @@
.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{ .account{
display: flex; display: flex;
padding: 12px; padding: 12px;

@ -3,7 +3,7 @@ Page({
data: { data: {
information : "## **认证须知**\n1. 你需要准备有效的身份证正面(人像面)的证件照照片,请确保证件照片清晰可见,**严禁PS**\n\n2. 我们将在你提交认证信息后的24小时不包含节假日内完成审核审核结果将会以系统消息的形式发送给你\n\n3. 实名认证审核完成后,**无法删除**,请谨慎填写;\n\n4. 实名认证审核完成后系统将自动发放500个金币作为奖励\n\n5. 我们会确保你所提供的信息均处于严格的保密状态,不会泄露;\n\n6. 如存在恶意乱填写姓名,证件号,及上传与实名认证证件无关图片者,一经发现将**冻结EduCoder账号**。\n\n7. 提交实名认证后系统会自动将状态改为已认证,你将可以体验平台需要实名认证的功能;如果在认证后的使用过程中未通过审核,你将不能继续体验需要认证的功能。\n\n", information : "## **认证须知**\n1. 你需要准备有效的身份证正面(人像面)的证件照照片,请确保证件照片清晰可见,**严禁PS**\n\n2. 我们将在你提交认证信息后的24小时不包含节假日内完成审核审核结果将会以系统消息的形式发送给你\n\n3. 实名认证审核完成后,**无法删除**,请谨慎填写;\n\n4. 实名认证审核完成后系统将自动发放500个金币作为奖励\n\n5. 我们会确保你所提供的信息均处于严格的保密状态,不会泄露;\n\n6. 如存在恶意乱填写姓名,证件号,及上传与实名认证证件无关图片者,一经发现将**冻结EduCoder账号**。\n\n7. 提交实名认证后系统会自动将状态改为已认证,你将可以体验平台需要实名认证的功能;如果在认证后的使用过程中未通过审核,你将不能继续体验需要认证的功能。\n\n",
imgDir:global.config.imgDir attachDir:global.config.attachDir
}, },
onLoad(){ onLoad(){
this.refresh(); this.refresh();
@ -25,8 +25,6 @@ Page({
value.attachment_ids[0] = parseInt(value.attachment_ids[0]); value.attachment_ids[0] = parseInt(value.attachment_ids[0]);
app.api("users.accounts.authentication_apply")(value) app.api("users.accounts.authentication_apply")(value)
.then(res=>{ .then(res=>{
res.message = "实名认证提交成功";
app.showMsg(res);
let db = wx.cloud.database(); let db = wx.cloud.database();
db.collection("data").add({ db.collection("data").add({
data:{ data:{
@ -35,6 +33,13 @@ Page({
createdAt: db.serverDate() createdAt: db.serverDate()
} }
}); });
res.message = "提交成功";
app.showMsg(res);
setTimeout(()=>{
wx.navigateBack({
delta:1
})
},800);
}).catch(e=>{ }).catch(e=>{
app.showError(e); app.showError(e);
}) })

@ -18,12 +18,12 @@
</view> </view>
<view class="form-item"> <view class="form-item">
<text class="key">身份证号</text> <text class="key">身份证号</text>
<input class="value" type="idcard" cursor-spacing="26" name="id_number" placeholder="请输入身份证号"></input> <input class="value" type="idcard" cursor-spacing="36" name="id_number" placeholder="请输入身份证号"></input>
</view> </view>
<view class="idcard-wrap"> <view class="idcard-wrap">
<view class="key">身份证上传</view> <view class="key">身份证上传</view>
<view class="idcards"> <view class="idcards">
<image class="idcard" mode="aspectFit" src="{{imgDir}}svg/idcard.svg"></image> <image class="idcard" mode="aspectFit" src="{{attachDir}}762168"></image>
<image wx:if="{{image_path}}" bindtap="upload" class="idcard" mode="aspectFit" src="{{image_path}}"></image> <image wx:if="{{image_path}}" bindtap="upload" class="idcard" mode="aspectFit" src="{{image_path}}"></image>
<view wx:else bindtap="upload" class="idcard upload"> <view wx:else bindtap="upload" class="idcard upload">
<view class="photo"> <view class="photo">
@ -40,7 +40,7 @@
<checkbox value="{{attachment_id}}" checked="{{attachment_id}}"></checkbox> <checkbox value="{{attachment_id}}" checked="{{attachment_id}}"></checkbox>
</checkbox-group> </checkbox-group>
<input name="user_id" hidden="1" disabled="1" value="{{user_id}}"></input> <input name="user_id" hidden="1" disabled="1" value="{{user_id}}"></input>
<button class="submit" type="main" form-type="submit">提交</button> <button class="submit" type="main" form-type="submit">提交信息</button>
<view class="footer-tip"> <view class="footer-tip">
*提交前请检查信息并阅读认证须知 *提交前请检查信息并阅读认证须知
</view> </view>

@ -3,7 +3,7 @@ Page({
data: { data: {
information: "## **认证须知**\n1. 根据职业上传相应的证件照:教师(教师证),专业人士(员工证)、学生(学生证),请确保证件照内容完整并且清晰可见,**严禁PS**\n\n2. 我们将在你提交职业证信息后的24小时不包含节假日内完成审核审核结果将会以系统消息的形式发送给你\n\n3. 职业认证审核完成后,**无法删除**,请谨慎填写;职业变更请选择重新认证;\n\n4. 职业认证审核完成后系统将自动发放500个金币作为奖励\n\n5. 我们会确保你所提供的信息均处于严格的保密状态,不会泄露;\n\n6. 如存在恶意乱填写姓名,学号,及上传与职业证件无关图片者,一经发现将**冻结EduCoder账号**。\n\n7. 非老师身份提交职业认证后系统会自动将状态改为已认证,你将可以体验平台需要职业认证的功能;如果在认证后的使用过程中未通过审核,你将不能继续体验需要认证的功能。\n\n", information: "## **认证须知**\n1. 根据职业上传相应的证件照:教师(教师证),专业人士(员工证)、学生(学生证),请确保证件照内容完整并且清晰可见,**严禁PS**\n\n2. 我们将在你提交职业证信息后的24小时不包含节假日内完成审核审核结果将会以系统消息的形式发送给你\n\n3. 职业认证审核完成后,**无法删除**,请谨慎填写;职业变更请选择重新认证;\n\n4. 职业认证审核完成后系统将自动发放500个金币作为奖励\n\n5. 我们会确保你所提供的信息均处于严格的保密状态,不会泄露;\n\n6. 如存在恶意乱填写姓名,学号,及上传与职业证件无关图片者,一经发现将**冻结EduCoder账号**。\n\n7. 非老师身份提交职业认证后系统会自动将状态改为已认证,你将可以体验平台需要职业认证的功能;如果在认证后的使用过程中未通过审核,你将不能继续体验需要认证的功能。\n\n",
imgDir: global.config.imgDir, attachDir: global.config.attachDir,
identities: [{ value: "teacher", text: "教师" }, { value: "student", text: "学生" }, { value: "professional", text: "专业人士" }], identities: [{ value: "teacher", text: "教师" }, { value: "student", text: "学生" }, { value: "professional", text: "专业人士" }],
technicalTitles: { technicalTitles: {
0: ["教授", "副教授", "讲师", "助教"], 0: ["教授", "副教授", "讲师", "助教"],
@ -85,8 +85,6 @@ Page({
value.attachment_ids[0] = parseInt(value.attachment_ids[0]); value.attachment_ids[0] = parseInt(value.attachment_ids[0]);
app.api("users.accounts.professional_auth_apply")(value) app.api("users.accounts.professional_auth_apply")(value)
.then(res => { .then(res => {
res.message = "职业认证提交成功";
app.showMsg(res);
let db = wx.cloud.database(); let db = wx.cloud.database();
db.collection("data").add({ db.collection("data").add({
data: { data: {
@ -95,6 +93,13 @@ Page({
createdAt: db.serverDate() createdAt: db.serverDate()
} }
}); });
res.message = "提交成功";
app.showMsg(res);
setTimeout(() => {
wx.navigateBack({
delta: 1
})
}, 800);
}).catch(e => { }).catch(e => {
app.showError(e); app.showError(e);
}) })

@ -27,7 +27,7 @@
<view class="idcard-wrap"> <view class="idcard-wrap">
<view class="key">职业证上传</view> <view class="key">职业证上传</view>
<view class="idcards"> <view class="idcards">
<image class="idcard" mode="aspectFit" src="{{imgDir}}svg/procard.svg"></image> <image class="idcard" mode="aspectFit" src="{{attachDir}}762172"></image>
<image wx:if="{{image_path}}" bindtap="upload" class="idcard" mode="aspectFit" src="{{image_path}}"></image> <image wx:if="{{image_path}}" bindtap="upload" class="idcard" mode="aspectFit" src="{{image_path}}"></image>
<view wx:else bindtap="upload" class="idcard upload"> <view wx:else bindtap="upload" class="idcard upload">
<view class="photo"> <view class="photo">
@ -44,7 +44,7 @@
<checkbox value="{{attachment_id}}" checked="{{attachment_id}}"></checkbox> <checkbox value="{{attachment_id}}" checked="{{attachment_id}}"></checkbox>
</checkbox-group> </checkbox-group>
<input name="user_id" hidden="1" disabled="1" value="{{user_id}}"></input> <input name="user_id" hidden="1" disabled="1" value="{{user_id}}"></input>
<button class="submit" type="main" form-type="submit">提交</button> <button class="submit" type="main" form-type="submit">提交信息</button>
<view class="footer-tip"> <view class="footer-tip">
*提交前请检查信息并阅读认证须知 *提交前请检查信息并阅读认证须知
</view> </view>

@ -199,6 +199,7 @@ Page({
saveInfo(value){ saveInfo(value){
app.api("users.accounts", { method: "PUT" })(value) app.api("users.accounts", { method: "PUT" })(value)
.then(res => { .then(res => {
app.syncUser({refresh:1});
res.message = "更新成功"; res.message = "更新成功";
app.showMsg(res); app.showMsg(res);
console.log("success", res); console.log("success", res);

@ -1,186 +0,0 @@
const app = getApp();
const key = {
save_password: "we,nd;ke;hcy",
login: "login",
password: "Fie[Rxu[Eu?ua;c"
}
Page({
data: {
imgDir: global.config.imgDir,
action: "login",
code_button_text: "获取",
action_text: { login: "登录", register: "注册", reset: "找回密码" },
pos: { login: 1, register: 2, reset: 3 },
signUp: 1,
logIn:0
},
log_In: function (event) {
this.setData({
signUp: 0,
logIn: 1
})
},
sign_Up: function (event) {
this.setData({
signUp: 1,
logIn: 0
})
},
login_test() {
var data = { login: "educoder_weapp@126.com", password: "abcdefgh" };
this.setData(data);
this.login(data);
},
login({ login, password, showToast = 1 }) {
app.api("accounts.login")({ login, password })
.then(res => {
res.message = "登录成功";
if (showToast)
app.showMsg(res);
this.navBack();
})
.catch(e => {
if (showToast)
app.showError(e);
})
},
register({ login, password, code }) {
app.api("accounts.register")({ login, password, code })
.then(res => {
app.showMsg(res);
this.navBack();
}).catch(e => {
app.showError(e);
})
},
reset({ login, password: new_password, password_confirmation: new_password_confirmation, code, no_login }) {
app.api("accounts.reset_password")({ login, new_password, new_password_confirmation, code })
.then(res => {
res.message = "重置成功";
app.showMsg(res);
if (!no_login)
this.login({ login, password: new_password, showToast: 0 });
})
.catch(e => {
app.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();
app.showMsg(res);
}).catch(e => {
app.showError(e);
this.setData({ code_status: 0, code_button_text: "获取验证码" })
})
},
clearCount(id) {
console.log("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);
console.log("id", id);
return id;
},
onSubmit({ detail: { value, target } }) {
console.log("onSubmit", value, target, this.action);
console.log(this.checkInput({ value, action: target.id }));
if (!this.checkInput({ value, action: target.id }))
return;
console.log("target.id")
if (target.id == "code")
return this.getCode(value);
console.log("setAction", this.data);
if (target.id != this.data.action)
return this.setAction(target.id);
console.log("setStorage")
this.setStorage(value);
console.log("callapi", value);
this[target.id](value);
},
checkInput({ value, action }) {
if (action != this.data.action && action != "code")
return true;
if (!value.login)
return wx.showToast({
title: '请输入邮箱或手机号', icon: "none"
});
if (action == "code")
return true;
if (!value.password)
return wx.showToast({
title: '请输入密码', icon: "none"
});
if (action == "login")
return true;
if (!value.code)
return wx.showToast({
title: '请输入验证码', icon: "none"
});
if (action == "register")
return true;
if (!value.password_confirmation)
return wx.showToast({
title: '请再次输入密码', icon: "none"
});
return true;
},
setAction(action) {
let { pos } = this.data;
if (!(action in pos))
return;
let tmp = pos[action];
pos[action] = pos[this.data.action];
pos[this.data.action] = tmp;
this.setData({ pos, action });
},
onLoad: function (options) {
let { action = "login" } = options;
this.setAction({ action });
this.getStorage();
},
setStorage({ login, password, save_password }) {
wx.setStorageSync(key.login, login);
wx.setStorageSync("_password", password);
wx.setStorageSync(key.save_password, save_password);
if (save_password)
wx.setStorageSync(key.password, password);
else
wx.clearStorageSync(key.password);
},
getStorage() {
let save_password = wx.getStorageSync(key.save_password);
if (save_password !== 0)
save_password = 1;
let login = wx.getStorageSync(key.login);
if (save_password)
var password = wx.getStorageSync(key.password);
else
var password = "";
this.setData({ save_password, login, password });
},
navBack() {
setTimeout(() => {
wx.navigateBack({
delta: 1
});
}, 500);
}
})

@ -1,70 +0,0 @@
<page-meta>
<navigation-bar title="{{action_text[action]}}" />
</page-meta>
<view class="container" wx:if="{{!signUp && logIn}}">
<view class="block_blueBig">
<view class="white_big" bindtap="log_In">注册</view>
<form class="account-form" bindsubmit="onSubmit">
<view class="inputs">
<view class="input-wrap">
<image src="../../../images/denglu.png" class="mini_button"></image>
<input class="input" name="login" value="{{login}}" placeholder="邮箱或手机号"></input>
</view>
<view class="input-wrap">
<image src="../../../images/yanzhengma.png" class="mini_button"></image>
<input class="input" name="code" placeholder="验证码"></input>
<button plain id="code" form-type="submit" loading="{{code_status==2}}" disabled="{{code_status}}" style="width: 100rpx;">
{{code_button_text}}
</button>
</view>
<view class="input-wrap">
<image src="../../../images/suo.png" class="mini_button"></image>
<input password name="password" value="{{password}}" placeholder="请输入密码"></input>
</view>
<view class="input-wrap {{action=='reset'?'':'hidden'}}">
<image src="../../../images/suo.png" class="mini_button"></image>
<input password name="password_confirmation" placeholder="请再次输入密码"></input>
</view>
<view class="checkbox-wrap">
<switch type="checkbox" name="save_password" color="#00b0f0" checked="{{save_password}}">保存密码</switch>
</view>
</view>
<button class="register" id="register" form-type="submit" style="width:100%">注 册</button>
</form>
</view>
<view class="white_little block_red" bindtap="change" bindtap="sign_Up">登录</view>
</view>
<view class="container" wx:else>
<view class="block_blue">
<view class="white_little" bindtap="log_In">注册</view>
</view>
<view class="white_big">登录</view>
<form class="account-form" bindsubmit="onSubmit">
<view class="inputs">
<view class="input-wrap">
<image src="../../../images/denglu.png" class="mini_button"></image>
<input class="input" name="login" value="{{login}}" placeholder="邮箱或手机号"></input>
</view>
<view class="input-wrap">
<image src="../../../images/suo.png" class="mini_button"></image>
<input class="input" password name="password" value="{{password}}" placeholder="请输入密码"></input>
</view>
</view>
<view class="check_row">
<view class="save_password">
<switch type="checkbox" name="save_password" color="#00b0f0" checked="{{save_password}}">保存密码</switch>
</view>
<navigator class="reset" >忘记密码</navigator>
</view>
<button id="login" form-type="submit" class="login" style="width:100%">登 录</button>
</form>
</view>
<view class="foot">
<navigator class="agreement" hover-class="none" url="/account/pages/agreement/agreement">
登录即代表您同意 <text class="color-main agreement">用户协议</text>
</navigator>
</view>

@ -1,141 +0,0 @@
page{
height: 100%;
background-image: linear-gradient(to bottom left,#7BCBFA, #EF7FAE);
color: white;
}
.container{
transition: 1s all ease;
width: 100%;
height: 100%;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
}
.block_blueBig{
transition: 1s all ease;
width: 100%;
background: #67AFF3;
border-radius: 0px 0px 70rpx 70rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.white_big{
transition: 1s all ease;
font-size: 80rpx;
font-weight: bold;
padding: 40rpx 0rpx;
}
.block_blue{
transition: 1s all ease;
width: 100%;
height: 250rpx;
background: #67AFF3;
border-radius: 0px 0px 100% 100%;
font-size: 50rpx;
font-weight: bold;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.account-form{
transition: 1s all ease;
width: 85%;
}
.inputs{
transition: 1s all ease;
margin-bottom: 50rpx;
}
.input-wrap{
transition: 1s all ease;
padding: 10px;
margin-bottom: 30rpx;
display: flex;
align-items: center;
border: 1px white solid;
border-radius: 20px;
}
.input{
transition: 1s all ease;
white-space: nowrap;
width: 100%;
}
.mini_button{
transition: 1s all ease;
width: 48rpx;
height: 45rpx;
padding-right: 20rpx;
padding-left: 10rpx;
}
button[plain] {
transition: 1s all ease;
border: 0;
padding: 0;
margin: 0;
color: white;
}
.white_little{
transition: 1s all ease;
font-size: 50rpx;
font-weight: bold;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.check_row{
transition: 1s all ease;
width: 100%;
display: flex;
justify-content: space-between;
margin-bottom: 60rpx;
}
.login{
transition: 1s all ease;
width: 100%;
padding: 8px;
color: #F54171;
font-weight: 400;
border-radius: 20px;
box-shadow: #F54171 -1px 1px 1rpx;
}
.register{
transition: 1s all ease;
width: 100%;
padding: 8px;
font-weight: 400;
border-radius: 20px;
box-shadow: #2190F3 -1px 1px 1rpx;
color: #2190F3;
margin-bottom: 40rpx;
}
.foot{
transition: 1s all ease;
position: fixed;
bottom:0px;
width: 100%;
display: flex;
}
navigator.agreement{
transition: 1s all ease;
margin: 10px auto;
font-size: 14px;
text-align: center;
}
text.agreement{
transition: 1s all ease;
text-decoration: underline;
}
.block_red{
transition: 1s all ease;
width: 100%;
height: 220rpx;
}

@ -0,0 +1,24 @@
const app = getApp();
Page({
data: {
info:'获取中...'
},
onLoad: function (options) {
this.setData({info:"获取中"})
wx.cloud.callFunction({name:"login"})
.then(res=>{
console.log(res);
this.setData(res.result);
}).catch(e=>{
console.error(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;
}

@ -16,7 +16,7 @@ App({
callApi(options) { return client.callApi(options) }, callApi(options) { return client.callApi(options) },
user() { return client.user }, user() { return client.user },
syncUser(options) { return client.syncUser(options) }, syncUser(options) { return client.syncUser(options) },
updateUserInfo(info){return client.updateUserInfo(info)},
onLaunch: function (options) { onLaunch: function (options) {
const db = wx.cloud.database(); const db = wx.cloud.database();
if (options.referrerInfo && options.referrerInfo.appId) { if (options.referrerInfo && options.referrerInfo.appId) {
@ -30,6 +30,9 @@ App({
} }
}) })
} }
wx.reportAnalytics('version', {
app_version: global.config.version
});
this.api("users.system_update")().then(res => { this.api("users.system_update")().then(res => {
if (res.system_update) { if (res.system_update) {
let { subject = "升级服务通知", system_score} = res; let { subject = "升级服务通知", system_score} = res;

@ -23,11 +23,11 @@
"pages/change_password/change_password", "pages/change_password/change_password",
"pages/profile/profile", "pages/profile/profile",
"pages/account/account", "pages/account/account",
"pages/signUp/signUp",
"pages/profile/school_select/school_select", "pages/profile/school_select/school_select",
"pages/authentication/authentication", "pages/authentication/authentication",
"pages/pro_authentication/pro_authentication", "pages/pro_authentication/pro_authentication",
"pages/accounts/accounts" "pages/accounts/accounts",
"pages/user_info/user_info"
] ]
}, },
{ {
@ -118,7 +118,7 @@
} }
}, },
"tabBar": { "tabBar": {
"selectedColor": "#1890ff", "selectedColor": "#00b0f0",
"color": "#8a8a8a", "color": "#8a8a8a",
"list": [ "list": [
{ {
@ -129,7 +129,7 @@
}, },
{ {
"pagePath": "pages/tidings/tidings", "pagePath": "pages/tidings/tidings",
"text": "消息中心", "text": "消息",
"iconPath": "images/tab_tiding_default.png", "iconPath": "images/tab_tiding_default.png",
"selectedIconPath": "images/tab_tiding_pressed.png" "selectedIconPath": "images/tab_tiding_pressed.png"
}, },

@ -30,6 +30,7 @@ page {
.error{color:red;} .error{color:red;}
.warning{color: orange;} .warning{color: orange;}
.color-main{color:#00b0f0} .color-main{color:#00b0f0}
.bg-main{background-color:#00b0f0}
.color-dark-main{color:#0080f0} .color-dark-main{color:#0080f0}
button.button-main{background:#00b0f0} button.button-main{background:#00b0f0}
.border-main{border-color:#00b0f0} .border-main{border-color:#00b0f0}
@ -54,7 +55,7 @@ button.button-main{background:#00b0f0}
overflow: hidden; overflow: hidden;
} }
/*
radio .wx-radio-input.wx-radio-input-checked{ radio .wx-radio-input.wx-radio-input-checked{
background: #00b0f0; background: #00b0f0;
border-color: #00b0f0; border-color: #00b0f0;
@ -65,6 +66,7 @@ checkbox .wx-checkbox-input.wx-checkbox-input-checked {
.checkbox .wx-checkbox-input.wx-checkbox-input-checked { .checkbox .wx-checkbox-input.wx-checkbox-input-checked {
color: #00b0f0; color: #00b0f0;
} }
*/
button[type=main][plain],button[type=cap][plain]{ button[type=main][plain],button[type=cap][plain]{
color: #00b0f0; color: #00b0f0;
border: 1px solid #00b0f0; border: 1px solid #00b0f0;

@ -101,7 +101,7 @@ Page({
let height = device.windowHeight - 42; let height = device.windowHeight - 42;
let cropperOpt = { let cropperOpt = {
id: 'cropper', id: 'cropper',
scale: 2.6, scale: 3.2,
zoom: 8, zoom: 8,
width, width,
height, height,

@ -1,11 +1,11 @@
@font-face { @font-face {
font-family: 'iconfont'; /* project id 1656783 */ font-family: 'iconfont'; /* project id 1656783 */
src: url('//at.alicdn.com/t/font_1656783_u55yruw45f.eot'); src: url('//at.alicdn.com/t/font_1656783_bu9nkm5qk8.eot');
src: url('//at.alicdn.com/t/font_1656783_u55yruw45f.eot?#iefix') format('embedded-opentype'), src: url('//at.alicdn.com/t/font_1656783_bu9nkm5qk8.eot?#iefix') format('embedded-opentype'),
url('//at.alicdn.com/t/font_1656783_u55yruw45f.woff2') format('woff2'), url('//at.alicdn.com/t/font_1656783_bu9nkm5qk8.woff2') format('woff2'),
url('//at.alicdn.com/t/font_1656783_u55yruw45f.woff') format('woff'), url('//at.alicdn.com/t/font_1656783_bu9nkm5qk8.woff') format('woff'),
url('//at.alicdn.com/t/font_1656783_u55yruw45f.ttf') format('truetype'), url('//at.alicdn.com/t/font_1656783_bu9nkm5qk8.ttf') format('truetype'),
url('//at.alicdn.com/t/font_1656783_u55yruw45f.svg#iconfont') format('svg'); url('//at.alicdn.com/t/font_1656783_bu9nkm5qk8.svg#iconfont') format('svg');
} }
.iconfont { .iconfont {
display: inline-block; display: inline-block;
@ -44,6 +44,30 @@
} }
.icon-fuzhilianjie:before {
content: "\e61e";
}
.icon-fenxiang:before {
content: "\e603";
}
.icon-yiwen:before {
content: "\e600";
}
.icon-qiehuanzhanghao:before {
content: "\e688";
}
.icon-jinru:before {
content: "\e657";
}
.icon-saoma:before {
content: "\e7f3";
}
.icon-xiugaimima:before { .icon-xiugaimima:before {
content: "\e68b"; content: "\e68b";
} }
@ -95,15 +119,3 @@
.icon-filter:before { .icon-filter:before {
content: "\e6c7"; content: "\e6c7";
} }
.icon-saoma:before{
content: "\e7f3"
}
.icon-jinru:before{
content: "\e657"
}
.icon-qiehuanzhanghao:before{
content: "\e688";
}
.icon-yiwen:before{
content: "\e600";
}

@ -11,7 +11,7 @@ Component({
success:res=>{ success:res=>{
if(!res.data||res.data==this.clipboardData) return; if(!res.data||res.data==this.clipboardData) return;
this.clipboardData = res.data; this.clipboardData = res.data;
var match = res.data.match(/[A-Z0-9]{5,6}/); var match = res.data.match(/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Z]{5,6}$/);
if(match) if(match)
this.setData({invite_code: match[0]}); this.setData({invite_code: match[0]});
} }

@ -1,6 +1,6 @@
<modal class="join_course" wx:if="{{!hidden}}" title="加入课堂" confirm-text="提交" cancel-text="取消" bindcancel="cancel" bindconfirm="join_course"> <modal class="join_course" wx:if="{{!hidden}}" title="加入课堂" confirm-text="提交" cancel-text="取消" bindcancel="cancel" bindconfirm="join_course">
<view class="input-wrap {{show_code?'':'hidden'}}"> <view class="input-wrap {{show_code?'':'hidden'}}">
<input class="code-input" type='text' bindinput="update_invite_code" value="{{invite_code}}" placeholder="邀请码" auto-focus/> <input class="code-input" cursor-spacing="106" type='text' bindinput="update_invite_code" value="{{invite_code}}" placeholder="邀请码" auto-focus/>
<image class="scan" src="./scan.png" mode="aspectFit" catchtap="scan"/> <image class="scan" src="./scan.png" mode="aspectFit" catchtap="scan"/>
</view> </view>
<checkbox-group class="identities" bindchange="update_identities"> <checkbox-group class="identities" bindchange="update_identities">

@ -5,16 +5,9 @@ const developUrl = "https://test-newweb.educoder.net";
const trialUrl = "https://pre-newweb.educoder.net"; const trialUrl = "https://pre-newweb.educoder.net";
const releaseUrl = "https://www.educoder.net"; const releaseUrl = "https://www.educoder.net";
let _version = "0.14.0"; let _version = "0.14.2";
var eduUrl = releaseUrl; var eduUrl = releaseUrl;
/** /**
* A 职业认证页面
* A 学生课堂签到模块
* A 切换账号功能
* U 个人中心增加图标
* U iconfont增加modal与toast提示方式
* U 图片资源转移服务器
* F 实名认证证件上传判断bug
*/ */
export function switchEnv(env) { export function switchEnv(env) {

@ -17,14 +17,14 @@ Component({
if(course_identity==5&&mode=="QUICK"){ if(course_identity==5&&mode=="QUICK"){
this.quickAttendance(); this.quickAttendance();
}else{ }else{
app.navigateTo({url:`{attendance_detail}?course_identity=${course_identity}&course_id=${course_id}&code=${code||''}&mode=${mode}&attendance_id=${attendance_id}`}); app.navigateTo({url:`{attendance_detail}?course_identity=${course_identity}&course_id=${course_id}&code=${code||''}&mode=${mode||''}&attendance_id=${attendance_id}`});
} }
}, },
onTap(e){ onTap(e){
let { course_identity, course_id, data: { mode, code, attendance_status, id: attendance_id} } = this.data; let { course_identity, course_id, data: { mode, code, attendance_status, id: attendance_id} } = this.data;
if(course_identity==5&&(attendance_status=='NORMAL'||mode=="QUICK")) if(course_identity==5&&(attendance_status=='NORMAL'||mode=="QUICK"))
return; return;
app.navigateTo({ url: `{attendance_detail}?course_identity=${course_identity}&course_id=${course_id}&code=${code||''}&mode=${mode}&attendance_id=${attendance_id}` }); app.navigateTo({ url: `{attendance_detail}?course_identity=${course_identity}&course_id=${course_id}&code=${code||''}&mode=${mode||''}&attendance_id=${attendance_id}` });
}, },
quickAttendance(){ quickAttendance(){
let { mode:attendance_mode,id:attendance_id} = this.data.data; let { mode:attendance_mode,id:attendance_id} = this.data.data;

@ -23,11 +23,13 @@ Component({
}, },
pageLifetimes:{ pageLifetimes:{
show(){ show(){
this.refresh(); if(this.secondShow)
this.refresh();
this.secondShow = 1;
} }
}, },
attached(){ attached(){
this.refresh();
//this.test(); //this.test();
}, },
methods: { methods: {

@ -10,8 +10,13 @@ Component({
methods: { methods: {
onTap(){ onTap(){
// 让用户选择是否答题,故添加封面; let { exercise_status, current_status, id} = this.data.data;
app.navigateTo({url:`{exercise_cover}?exercise_id=${this.data.data.id}`}); if (exercise_status == 3 || current_status < 2) {
app.navigateTo({ url: `{exercise}?exercise_id=${id}` });
}else{
// 让用户选择是否答题,故添加封面;
app.navigateTo({url:`{exercise_cover}?exercise_id=${id}&exercise_status=${exercise_status}&current_status=${current_status}`});
}
} }
} }
}) })

@ -1,3 +1,4 @@
import { getWXACodeUrl} from "../../../js/utils";
const app = getApp(); const app = getApp();
Component({ Component({
properties: { properties: {
@ -8,20 +9,51 @@ Component({
code:String code:String
}, },
data:{ data:{
attachDir:global.config.attachDir
}, },
methods:{ methods:{
onLoad(){ onShow(){
if(this.data.status!=200)
this.refresh();
},
onLoad(options){
if(options.scene){
console.log(options);
let scene = decodeURIComponent(options.scene);
let [attendance_id, mode, code] = scene.split("-");
this.setData({attendance_id, mode, code,course_identity:5});
}
this.refresh(); this.refresh();
}, },
attendance(e){ previewQrCode(){
let {code_url} = this.data;
wx.previewImage({
urls: [code_url]
});
},
copyCode(){
let {name, start_time,end_time, code} = this.data;
let data = `签到名称:${name}\n签到时间:${start_time}-${end_time}\n签到码:${code}`;
wx.setClipboardData({data});
},
onCodeLoadError(e) {
console.log("onCodeLoadError", e);
let page = this.route;
let scene = this.getScene();
console.log(page, scene);
wx.cloud.callFunction({ name: "openapi", data: { action: "getWXACodeUnlimited", page, scene } })
.then(res => {
this.setData({ code_url: "" });
this.setData({ code_url: res.result })
});
}, },
navBack(){ navBack(){
if(getCurrentPages().length<=1) if(getCurrentPages().length<=1){
app.reLaunch({url:"{main}"}) let {course_identity, course_id} = this.data;
else app.redirectTo({
url: `{attendance}?course_identity=${course_identity}&course_id=${course_id}`
});
}else
wx.navigateBack({ wx.navigateBack({
delta:1 delta:1
}); });
@ -50,9 +82,9 @@ Component({
.then(res => { .then(res => {
res.message = "签到成功"; res.message = "签到成功";
app.showMsg(res); app.showMsg(res);
setTimeout(()=>{wx.navigateBack({ setTimeout(()=>{
delta:1 this.navBack();
})},800); },800);
}).catch(e => { }).catch(e => {
app.showError(e); app.showError(e);
}); });
@ -60,8 +92,34 @@ Component({
scanCode(){ scanCode(){
wx.scanCode({ wx.scanCode({
success:res=>{ success:res=>{
var fail = false;
if (res.scanType == "QR_CODE") if (res.scanType == "QR_CODE")
this.setData({ scaned_code: res.result}); this.setData({ scaned_code: res.result});
else if (res.scanType == "WX_CODE" && res.path) {
var match = res.path.match(/attendance_detail\?(.*)$/)
console.log("match", match);
var options = {}
match[1].split("&").map(i => {
var index = i.indexOf("=");
var k = i.slice(0, index);
var v = i.slice(index + 1);
options[k] = v
})
console.log("options", options);
if(options.scene){
let [attendance_id, mode, scaned_code] = decodeURIComponent(options.scene).split("-");
if(scaned_code)
this.setData({scaned_code});
else
fail = false;
}else{
fail = true;
}
}else{
fail = true;
}
if (fail)
wx.showToast({title: "识别失败", icon: "none"})
} }
}) })
}, },
@ -71,34 +129,38 @@ Component({
let {attendance_id} = this.data; let {attendance_id} = this.data;
app.api("weapps.attendances")({attendance_id}) app.api("weapps.attendances")({attendance_id})
.then(res=>{ .then(res=>{
this.setData(res); this.setData(res);
}) this.setData({status:200});
if(course_identity<5&&res.mode=="QRCODE"){
let code_url = getWXACodeUrl({url: this.route, scene: this.getScene()});
this.setData({code_url});
}
}).catch(e=>{
console.error(e);
app.showError(e)
this.setData({status: e.code});
})
}else{ }else{
this.refreshIdentity(); this.refreshIdentity()
.then(res=>{
if(this.data.course_identity)
this.refresh();
})
} }
}, },
getScene(){
let {mode, code, attendance_id} = this.data;
return attendance_id+"-"+mode+"-"+code;
},
refreshIdentity(){ refreshIdentity(){
let {course_id} = this.data; let {course_id} = this.data;
app.api("courses.top_banner")({course_id}) return app.api("courses.top_banner")({course_id})
.then(res=>{ .then(res=>{
console.log(res); console.log(res);
this.setData({}); let {course_identity} = res;
this.setData({course_identity});
}) })
}, },
checkInfo(){
let { course_identity,attendance_id,mode,code} = this.data;
if(course_identity<5){
return false;
}else if(course_identity==5){
if(!mode)
return false;
else
return true;
}else{
}
},
analyseWxaCode(){ analyseWxaCode(){
}, },

@ -1,6 +1,7 @@
{ {
"usingComponents": { "usingComponents": {
"iconfont":"/components/iconfont/iconfont" "iconfont":"/components/iconfont/iconfont",
"require-login":"/components/require-login/require-login"
}, },
"navigationBarTitleText": "课堂签到" "navigationBarTitleText": "课堂签到"
} }

@ -1,24 +1,46 @@
<page-meta> <page-meta>
<navigation-bar title="{{name?'签到-'+name:'签到'}}"/> <navigation-bar title="{{name?'签到-'+name:'签到'}}" />
</page-meta> </page-meta>
<require-login message="登陆后才可以查看哦,点击这里登录吧"/>
<view class="info">
<view class="tea-title">{{name}}</view>
<view class="tea-time-wrap" wx:if="{attendance_date{}}">签到时间:
<text class="tea-time">{{attendance_date}} {{start_time}}-{{end_time}}</text>
</view>
</view>
<block wx:if="{{course_identity&&course_identity<5}}"> <block wx:if="{{course_identity&&course_identity<5}}">
<view class="tea-info">
<view class="qrcode-wrap" wx:if="{{mode=='QRCODE'}}">
<image class="qrcode" bindtap="previewQrCode" src="{{code_url}}" mode="aspectFit" binderror="onCodeLoadError"></image>
<view class="qrcode-tip">学生需要扫码签到</view>
</view>
<view class="number-wrap" wx:elif="{{mode=='NUMBER'}}">
<view class="tea-code-wrap" bindtap="copyCode">学生签到码:
<text class="tea-code">{{code}}</text>
<iconfont type="fuzhilianjie" size="18" />
</view>
</view>
<view class="quick-wrap" wx:elif="{{mode}}">
<image class="quick-img" src="{{attachDir}}753481" mode="aspectFit"></image>
<view class="share-tip">分享链接,学生点击按钮快速签到<button size="mini" class="share-button" open-type="share"><text class="button-text">转发</text><iconfont type="fenxiang" size="18"/></button></view>
</view>
</view>
</block> </block>
<block wx:elif="{{course_identity==5}}"> <block wx:elif="{{course_identity==5}}">
<view class="stu-attendance"> <view class="stu-attendance">
<form bindsubmit="onSubmit"> <form bindsubmit="onSubmit">
<view class="stu-title"></view> <view class="input-wrap" hidden="{{mode=='QUICK'}}">
<view class="stu-tip"></view> <input name="code" value="{{scaned_code}}" />
<view class="stu-detail"></view> <iconfont type="saoma" color="dimgrey" bindtap="scanCode" />
<view class="input-wrap"> </view>
<input name="code" value="{{scaned_code}}"/> <view class="quick-img-wrap" wx:if="{{mode=='QUICK'}}">
<iconfont type="saoma" color="dimgrey" bindtap="scanCode"/> <image class="quick-img" src="{{attachDir}}753481" mode="aspectFit"></image>
</view> </view>
<input hidden="1" name="attendance_id" value="{{attendance_id}}"></input> <input hidden="1" name="attendance_id" value="{{attendance_id}}"></input>
<view class="tip">{{mode=='QRCODE'?'请扫二维码签到':mode=='NUMBER'?'请输入签到码签到':mode=='QUICK'?'点击签到按钮,快速签到':''}}</view> <view class="tip">{{mode=='QRCODE'?'请扫二维码签到':mode=='NUMBER'?'请输入签到码签到':mode=='QUICK'?'点击签到按钮,快速签到':''}}</view>
<view> <view>
<button form-type="submit" type="main">签到</button> <button form-type="submit" type="main">{{mode=='QUICK'?'快速签到':'签到'}}</button>
<button class="nav-back" type="main" plain="1" bindtap="navBack">返回</button> <button class="nav-back" type="main" plain="1" bindtap="navBack">返回</button>
</view> </view>
</form> </form>

@ -16,4 +16,65 @@
} }
.nav-back{ .nav-back{
margin-top: 4px; margin-top: 4px;
}
.quick-img-wrap{
background: white;
text-align: center;
}
.info,
.tea-info{
background: white;
padding: 12px;
text-align: center;
}
.tea-title{
font-weight: bold;
font-size: 20px;
}
.tea-time-wrap{
font-size: 14px;
}
.tea-time{
font-weight: bold;
}
.quick-img,
.qrcode{
margin: 8px 0;
}
.share-tip,
.qrcode-tip{
font-size: 13px;
color: dimgray;
}
.tea-code-wrap{
background: #f0f2ff;
color: dimgray;
padding: 12px;
border-radius: 2px;
display: inline-block;
margin: 12px 0;
}
.tea-code{
text-decoration: underline;
color: #00b0f0;
font-weight: bold;
margin-right: 12px;
}
.share-tip{
display: flex;
align-items: center;
justify-content: center;
}
.share-button{
display: flex;
justify-content: center;
align-items: center;
margin: 0;
color: #00b0f0;
background: white;
}
.button-text{
margin-right: 8px;
} }

@ -1,3 +1,4 @@
import { getWXACodeUrl} from '../../../js/utils';
const app = getApp(); const app = getApp();
/** /**
* status:[404,-1] * status:[404,-1]
@ -64,11 +65,6 @@ Page({
let { course_id} = this.data; let { course_id} = this.data;
return `course_id=${course_id}` return `course_id=${course_id}`
}, },
getWxaCodeUrl(){
let page = this.getPageUrl();
let scene = this.getScene();
return global.config.imgDir+"wxacode/"+ (page+"?"+scene).replace(/[\/?&]/g, "_")+".jpeg";
},
pull_course(){ pull_course(){
app.api("weapps.courses.basic_info")({ course_id:this.data.course_id }) app.api("weapps.courses.basic_info")({ course_id:this.data.course_id })
.then(({course}) => { .then(({course}) => {
@ -95,7 +91,7 @@ Page({
return; return;
} }
this.setData({ course_id, status: 1}); this.setData({ course_id, status: 1});
let invite_code_url = this.getWxaCodeUrl(); let invite_code_url = getWXACodeUrl({url: this.getPageUrl(), scene:this.getScene()});
this.pull_course(); this.pull_course();
this.setData({ invite_code_url}); this.setData({ invite_code_url});
}, },

@ -13,12 +13,27 @@ Component({
} }
//console.log(this.user_answers); //console.log(this.user_answers);
} }
} },
exercise_status:Number,
user_exercise_status: Number,
is_md:Boolean
},
attached(){
console.log(this.data);
}, },
data: { data: {
}, },
methods: { methods: {
triggerAnswer({answered=1}={}){
console.info("trigger answer");
if(this.answered!=answered){
let {question_id, q_position} = this.data.data;
this.triggerEvent("answer", { q_position, question_id, answered}, { bubbles: true});
console.log("triggered");
this.answered = answered;
}
},
answer_choice_question: function (e) { answer_choice_question: function (e) {
console.log(e, this.data); console.log(e, this.data);
let { detail: { value }, currentTarget: { dataset } }=e let { detail: { value }, currentTarget: { dataset } }=e
@ -38,6 +53,14 @@ Component({
} }
app.api("exercise_questions.exercise_answers")({ question_id: dataset.question_id, exercise_choice_id }) app.api("exercise_questions.exercise_answers")({ question_id: dataset.question_id, exercise_choice_id })
.then(res => { .then(res => {
var answered = 0;
if (Array.isArray(exercise_choice_id)) {
answered = exercise_choice_id.length>0?1:0;
} else if (exercise_choice_id) {
answered = 1;
}
console.log(answered);
this.triggerAnswer({answered});
console.log("answer_question"); console.log(res); console.log("answer_question"); console.log(res);
if (!Array.isArray(exercise_choice_id)) if (!Array.isArray(exercise_choice_id))
exercise_choice_id = [exercise_choice_id]; exercise_choice_id = [exercise_choice_id];

@ -1,24 +1,28 @@
<view class="question"> <view class="question">
<view class="hint">第{{data.q_position}}题</view> <view class="index hint"><text class="gap">第{{data.q_position}}题</text><text class="ques-score">{{data.question_score}}分</text></view>
<rich-md my-class="title" nodes="{{data.question_title}}"/> <rich-md my-class="title" nodes="{{data.question_title}}"/>
<radio-group class="choices" wx:if="{{data.question_type==0 || data.question_type==2}}" bindchange="answer_choice_question" data-question_id="{{data.question_id}}"> <radio-group class="choices" wx:if="{{data.question_type==0 || data.question_type==2}}" bindchange="answer_choice_question" data-question_id="{{data.question_id}}">
<block wx:for="{{data.question_choices}}" wx:for-item="choice" wx:key="choice_id"> <block wx:for="{{data.question_choices}}" wx:for-item="choice" wx:key="choice_id">
<radio class="choice" color="#00b0f0" disabled="{{exercise.user_exercise_status==1 || exercise.user_exercise_status==4}}" checked="{{choice.user_answer_boolean}}" value="{{choice.choice_id}}"> <radio class="choice radio" color="{{user_exercise_status==1||user_exercise_status==4?'#a1a0a2':'#00b0f0'}}" checked="{{choice.user_answer_boolean}}" value="{{choice.choice_id}}">
<view class="choice-content"> <view class="choice-content">
<rich-md class="choice-text" nodes="{{choice.choice_text}}"/> <rich-md class="choice-text" nodes="{{choice.choice_text}}"/>
<text wx:if="{{choice.standard_boolean}}" class="error standard-choice">正确答案</text>
</view> </view>
</radio> </radio>
</block> </block>
</radio-group> </radio-group>
<checkbox-group class="choices" wx:elif="{{data.question_type==1}}" bindchange="answer_choice_question" data-question_id="{{data.question_id}}"> <checkbox-group class="choices" wx:elif="{{data.question_type==1}}" bindchange="answer_choice_question" data-question_id="{{data.question_id}}">
<block wx:for="{{data.question_choices}}" wx:for-item="choice" wx:key="choice_id"> <block wx:for="{{data.question_choices}}" wx:for-item="choice" wx:key="choice_id">
<checkbox color="#00b0f0" class="choice" disabled="{{exercise.user_exercise_status==1 || exercise.user_exercise_status==4}}" checked="{{choice.user_answer_boolean}}" value="{{choice.choice_id}}"> <checkbox color="#00b0f0" class="choice" disabled="{{user_exercise_status==1||user_exercise_status==4}}" checked="{{choice.user_answer_boolean}}" value="{{choice.choice_id}}">
<view class="choice-content"> <view class="choice-content">
<rich-md class="choice-text" nodes="{{choice.choice_text}}"/> <rich-md class="choice-text" nodes="{{choice.choice_text}}"/>
<text wx:if="{{choice.standard_boolean}}" class="error standard-choice">正确答案</text>
</view> </view>
</checkbox> </checkbox>
</block> </block>
</checkbox-group> </checkbox-group>
<view class="outcome" wx:if="{{data.user_score}}">
得分:<text class="score-num">{{data.user_score}}</text><text class="gap">/{{data.question_score}}</text>
<block wx:if="{{data.standard_answer_show}}">
正确答案:<text class="stand-answer">{{data.standard_answer_show}}</text>
</block>
</view>
</view> </view>

@ -1,18 +1,34 @@
const app = getApp(); const app = getApp();
Component({ Component({
properties: { properties: {
data:Object data:Object,
is_md: Boolean,
exercise_status: Number,
user_exercise_status: Number,
}, },
data: { data: {
}, },
methods: { methods: {
triggerAnswer({answered=1}={}) {
if (this.answered!=answered) {
let { question_id, q_position } = this.data.data;
this.triggerEvent("answer", { q_position, question_id, answered }, { bubbles: true });
this.answered = answered;
}
},
answer_main_question: function ({detail: {value}, currentTarget: { dataset } }) { answer_main_question: function ({detail: {value}, currentTarget: { dataset } }) {
console.log("answer_main_question"); console.log("answer_main_question");
console.log(value); console.log(value);
console.log(dataset); console.log(dataset);
app.api("exercise_questions.exercise_answers")({ question_id: dataset.question_id, answer_text: value }) app.api("exercise_questions.exercise_answers")({ question_id: dataset.question_id, answer_text: value })
.then(res => { console.log("answer_main_question"); console.log(res); }) .then(res => {
if (value)
this.triggerAnswer({answered:1});
else
this.triggerAnswer({answered:0});
console.log("answer_main_question"); console.log(res);
})
.catch(e => { .catch(e => {
console.error(e); console.error(e);
app.showError(e); app.showError(e);

@ -1,14 +1,17 @@
<view class="question"> <view class="question">
<view class="hint">第{{data.q_position}}题</view> <view class="index hint"><text class="gap">第{{data.q_position}}题</text><text class="ques-score">{{data.question_score}}分</text></view>
<rich-md my-class="title" nodes="{{data.question_title}}"/> <rich-md my-class="title" nodes="{{data.question_title}}"/>
<textarea disabled="{{exercise.user_exercise_status==1 || exercise.user_exercise_status==4}}" class="main-input" <textarea disabled="{{user_exercise_status==1 || user_exercise_status==4}}" class="main-input"
placeholder="输入答案" placeholder="输入答案"
bindblur="answer_main_question" bindblur="answer_main_question"
value="{{data.user_answer[0]||''}}" value="{{data.user_answer[0]||''}}"
data-question_id="{{data.question_id}}"> data-question_id="{{data.question_id}}">
</textarea> </textarea>
<view wx:if="{{data.standard_answer}}" class="standard-main-input"> <view class="outcome" wx:if="{{data.user_score}}">
<text class="hint">参考答案:</text> 得分:<text class="score-num">{{data.user_score}}</text>/{{data.question_score}}
<text class="error">{{question.standard_answer[0]||'暂无'}}</text> </view>
<view wx:if="{{data.standard_answer}}" class="outcome">
<view>正确答案:</view>
<text class="stand-answer">{{question.standard_answer[0]||'暂无'}}</text>
</view> </view>
</view> </view>

@ -1,18 +1,45 @@
const app = getApp(); const app = getApp();
Component({ Component({
properties: { properties: {
data:Object data:{
type:Object,
observer:function(data){
this.answers = data.user_answer.map(i=>i.answer_text);
console.log("answers",this.answers);
}
},
is_md: Boolean,
exercise_status: Number,
user_exercise_status: Number,
}, },
data: { data: {
}, },
methods: { methods: {
triggerAnswer() {
let answered = this.checkAnswered();
if (this.answered != answered) {
let { question_id, q_position } = this.data.data;
this.triggerEvent("answer", { q_position, question_id, answered }, { bubbles: true });
this.answered = answered;
}
},
checkAnswered(){
for(var answer of this.answers){
if(answer)
return 1;
}
return 0;
},
answer_null_question: function ({ detail: { value }, currentTarget: { dataset:{question_id, exercise_choice_id}} }) { answer_null_question: function ({ detail: { value }, currentTarget: { dataset:{question_id, exercise_choice_id}} }) {
console.log("answer_main_question"); console.log("answer_main_question");
console.log(value); console.log(value);
console.log(question_id, exercise_choice_id); console.log(question_id, exercise_choice_id);
app.api("exercise_questions.exercise_answers")({ question_id, exercise_choice_id, answer_text: value }) app.api("exercise_questions.exercise_answers")({ question_id, exercise_choice_id, answer_text: value })
.then(res => { .then(res => {
this.answers[exercise_choice_id-1] = value;
console.log("answers",this.answers);
this.triggerAnswer();
console.log("answer_main_question"); console.log(res); console.log("answer_main_question"); console.log(res);
}) })
.catch(e => { .catch(e => {

@ -1,9 +1,9 @@
<view class="question"> <view class="question">
<view class="hint">第{{data.q_position}}题</view> <view class="index hint"><text class="gap">第{{data.q_position}}题</text><text class="ques-score">{{data.question_score}}分</text></view>
<rich-md my-class="title" nodes="{{data.question_title}}"/> <rich-md my-class="title" nodes="{{data.question_title}}"/>
<view wx:for="{{data.multi_count}}" class="input-wrap"> <view wx:for="{{data.multi_count}}" class="input-wrap">
<text class="hint">第{{index+1}}空</text> <text class="hint">第{{index+1}}空</text>
<input class="null-input" disabled="{{exercise.user_exercise_status==1 || exercise.user_exercise_status==4}}" <input class="null-input" disabled="{{user_exercise_status==1 || user_exercise_status==4}}"
placeholder="输入第{{index+1}}空的答案" placeholder="输入第{{index+1}}空的答案"
data-question_id="{{data.question_id}}" data-question_id="{{data.question_id}}"
data-exercise_choice_id="{{index+1}}" data-exercise_choice_id="{{index+1}}"
@ -11,10 +11,13 @@
bindblur="answer_null_question"> bindblur="answer_null_question">
</input> </input>
</view> </view>
<view wx:if="{{data.standard_answer}}" class="standard-null-inputs"> <view class="outcome" wx:if="{{data.user_score}}">
<view class="standard-null-input" wx:for="{{question.standard_answer}}" wx:for-item="answer"> 得分:<text class="score-num">{{data.user_score}}</text><text class="gap">/{{data.question_score}}</text>
<text class="hint">第{{answer.choice_id}}空答案:</text>
<text class="error" style="padding-top: 12rpx;">{{answer.answer_text}}</text>
</view> </view>
<view wx:if="{{data.standard_answer}}" class="outcome">
<view wx:for="{{data.standard_answer}}" wx:for-item="answer" class="mg">
<text>第{{answer.choice_id}}空答案:</text>
<text class="stand-answer">{{answer.answer_text}}</text>
</view>
</view> </view>
</view> </view>

@ -13,7 +13,7 @@
border-radius: 10rpx; border-radius: 10rpx;
} }
standard-null-input{ .mg{
align-items: center; margin-top: 4px;
display: flex; margin-bottom: 2px;
} }

@ -11,4 +11,25 @@
.title>text,.title>rich-text{ .title>text,.title>rich-text{
margin: 5rpx 6rpx 12rpx 6rpx; margin: 5rpx 6rpx 12rpx 6rpx;
display: block; display: block;
}
.gap{
margin-right: 8px;
}
.ques-score{
font-size: 12px;
color: dimgray;
}
.outcome{
padding: 4px 4px 1px 6px;
font-size: 14px;
}
.score-num{
color: #00b0f0;
font-size: 16px;
font-weight: bold;
}
.stand-answer{
font-weight: bold;
} }

@ -2,7 +2,10 @@ const app = getApp();
Component({ Component({
properties: { properties: {
data:Object data:Object,
is_md: Boolean,
exercise_status: Number,
user_exercise_status: Number,
}, },

@ -1,8 +1,11 @@
<view class="question"> <view class="question">
<view class="hint">第{{data.q_position}}题</view> <view class="index hint"><text class="gap">第{{data.q_position}}题</text><text class="ques-score">{{data.question_score}}分</text></view>
<rich-md c-class="title" nodes="{{data.question_title}}"/> <rich-md c-class="title" nodes="{{data.question_title}}"/>
<view class="shixun-detail"> <view class="shixun-detail">
<button class="button-shixun" size="mini" type="main" plain bindtap="enterShixun">进入该实训</button> <button class="button-shixun" size="mini" type="main" plain bindtap="enterShixun">进入该实训</button>
<view>{{data.shixun_name}}</view> <view>{{data.shixun_name}}</view>
</view> </view>
<view class="outcome" wx:if="{{data.user_score}}">
得分:<text class="score-num">{{data.user_score}}</text><text class="gap">/{{data.question_score}}</text>
</view>
</view> </view>

@ -6,11 +6,26 @@ Page({
loading: true, loading: true,
exercise: {} exercise: {}
}, },
scrollToQues(e){
let {target: {dataset:{ques_id}}} = e;
console.log(ques_id);
if(!ques_id) return;
wx.pageScrollTo({
selector:"#q-"+ques_id
});
},
onAnswer(e){
console.log(e);
let {detail:{q_position,answered}} = e;
let key = "question_status[" + (q_position - 1) +"].ques_status"
this.setData({[key]: answered});
},
pull_questions: function(){ pull_questions: function(){
app.api("exercises.start_answer")({exercise_id: this.exercise_id}) app.api("exercises.start_answer")({exercise_id: this.exercise_id})
.then(res=>{ .then(res=>{
console.log("pull questions"); console.log("pull questions");
this.setData({ exercise: res.exercise, exercise_questions: res.exercise_questions, loading: false}); this.setData(res);
this.setData({loading: false});
}).catch(e => { }).catch(e => {
this.setData({status:e.code}); this.setData({status:e.code});
app.showError(e); app.showError(e);
@ -36,9 +51,18 @@ Page({
}, },
commit_exercise: function(){ commit_exercise: function(){
let { question_status} = this.data;
var answered = 1;
for(var ques of question_status){
if (!ques.ques_status){
answered = 0;
break;
}
}
var content = answered?'交卷后不可更改,确定交卷吗':'您还有题目未作答,交卷后无法更改,确认吗';
wx.showModal({ wx.showModal({
title: '确认', title: '确认',
content: '交卷后不可更改,确定交卷吗?', content,
success: res=>{ success: res=>{
if(res.confirm){ if(res.confirm){
app.api("exercises.begin_commit")({ exercise_id: this.exercise_id}) app.api("exercises.begin_commit")({ exercise_id: this.exercise_id})
@ -48,14 +72,20 @@ Page({
.then(res=>{ .then(res=>{
console.log("交卷"); console.log("交卷");
console.log(res); console.log(res);
wx.navigateBack({ app.showMsg(res);
setTimeout(()=>{
wx.navigateBack({
delta:1 delta:1
}) })
wx.showToast({ }, 800);
title: res.message }).catch(e=>{
}) app.showError(e);
}).catch(console.error); console.error(e);
}).catch(console.error); });
}).catch(e=>{
app.showError(e);
console.error(e);
});
} }
} }
}) })

@ -1,15 +1,47 @@
<page-meta> <page-meta>
<navigation-bar title="{{exercise.exercise_name}}"/> <navigation-bar title="{{exercise.exercise_name}}" />
</page-meta> </page-meta>
<scroll-view scroll-y="1" class="questions">
<!--exercise-score wx:if="{{exercise.exercise_scores}}" data="{{exercise.exercise_scores}}"/-->
<view class="exercise-score" wx:if="{{exercise_scores}}">
<view class="user-score">总分
<text class="color-main total-score">{{user_score}}</text>/{{exercise_types.q_scores}}</view>
<view>客观题</view>
<view class="scores" bindtap="scrollToQues">
<view class="score-item" data-ques_id="{{item.ques_id}}" wx:for="{{exercise_scores.objective_scores}}">
{{item.ques_position}}
<view class="score {{item.answer_status==1?'right':'wrong'}}">{{item.user_score||'未评分'}}</view>
</view>
</view>
<view>主观题</view>
<view class="scores" bindtap="scrollToQues">
<view class="score-item" data-ques_id="{{item.ques_id}}" wx:for="{{exercise_scores.subjective_scores}}">
{{item.ques_position}}
<view class="score {{item.answer_status==1?'right':'wrong'}}">{{item.user_score||'未评分'}}</view>
</view>
</view>
</view>
<view bindanswer="onAnswer">
<view class="question-wrap" wx:for="{{exercise_questions}}" wx:key="question_id"> <view class="question-wrap" wx:for="{{exercise_questions}}" wx:key="question_id">
<choice-question id="q-{{item.question_id}}" wx:if="{{item.question_type<3}}" data="{{item}}"/> <choice-question id="q-{{item.question_id}}" is_md="{{exercise.is_md}}" wx:if="{{item.question_type<3}}" data="{{item}}" exercise_status="{{exercise.exercise_status}}" user_exercise_status="{{exercise.user_exercise_status}}"/>
<null-question id="q-{{item.question_id}}" wx:elif="{{item.question_type==3}}" data="{{item}}"/> <null-question id="q-{{item.question_id}}" is_md="{{exercise.is_md}}" wx:elif="{{item.question_type==3}}" data="{{item}}" exercise_status="{{exercise.exercise_status}}" user_exercise_status="{{exercise.user_exercise_status}}"/>
<main-question id="q-{{item.question_id}}" wx:elif="{{item.question_type==4}}" data="{{item}}"/> <main-question id="q-{{item.question_id}}" is_md="{{exercise.is_md}}" wx:elif="{{item.question_type==4}}" data="{{item}}" exercise_status="{{exercise.exercise_status}}" user_exercise_status="{{exercise.user_exercise_status}}"/>
<shixun-question id="q-{{item.question_id}}" wx:elif="{{item.question_type==5}}" data="{{item}}"/> <shixun-question id="q-{{item.question_id}}" is_md="{{exercise.is_md}}" wx:elif="{{item.question_type==5}}" data="{{item}}" exercise_status="{{exercise.exercise_status}}" user_exercise_status="{{exercise.user_exercise_status}}"/>
</view> </view>
</scroll-view> </view>
<view wx:if="{{exercise.user_exercise_status!=1 && exercise.user_exercise_status!=4}}" hidden="{{loading}}" class="foot flex-wrap" > <view class="end" wx:if="{{!loading}}">---- END ----</view>
<button class="save operation" catchtap="save_exercise">保存</button> <!--exercise-status wx:if="{{exercise.exercise_stauts}}" data="{{exercise.exercise_status}}"/-->
<button class="commit operation" catchtap="commit_exercise">交卷</button> <block wx:if="{{question_status}}">
</view> <view class="exercise-status">
<view>作答情况</view>
<view class="statuses" bindtap="scrollToQues">
<view data-ques_id="{{item.ques_id}}" class="status-item {{item.ques_status==1?'answered':''}}" wx:for="{{question_status}}">
{{item.ques_number}}
</view>
</view>
</view>
<view class="operations" wx:if="{{exercise.user_exercise_status!=1&&exercise.user_exercise_status!=4}}">
<button type="main" plain="1" catchtap="save_exercise">保存</button>
<button type="main" catchtap="commit_exercise">交卷</button>
</view>
</block>

@ -1,30 +1,80 @@
page{ page{
display: flex; overflow-x: hidden;
flex-direction: column; }
height:100%; .user-score{
text-align: center;
font-size: 18px;
padding-bottom: 12px;
} }
.foot{
flex:none; .total-score{
width: 100%; font-weight: bolder;
}
.exercise-status,
.exercise-score{
background: white; background: white;
padding: 12px;
}
.statuses,
.scores{
display: flex;
flex-wrap: wrap;
}
.status-item,
.score-item{
border-radius: 4px;
margin: 8px 0 8px 10px;
width: 44px;
height: 44px;
display: flex;
justify-content: center;
align-items: center;
flex: none;
} }
.operation{ .status-item{
background: #cbcbcb;
color: white; color: white;
width: 50%;
} }
.save{ .status-item.answered{
background-color: #00b0f0; background: #00b0f0;
} }
.commit{ .score-item{
background-color: orangered; color: #00b0f0;
border: 1px #00b0f0 solid;
position: relative;
} }
.container{
padding-bottom: 40px; .score{
position: absolute;
bottom: -8px;
font-size: 10px;
color: white;
border-radius: 32px;
padding: 1.8px 5px;
white-space: nowrap;
}
.score.right{
background: #00b0f0;
} }
.score.wrong{
background: #fa5151;
}
.question-wrap{ .question-wrap{
margin: 8px 10px; margin: 8px 10px;
} }
.questions{ .end{
flex: 1 1 1px; text-align: center;
height: 1px; margin: 10px;
} color: dimgray;
}
.operations{
display: flex;
}
.operations>button{
flex: 1;
}

@ -3,7 +3,13 @@ import {getFormatDatetime} from "../../../js/utils";
const app = getApp(); const app = getApp();
Component({ Component({
properties: { properties: {
exercise_id:Number exercise_id:Number,
exercise_status: Number,
current_status:{
type:Number,
value: 100 // @todo
},
user_exercise_status:Number
}, },
data: { data: {
@ -12,7 +18,15 @@ Component({
methods: { methods: {
onLoad:function(){ onLoad:function(){
this.refresh(); if(this.data.exercise_status==3||this.data.current_status<2){
app.redirectTo({ url: `{exercise}?exercise_id=${this.data.exercise_id}` });
}
this.refresh()
.then(res=>{
if(res.exercise.exercise_status==3)
// for 通知进入的情况,跳转
app.redirectTo({ url: `{exercise}?exercise_id=${this.data.exercise_id}` });
})
}, },
async refresh(){ async refresh(){
let {exercise_id} = this.data; let {exercise_id} = this.data;
@ -20,6 +34,7 @@ Component({
let date = new Date(data.exercise.end_time); let date = new Date(data.exercise.end_time);
data.exercise.end_time_str = getFormatDatetime(date); data.exercise.end_time_str = getFormatDatetime(date);
this.setData(data); this.setData(data);
return data;
}, },
onTap(){ onTap(){
let {time} = this.data.exercise; let {time} = this.data.exercise;

@ -9,6 +9,6 @@
答题时间:{{exercise.time}} 分钟 答题时间:{{exercise.time}} 分钟
</view> </view>
<button class="enter" type="main" bindtap="onTap"> <button class="enter" type="main" bindtap="onTap">
开始答题 {{exercise.exercise_status==2?'开始答题':'查看答题'}}
</button> </button>
</view> </view>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -132,7 +132,7 @@ users:{
email: { url: "{login}/*", query, config, form: { email: null, code: null } }, email: { url: "{login}/*", query, config, form: { email: null, code: null } },
password: { url: "{login}/*", query, form:{old_password:null, password:null} ,config:{method:"PUT"}}, password: { url: "{login}/*", query, form:{old_password:null, password:null} ,config:{method:"PUT"}},
phone_bind:{url:"{login}/*", query,config, form:{phone:null, code:null}}, phone_bind:{url:"{login}/*", query,config, form:{phone:null, code:null}},
professional_auth_apply:{url:"{user_id}/*",query, form:{shool_id:null, department_id:null,identity:null, extra:null, attachment_ids: null},config} professional_auth_apply:{url:"{user_id}/*",query, form:{school_id:null, department_id:null,identity:null, extra:null, attachment_ids: null},config}
}, },
attendance: {query, config}, attendance: {query, config},
courses:{url:"{login}/*", query, form:{page:1, sort_by:"updated_at",sort_direction:"desc", per_page:10, category:void 0, status:void 0},category:["study","manage"],status:["processing","end"]}, courses:{url:"{login}/*", query, form:{page:1, sort_by:"updated_at",sort_direction:"desc", per_page:10, category:void 0, status:void 0},category:["study","manage"],status:["processing","end"]},
@ -155,7 +155,9 @@ weapps:{
basic_info:{url:"{course_id}/*",query,disp:"课堂基本信息"}, basic_info:{url:"{course_id}/*",query,disp:"课堂基本信息"},
course_activities:{url:"{course_id}/*",query,form:{page:1, limit:20}}, course_activities:{url:"{course_id}/*",query,form:{page:1, limit:20}},
}, },
course_member_attendances:{query, form:{attendance_id:null, attendance_mode:null, code:void 0}, config, disp:"课堂成员签到"}, course_member_attendances:{query, form:{_:1, GET:{page:1, limit: 10},POST:{attendance_id:null, attendance_mode:null, code:void 0}}, config, disp:"课堂成员签到",
update_status:{query, config, form:{attendance_id:null, attendance_status:null, course_id:null, user_id:null}}
},
challenges: { challenges: {
is_play: {} is_play: {}
}, },

@ -12,6 +12,7 @@ export default class Client{
this.load_cookies(); this.load_cookies();
this.load_user(); this.load_user();
this.randomcode=0; this.randomcode=0;
this.tidingGet = 0;
this.cb={ this.cb={
before:{}, before:{},
success:{}, success:{},
@ -49,15 +50,15 @@ export default class Client{
this.save_user(); this.save_user();
}); });
this.on("success","users.get_user_info", res=>{ this.on("success","users.get_user_info", res=>{
this.user = res; this.updateUserInfo(res);
this.synch = 1; this.synch = 1;
this.save_user(); this.save_user();
this.save_cookies(); this.save_cookies();
}); });
this.on("success","accounts.login", res=>{ this.on("success","accounts.login", res=>{
this.synch=0; this.synch=0;
this.user = {};
this.save_cookies(); this.save_cookies();
this.getTidingInfo({login:res.login});
}); });
this.on("success", "accounts.register", res => { this.on("success", "accounts.register", res => {
this.synch = 0; this.synch = 0;
@ -69,11 +70,20 @@ export default class Client{
}) })
} }
getTidingInfo({login}={}){ getTidingInfo({login}={}){
const handler = {
success: res=>{this.tidingGet =1},
fail: res=>{this.tidingGet=0}
};
return this.api("users.unread_message_info")({login}).then(res => { return this.api("users.unread_message_info")({login}).then(res => {
if (res.unread_message_count) if (res.unread_message_count)
wx.setTabBarBadge({ index: 1, text: res.unread_message_count.toString() }); wx.setTabBarBadge({ index: 1, text: res.unread_message_count.toString(),...handler});
else if (res.unread_tiding_count) else if (res.unread_tiding_count)
wx.showTabBarRedDot({ index: 1 }) wx.showTabBarRedDot({index: 1,...handler});
else{
wx.removeTabBarBadge({index:1});
wx.hideTabBarRedDot({index:1});
this.tidingGet = 1;
}
}).catch(e => { }).catch(e => {
console.error("getTidingInfo", e); console.error("getTidingInfo", e);
}); });
@ -91,7 +101,19 @@ export default class Client{
await this.callApi({ name: "users.get_user_info" }); await this.callApi({ name: "users.get_user_info" });
} }
} }
return {synch:this.synch,change:old_id!=this.user.user_id,user:this.user}; let changed = old_id != this.user.user_id;
if (this.user.login && this.user.login!=2){
if(changed)
this.tidingGet = 0;
if(!this.tidingGet){
this.getTidingInfo({ login: this.user.login });
}
}
return {synch:this.synch,changed, user:this.user};
}
updateUserInfo(info){
this.user = {...this.user, ...info};
} }
refresh_key(){ refresh_key(){
let newCode = Date.parse(Date()) / 1e3; let newCode = Date.parse(Date()) / 1e3;

@ -10,15 +10,12 @@ export default class{
let cookies = string.split(/,\s?(?=[^=,\;]*?\=)/ig); let cookies = string.split(/,\s?(?=[^=,\;]*?\=)/ig);
return cookies; return cookies;
} }
request({url, data, header, method, dataType, responseType, success, fail, complete}) { request({ url, header, success,...options}) {
console.debug("wx.requests arguments", arguments[0], new Date().getTime()); console.debug("wx.requests arguments", arguments[0], new Date().getTime());
return wx.request({ return wx.request({
...options,
url, url,
data,
header: {...header, "Cookie": this.cookies}, header: {...header, "Cookie": this.cookies},
method,
dataType,
responseType,
success: (res) => { success: (res) => {
if (res.cookies && res.cookies.length>0) if (res.cookies && res.cookies.length>0)
this.saveCookies(res.cookies); this.saveCookies(res.cookies);
@ -42,9 +39,7 @@ export default class{
} }
}) })
} }
}, }
fail: fail,
complete: complete
}); });
} }
uploadFile({url, filePath, name, header, formData, success, fail, complete}){ uploadFile({url, filePath, name, header, formData, success, fail, complete}){

@ -100,11 +100,26 @@ class AccountManager{
this.loadStorage(); this.loadStorage();
} }
addAccount(account, sync=1){ addAccount(account, sync=1){
this.accounts = this.accounts.filter(i => i.login != account.login && i.user_id != account.user_id); if(!this.updateAccount(account))
this.accounts.push(account); this.accounts.push(account);
if(sync) if(sync)
this.setStorage(); this.setStorage();
} }
updateAccount(account, sync=1){
let flag = false;
for (var i = 0; i < this.accounts.length; i++) {
if (this.accounts[i].login == account.login || this.accounts[i].user_id == account.user_id) {
this.accounts[i] = { ...this.accounts[i], ...account };
flag = true;
break;
}
}
if (this.currentAccount&&(this.currentAccount.login == account.login || this.currentAccount.user_id == account.user_id))
this.currentAccount = {...this.currentAccount, ...account};
if (sync)
this.setStorage();
return flag;
}
deactivateCurrentAccount(){ deactivateCurrentAccount(){
if(this.currentAccount){ if(this.currentAccount){
this.currentAccount.active = 0; this.currentAccount.active = 0;
@ -185,3 +200,8 @@ class AccountManager{
} }
export const accountManager = global.accountManager = new AccountManager(); export const accountManager = global.accountManager = new AccountManager();
export function getWXACodeUrl({url, scene}){
return global.config.imgDir + "wxacode/" + (url + "?" + scene).replace(/[\/?&]/g, "_") + ".jpeg";
}

@ -1,3 +1,4 @@
import {accountManager} from "../../js/utils";
const app = getApp(); const app = getApp();
Page({ Page({
@ -80,21 +81,13 @@ Page({
switch (res.tapIndex) { switch (res.tapIndex) {
case 0: case 0:
wx.previewImage({ wx.previewImage({
urls: [global.eduImgDir + dataset.url], urls: [global.config.eduImgDir + dataset.url],
}); });
break; break;
case 1: case 1:
wx.chooseImage({ wx.navigateTo({
count: 1, url: '/avatar/pages/image_crop/image_crop'
success: function (res) { });
console.log("choose image")
console.log(res);
const src = res.tempFilePaths[0]
wx.navigateTo({
url: '/avatar/pages/image_crop/image_crop?src=' + src, fail: console.error
})
},
})
break; break;
} }
} }
@ -140,18 +133,33 @@ Page({
if (res.user.user_id != 2) if (res.user.user_id != 2)
this.refresh(); this.refresh();
else else
this.setData({ user: {} }) this.setData({ user: {}, currentLogin:"" })
}); });
}, },
refresh: function () { refresh: function () {
app.api("users.homepage_info")() app.api("users.homepage_info")()
.then(res => { .then(res => {
let { name: show_name, avatar_url: image_url} = res;
if(show_name&&image_url)
app.updateUserInfo({ show_name, image_url});
console.log("get_homepage_info"); console.log("get_homepage_info");
console.log(res) console.log(res)
this.setData({ user: res }) this.setData({ user: res })
if (!res.attendance_signed && this.data.auto_attendance) if (!res.attendance_signed && this.data.auto_attendance)
this.attendance({ show: 0 }) this.attendance({ show: 0 })
}); });
let account = accountManager.getCurrentAccount();
if (account && account.login && account.active) {
var currentLogin = account.login;
/*if(account.login.indexOf("@")!=-1)
currentLogin = currentLogin.replace(/.{4}()/)
else
currentLogin = currentLogin.replace(/./,"");
*/
} else {
var currentLogin = "";
}
this.setData({ currentLogin });
}, },
tapAutoAttendence() { tapAutoAttendence() {
this.setData({ auto_attendance: !this.data.auto_attendance }); this.setData({ auto_attendance: !this.data.auto_attendance });
@ -171,6 +179,9 @@ Page({
app.showMsg(res); app.showMsg(res);
}) })
}, },
enterUserinfo(){
app.navigateTo({url:"{user_info}"});
},
onShareAppMessage: function () { onShareAppMessage: function () {
console.log(this.route, this.__route__,212121); console.log(this.route, this.__route__,212121);
} }

@ -12,6 +12,7 @@
<iconfont catchtap class="auth-tip" type="authentication" color="{{user.authentication?'#00c6da':'#bbbbbb'}}" size="22" info="{{user.authentication?'已实名认证':'未实名认证'}}" /> <iconfont catchtap class="auth-tip" type="authentication" color="{{user.authentication?'#00c6da':'#bbbbbb'}}" size="22" info="{{user.authentication?'已实名认证':'未实名认证'}}" />
<iconfont catchtap type="certification" size="22" color="{{user.professional_certification?'#00c6da':'#bbbbbb'}}" info="{{user.professional_certification?'已职业认证':'未职业认证'}}" /> <iconfont catchtap type="certification" size="22" color="{{user.professional_certification?'#00c6da':'#bbbbbb'}}" info="{{user.professional_certification?'已职业认证':'未职业认证'}}" />
</view> </view>
<iconfont type="jinru" color="dimgrey" size="15" class="enter"></iconfont>
</navigator> </navigator>
<view class="relation-info"> <view class="relation-info">
<view class="relation-detail"> <view class="relation-detail">
@ -58,6 +59,7 @@
</navigator> </navigator>
<navigator class="nav" url="/account/pages/accounts/accounts"> <navigator class="nav" url="/account/pages/accounts/accounts">
<iconfont class="icon" size="21" type="qiehuanzhanghao" />切换账号 <iconfont class="icon" size="21" type="qiehuanzhanghao" />切换账号
<text class="tip">{{currentLogin}}</text>
<iconfont type="jinru" color="dimgrey" size="15" class="enter"></iconfont> <iconfont type="jinru" color="dimgrey" size="15" class="enter"></iconfont>
</navigator> </navigator>
</view> </view>
@ -68,5 +70,5 @@
</view> </view>
<view class="version"> <view class="version">
<text catchtap="onTapVersion">当前版本:{{version}}</text> <text catchtap="onTapVersion" bindlongpress="enterUserinfo">当前版本:{{version}}</text>
</view> </view>

@ -19,12 +19,21 @@
position: relative; position: relative;
} }
.nav>.enter{ .nav>.enter{
position: absolute;
right: 12px; right: 12px;
} }
.nav>.icon{ .nav>.icon{
margin-right: 10px; margin-right: 10px;
} }
.nav>.tip{
right: 36px;
color: dimgray;
font-size: 12px;
}
.nav>.enter, .nav>.tip{
position: absolute;
top:50%;
transform: translateY(-50%);
}
button.nav::after{ button.nav::after{
border: none; border: none;
} }
@ -71,6 +80,11 @@ navigator[hidden] {
height: 50px; height: 50px;
display: flex; display: flex;
align-items: center; align-items: center;
position: relative;
}
.user-info>.enter{
position: absolute;
right: 12px;
} }
.user-detail{ .user-detail{
margin-left: 20px; margin-left: 20px;

@ -11,16 +11,17 @@ const categories = [{ text: "我学习的", value: "study" }, { text: "我管理
Component({ Component({
data: { data: {
imgDir: global.config.imgDir, imgDir: global.config.imgDir,
attachDir:global.config.attachDir, attachDir: global.config.attachDir,
categories, categories,
statuses: [{ text: "正在进行", value: "processing" }, { text: "已结束", value: "end" }], statuses: [{ text: "正在进行", value: "processing" }, { text: "已结束", value: "end" }],
courses: [], courses: [],
status: 0, status: 0,
user: {}, user: {},
loading: true,
current_cate: -1 current_cate: -1
}, },
attached() { attached() {
this.options = {page:1, limit:15}; this.options = { page: 1, limit: 15 };
if (app.user().is_teacher) if (app.user().is_teacher)
var current_cate = 1; var current_cate = 1;
else else
@ -30,7 +31,7 @@ Component({
pageLifetimes: { pageLifetimes: {
show: function () { show: function () {
if (this.data.current_cate >= 0) { if (this.data.current_cate >= 0) {
this.pullCourses({refresh:2}); this.pullCourses({ refresh: 2 });
} }
} }
}, },
@ -38,24 +39,24 @@ Component({
onCategoryChange: function ({ detail: { current, value } }) { onCategoryChange: function ({ detail: { current, value } }) {
console.log("category change", current); console.log("category change", current);
this.options["category"] = value.value; this.options["category"] = value.value;
this.pullCourses({refresh:1}); this.pullCourses({ refresh: 1 });
this.setData({ category: value.value }); this.setData({ category: value.value });
}, },
onStatusChange: function ({ detail: { value } }) { onStatusChange: function ({ detail: { value } }) {
this.options["status"] = value.value; this.options["status"] = value.value;
this.pullCourses({refresh:1}); this.pullCourses({ refresh: 1 });
}, },
addCourse(){ addCourse() {
if(app.user().user_identity=="学生"){ if (app.user().user_identity == "学生") {
this.setData({showModal:1}); this.setData({ showModal: 1 });
}else{ } else {
wx.showActionSheet({ wx.showActionSheet({
itemList: ["加入课堂",'创建课堂'], itemList: ["加入课堂", '创建课堂'],
success:res=>{ success: res => {
if(res.tapIndex==0){ if (res.tapIndex == 0) {
this.setData({ showModal: 1 }); this.setData({ showModal: 1 });
}else{ } else {
app.navigateTo({ url:"{course_setting}?intent=create"}); app.navigateTo({ url: "{course_setting}?intent=create" });
} }
} }
}) })
@ -69,22 +70,21 @@ Component({
}) })
}, },
pullCourses: function ({ refresh=0} = {}) { pullCourses: function ({ refresh = 0 } = {}) {
if(refresh){ if (refresh) {
if(refresh==1){ if (refresh == 1) {
this.options.page=1; this.options.page = 1;
var {options:data}=this; var { options: data } = this;
}else if(refresh==2){ } else if (refresh == 2) {
var {limit, page, status, category}=this.options; var { limit, page, status, category } = this.options;
var data = {limit: page*limit, page:1, status, category}; var data = { limit: page * limit, page: 1, status, category };
} }
}else{ } else {
this.options.page++; this.options.page++;
var {options:data}=this; var { options: data } = this;
} }
return app.callApi({ return app.callApi({
name: "weapps.home", data, name: "weapps.home", data
complete: () => { this.setData({ loading: false }) }
}) })
.then(res => { .then(res => {
let { courses } = res; let { courses } = res;
@ -92,20 +92,19 @@ Component({
courses = courses.filter(i => { courses = courses.filter(i => {
return i.is_end == (data.status == "end") return i.is_end == (data.status == "end")
}); });
//console.log(courses); if (!refresh)
if(!refresh)
courses = this.data.courses.concat(courses); courses = this.data.courses.concat(courses);
this.setData({ courses }); this.setData({ courses, loading: false });
}).catch(e => { }).catch(e => {
this.setData({ courses: [] }); this.setData({ courses: [], loading: false });
}); });
}, },
onReachBottom(){ onReachBottom() {
this.pullCourses(); this.pullCourses();
}, },
onPullDownRefresh: function () { onPullDownRefresh: function () {
//console.log("pulldownrefresh"); //console.log("pulldownrefresh");
this.pullCourses({refresh:2}); this.pullCourses({ refresh: 2 });
}, },
} }
}) })

@ -5,7 +5,7 @@
<nav-bar list="{{statuses}}" width="300" itemWidth="140" cancellable="1" current="-1" type="plain" bg='' bindchange="onStatusChange"/> <nav-bar list="{{statuses}}" width="300" itemWidth="140" cancellable="1" current="-1" type="plain" bg='' bindchange="onStatusChange"/>
</view> </view>
<scroll-view scroll-y="1" refresher-enabled="1" bindrefresherrefresh="onPullDownRefresh" bindscrolltolower="onReachBottom" lower-threshold="120" bindrefresh="onPullDownRefresh" class="body"> <scroll-view scroll-y="1" refresher-enabled="1" bindrefresherrefresh="onPullDownRefresh" bindscrolltolower="onReachBottom" lower-threshold="120" bindrefresh="onPullDownRefresh" class="body">
<view hidden="{{courses.length!=0 || loading}}" class="none-content"> <view wx:if="{{courses.length==0&&!loading}}" class="none-content">
<image class="none-content" src="{{imgDir}}blank1.png" mode="aspectFit"></image> <image class="none-content" src="{{imgDir}}blank1.png" mode="aspectFit"></image>
<text class="none-content hint">空空如也!</text> <text class="none-content hint">空空如也!</text>
</view> </view>

@ -21,12 +21,15 @@ Component({
app.syncUser() app.syncUser()
.then(res => { .then(res => {
if (res.user.user_id != this.user_id) { if (res.user.user_id != this.user_id) {
this.pullShixuns({ refresh: 1 }); if(res.user_id==2)
this.setData({shixuns:[]})
else
this.pullShixuns({ refresh: 1 })
this.user_id = res.user.user_id; this.user_id = res.user.user_id;
} }
}) })
}, },
async pullShixuns({refresh=0}={}){ pullShixuns({refresh=0}={}){
if(refresh){ if(refresh){
if(refresh==1){ if(refresh==1){
this.options.page = 1; this.options.page = 1;
@ -38,11 +41,14 @@ Component({
}else{ }else{
this.options.page++; this.options.page++;
var {options} = this; var {options} = this;
}let {shixuns} = await app.api("users.shixuns")(options); }
if(!refresh) return app.api("users.shixuns")(options).then(({shixuns})=>{
shixuns = this.data.shixuns.concat(shixuns); if (!refresh)
this.setData({shixuns}); shixuns = this.data.shixuns.concat(shixuns);
return shixuns; this.setData({ shixuns });
}).catch(e=>{
this.setData({shixuns:[]});
})
}, },
onPullDownRefresh(){ onPullDownRefresh(){
this.pullShixuns({refresh:2}); this.pullShixuns({refresh:2});

@ -14,9 +14,9 @@
"newFeature": true, "newFeature": true,
"coverView": true, "coverView": true,
"nodeModules": false, "nodeModules": false,
"autoAudits": false, "autoAudits": true,
"showShadowRootInWxmlPanel": true, "showShadowRootInWxmlPanel": true,
"scopeDataCheck": false, "scopeDataCheck": true,
"uglifyFileName": false, "uglifyFileName": false,
"checkInvalidKey": true, "checkInvalidKey": true,
"checkSiteMap": true, "checkSiteMap": true,
@ -29,7 +29,7 @@
"bundle": false "bundle": false
}, },
"compileType": "miniprogram", "compileType": "miniprogram",
"libVersion": "2.10.1", "libVersion": "2.10.3",
"appid": "wxc5c2b711f23f3a1d", "appid": "wxc5c2b711f23f3a1d",
"projectname": "educoder_weapp", "projectname": "educoder_weapp",
"debugOptions": { "debugOptions": {
@ -110,6 +110,48 @@
"pathName": "course/pages/course_invite/course_invite", "pathName": "course/pages/course_invite/course_invite",
"query": "", "query": "",
"scene": null "scene": null
},
{
"id": -1,
"name": "course/pages/course/course",
"pathName": "course/pages/course/course",
"query": "course_id=5141",
"scene": null
},
{
"id": -1,
"name": "account/pages/accounts/accounts",
"pathName": "account/pages/accounts/accounts",
"query": "",
"scene": null
},
{
"id": 9,
"name": "exercise/pages/exercise/exercise",
"pathName": "exercise/pages/exercise/exercise",
"query": "exercise_id=4322",
"scene": null
},
{
"id": -1,
"name": "attendance",
"pathName": "course/modules/attendance/attendance",
"query": "course_identity=2&course_id=5141&id_=956",
"scene": null
},
{
"id": 11,
"name": "course/pages/attendance_detail/attendance_detail",
"pathName": "course/pages/attendance_detail/attendance_detail",
"query": "course_identity=2&course_id=5141&code=&mode=&attendance_id=956",
"scene": null
},
{
"id": -1,
"name": "accounts",
"pathName": "account/pages/accounts/accounts",
"query": "",
"scene": null
} }
] ]
} }

Loading…
Cancel
Save