增加实训模块

* A 实训模块
 * A 我参与的实训
 * A 搜索模块
 * A 修改密码模块
 * A 消息中心分类
 * A 加入课堂对话框增加扫码功能
 * U 部分按钮样式更新
 * U 我的课堂上拉加载更多 
 * F 加入课堂后Toast提示不消失
 * F 进入app未登录时获取消息通知报错
master
educoder_weapp 5 years ago
parent 62e5403be7
commit ef84c44d00

@ -1,11 +1,10 @@
# 简介
educoder微信小程序帮助使用[educoder平台](https://www.educoder.net)的应用,方便在手机上使用。利用educoder网站的API搭建
educoder微信小程序帮助使用[educoder平台](https://www.educoder.net)的应用,方便在手机上使用。
## 源码
[https://github/jinke18/smart_class](https://github.com/jinke18/smart_class)
## 小程序码
![小程序码](/images/weapp_code_smart_class.png)
![小程序码](/images/weacode.jpg)
# 功能介绍
@ -38,27 +37,3 @@ educoder微信小程序帮助使用[educoder平台](https://www.educoder.net)
## 其他
账号的注册、登陆、找回密码、头像更改等
# 实现
## educoder平台接入
使用HTTP与平台的api接口交互
接口列表如下
- 搜索课堂https://www.educoder.net/api/courses.json
- 查询用户的课堂https://www.educoder.net/api/users/<user_id>/courses.json
- 查询学校https://www.educoder.net/api/schools/school_list.json
- 新建课堂https://www.educoder.net/api/courses.json
- 加入课堂https://www.educoder.net/api/courses/apply_to_join_course.json
- 新建试卷https://www.educoder.net/api/courses/<course_id>/exercises/new.json
- 查询试卷https://www.educoder.net/api/courses/<course_id>/exercises.json
- 班级文件资源https://www.educoder.net/api/files.json
# 教室学员在位情况及分数的同步实现
使用了[leancloud](https://www.leancloud.cn/)提供的javascript开发包实现数据同步功能, 如学员在位情况、分数的同步, 其底部技术为websocket

@ -0,0 +1,30 @@
## v0.12.0
* A 实训模块
* A 我参与的实训
* A 搜索模块
* A 修改密码模块
* A 消息中心分类
* A 加入课堂对话框增加扫码功能
* U 部分按钮样式更新
* U 我的课堂上拉加载更多
* F 加入课堂后Toast提示不消失
* F 进入app未登录时获取消息通知报错
## v0.11.1
* U error-page多按钮操作
* U 课堂错误处理界面返回主页操作
* F 非课程成员进入时弹出弹窗
* F 试卷填空题答题bug修补
## v0.11.0
* A 课程-普通作业模块
* A 页面不存在时进入主页
* A referrerInfo 分析
* A 主页课堂菜单操作
* A error-page组件错误处理界面
* U 加入课堂对话框功能升级
* U 课堂页面错误处理
* U 课程邀请界面改进,增加已停用时的图标
* U 改变小程序码生成接口为getWXACodeUnlimited
* F 试卷题目富文本显示异常
* D 隐藏发现页入口

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

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

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

@ -0,0 +1,17 @@
<view class="container">
<form bindsubmit="changePassword" bindreset="onFormReset">
<view class="input-wrap">
<input password name="old_password" placeholder="原密码"/>
</view>
<view class="input-wrap">
<input password name="password" placeholder="新密码"/>
</view>
<view class="input-wrap">
<input password name="password_confirmation" placeholder="请再次输入新密码"/>
</view>
<view class="buttons">
<button form-type="submit" type="main">确认</button>
<button form-type="reset" type="main" plain>返回</button>
</view>
</form>
</view>

@ -0,0 +1,17 @@
.container{
padding-top: 20px;
}
.input-wrap{
border-radius: 4px;
border: 1px solid grey;
margin: 16px 18px;
}
.input-wrap>input{
padding: 8px 10px;
}
.buttons>button{
margin: 0 10px;
transform: scale(0.72);
}

@ -1,4 +1,4 @@
{
"navigationBarTitleText": "登",
"navigationBarTitleText": "登",
"usingComponents": {}
}

@ -24,7 +24,7 @@
bindconfirm="login">
</input>
</view>
<button class="login-submit" type="primary" bindtap="login" bindlongpress="login_test">登陆</button>
<button class="login-submit" type="main" bindtap="login" bindlongpress="login_test">登录</button>
</view>
<view style="height:36px;">
<navigator class="tappable register" url="../register/register" open-type="redirect">注册</navigator>

@ -26,7 +26,7 @@
placeholder="输入8-16位密码">
</input>
</view>
<button class="submit" type="primary" form-type="submit">注册</button>
<button class="submit" type="main" form-type="submit">注册</button>
</form>
<navigator class="tappable login" url="../login/login" open-type="redirect">返回登</navigator>
<navigator class="tappable login" url="../login/login" open-type="redirect">返回登</navigator>
</view>

@ -33,8 +33,8 @@
</input>
<button class="code" bindtap="send_code" disabled="{{countDownNum}}">{{countDownNum?countDownNum+'秒后重试':'获取验证码'}}</button>
</view>
<checkbox checked="{{autologin}}" bindtap="updateAutologin">重置后自动登</checkbox>
<button class="submit" type="primary" form-type="submit">重置密码</button>
<checkbox checked="{{autologin}}" bindtap="updateAutologin">重置后自动登</checkbox>
<button class="submit" type="main" form-type="submit">重置密码</button>
</form>
<navigator class="tappable login" url="../login/login" open-type="redirect">返回登</navigator>
<navigator class="tappable login" url="../login/login" open-type="redirect">返回登</navigator>
</view>

@ -0,0 +1,155 @@
//miniprogram/account/pages/test/test.js
//我我我等等是顶顶顶
Page({
/**
* 页面的初始数据
*/
data: {
code: `.we-editor{
font: 12px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;
overflow: scroll;
white-space: pre;
cursor: text;
height: 100vh;
position: relative;
}
.ta{
width: auto;
background: #002B36;
color: #93A1A1;
font: 14px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;
white-space: pre;
padding: 12px;
}
.we-cursor{
height: 14px;
min-height: 14px;
width: 1px;
position: absolute;
font-size: 10px;
color: white;
background: white;
}
.theme-dark{
background-color: #002B36;
color: #93A1A1;
}
.we-line{
position: absolute;
}
.we-word{
}
.theme-dark .we-comment{
font-style: italic;
color: #657B83;
}
.theme-dark .we-keyword{
color: #859900;
}
.theme-dark .we-number{
color: #D33682;
}
.theme-dark .we-string{
color: #2AA198;
}
.we-chinese{
width: 13.1953px;
display: inline-block;
}`,
lines: [[]],
wordWidth: 6.59765,
cursor: {
row: 0,
col: 0,
cursor: 0,
value: ""
}
},
lines: [""],
indent: 0,
cursor: 0,
getColByValue(value, cursor) {
console.log(value, cursor);
return value.slice(0, cursor).length * 2 - value.slice(0, cursor).replace(/[\u4e00-\u9fa5]/g, "").length;
},
getCursor(value, col) {
var cursor = col;
var newCol = this.getColByValue(value, cursor);
while (newCol != col) {
cursor = cursor - 1;
newCol = this.getColByValue(value, cursor);
}
return cursor;
},
parseLine(value) {
var line = [];
for (var word of value.split(/([\u4e00-\u9fa5]+)/)) {
var match = word.match(/[\u4e00-\u9fa5]+/)
;
if (match) {
for (var i of word)
line.push({ class: "we-chinese", value: i });
} else {
line.push({ class: "", value: word });
}
}
return line;
},
onInput(e) {
console.log(e);
let { keyCodevalue, cursor, value } = e.detail;
console.log(e.detail)
var row = this.data.cursor.row;
if (value.length == 0 && this.data.cursor.row != 0) {
var { lines } = this.data;
lines.splice(row, 1);
var newValue = this.lines[row - 1];
this.setData({ lines, "cursor.row": row - 1, "cursor.col": this.getColByValue(newValue), "cursor.value": "" + newValue });
this.lines.splice(row, 1);
return;
}
this.setData({ "cursor.col": this.getColByValue(value, cursor) });
var line = this.parseLine(value);
this.setData({ ['lines[' + this.data.cursor.row + "]"]: line });
this.lines[row] = value;
var indentMatch = value.match(/(^ *)/)
if (indentMatch)
this.indent = indentMatch[1].length
else
this.indent = 0;
this.cursor = cursor;
//return "";
},
onTapEditor({ detail: { x, y } }) {
//console.log(e);
var row = Math.min(Math.round(y / 14), this.data.lines.length - 1);
var value = this.lines[row];
var col = Math.min(Math.round(x / this.data.wordWidth), this.getColByValue(value));
this.cursor = this.getCursor(value, col);
this.setData({ "cursor.row": row, "cursor.col": col, "cursor.focus": 1, "cursor.cursor": this.cursor, "cursor.value": value });
},
onInputConfirm(e) {
//console.log(e);
var row = this.data.cursor.row;
var value = this.lines[row];
var rightValue = " ".repeat(this.indent) + value.slice(this.cursor);
var leftValue = value.slice(0, this.cursor);
var { lines } = this.data;
lines.splice(row + 1, 0, this.parseLine(rightValue));
lines[row] = this.parseLine(leftValue);
this.setData({ lines, "cursor.value": rightValue });
this.lines.splice(row + 1, 0, rightValue);
this.lines[row] = leftValue;
this.setData({ "cursor.cursor": this.indent, "cursor.focus": 2, "cursor.row": row + 1, "cursor.col": this.indent });
},
onLoad: function (options) {
}
})

@ -0,0 +1,15 @@
<!--miniprogram/account/pages/test/test.wxml-->
<textarea class="ta" maxlength="-1" value="{{code}}">
</textarea>
<view wx:if="{{false}}" class="we-editor theme-dark" bindtap="onTapEditor">
<input class="we-cursor" bindinput="onInput" style="top:{{cursor.row*14}}px;left:{{cursor.col*wordWidth}}px" maxlength="-1" value="{{cursor.value}}" cursor="{{cursor.cursor}}" focus="{{cursor.focus}}" confirm-hold="1" bindconfirm="onInputConfirm"></input>
<view class="we-line" style="top:{{index*14}}px" wx:for="{{lines}}" wx:for-item="line" wx:key="index">
<block wx:for="{{line}}" wx:for-item="word">
<text class="we-word {{word.class}}">{{word.value}}</text>
</block>
</view>
</view>

@ -0,0 +1,55 @@
.we-editor{
font: 12px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;
overflow: scroll;
white-space: pre;
cursor: text;
height: 100vh;
position: relative;
}
.ta{
width: auto;
background: #002B36;
color: #93A1A1;
font: 14px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;
white-space: pre;
padding: 12px;
height: 100vh;
}
.we-cursor{
height: 14px;
min-height: 14px;
width: 1px;
position: absolute;
font-size: 10px;
color: white;
background: white;
}
.theme-dark{
background-color: #002B36;
color: #93A1A1;
}
.we-line{
position: absolute;
}
.we-word{
}
.theme-dark .we-comment{
font-style: italic;
color: #657B83;
}
.theme-dark .we-keyword{
color: #859900;
}
.theme-dark .we-number{
color: #D33682;
}
.theme-dark .we-string{
color: #2AA198;
}
.we-chinese{
width: 13.1953px;
display: inline-block;
}

@ -17,7 +17,6 @@ App({
user(){ return client.user},
syncUser(options){return client.syncUser(options)},
onLaunch: function (options) {
console.log("applaunch",options);
if(Object.keys(options.referrerInfo).length>0){
let { appId, extraData } = options.referrerInfo
const db = wx.cloud.database();
@ -32,25 +31,21 @@ App({
}
},
onShow(){
this.api("users.unread_message_info")().then(res=>{
if(res.unread_message_count)
wx.setTabBarBadge({index:1,text:res.unread_message_count.toString()});
else if(res.unread_tiding_count)
wx.showTabBarRedDot({index:1})
})
client.getTidingInfo();
},
onPageNotFound(res) {
this.redirectTo({url:"{my_courses}"});
},
showError(e){
showError(e,duration){
wx.showToast({
title: e.message,
icon:"none"
icon:"none",
duration
})
},
showMsg(res){
showMsg(res, duration){
wx.showToast({
title: res.message,
title: res.message,duration
})
},
getPageUrl(url,root="/"){

@ -1,9 +1,16 @@
{
"window": {
"backgroundTextStyle": "dark",
"navigationBarBackgroundColor": "#fbfbfb",
"navigationBarTextStyle": "black",
"navigationBarTitleText": "EduCoder",
"backgroundColor": "#f5f5f5"
},
"pages": [
"pages/my_courses/my_courses",
"pages/main/main",
"pages/findmore/findmore",
"pages/courses/courses",
"pages/my/my",
"pages/profile/profile",
"pages/tidings/tidings",
"path/pages/path/path"
],
@ -15,7 +22,9 @@
"pages/reset_password/reset_password",
"pages/register/register",
"pages/agreement/agreement",
"pages/about/about"
"pages/about/about",
"pages/test/test",
"pages/change_password/change_password"
]
},
{
@ -54,21 +63,27 @@
]
},
{
"root": "challenge",
"root": "task",
"pages": [
"pages/task/task"
]
},
{
"root": "pages/search",
"pages": [
"pages/challenge/challenge"
"search"
]
}
],
"preloadRule": {
"pages/my/my": {
"pages/profile/profile": {
"network": "all",
"packages": [
"avatar",
"account"
]
},
"pages/my_courses/my_courses": {
"pages/main/main": {
"network": "all",
"packages": [
"course",
@ -83,20 +98,14 @@
]
}
},
"window": {
"backgroundTextStyle": "dark",
"navigationBarBackgroundColor": "#fbfbfb",
"navigationBarTextStyle": "black",
"navigationBarTitleText": "EduCoder",
"backgroundColor": "#f5f5f5"
},
"tabBar": {
"selectedColor": "#1890ff",
"color": "#8a8a8a",
"list": [
{
"pagePath": "pages/my_courses/my_courses",
"text": "我的课堂",
"pagePath": "pages/main/main",
"text": "我的",
"iconPath": "images/tab_study_default.png",
"selectedIconPath": "images/tab_study_pressed.png"
},
@ -107,12 +116,13 @@
"selectedIconPath": "images/tab_tiding_pressed.png"
},
{
"pagePath": "pages/my/my",
"pagePath": "pages/profile/profile",
"iconPath": "images/tab_my_default.png",
"selectedIconPath": "images/tab_my_pressed.png",
"text": "我的"
"text": "个人中心"
}
]
},
"navigateToMiniProgramAppIdList":["wx2402d86a6b534f77"],
"sitemapLocation": "sitemap.json"
}

@ -17,7 +17,7 @@ page {
opacity: .6;
}
.container {
padding: 4px 12px;
padding: 0 12px;
}
.hint,
.error
@ -53,4 +53,17 @@ radio .wx-radio-input.wx-radio-input-checked{
}
checkbox .wx-checkbox-input.wx-checkbox-input-checked {
color: #00b0f0;
}
button[type=main][plain],button[type=cap][plain]{
color: #00b0f0;
border: 1px solid #00b0f0;
background-color: transparent;
}
button[type=cap],button[type=main]{
background: #00b0f0;
color: white;
}
button[type=cap]{
border-radius: 30px;
}

@ -1,66 +0,0 @@
// miniprogram/challenge/pages/challenge/challenge.js
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})

@ -1,2 +0,0 @@
<!--miniprogram/challenge/pages/challenge/challenge.wxml-->
<text>miniprogram/challenge/pages/challenge/challenge.wxml</text>

@ -1 +0,0 @@
/* miniprogram/challenge/pages/challenge/challenge.wxss */

@ -13,39 +13,37 @@ Component({
// 多少秒后关闭
duration: {
type: Number,
value: 3.2
value: 3
}
},
/**
* 组件的初始数据
*/
data: {
SHOW_TOP: false,
SHOW_MODAL: false
SHOW:false,
SHOW_TOP: true,
SHOW_MODAL: false,
statusBarHeight:20
},
ready: function () {
// 判断是否已经显示过
let cache = wx.getStorageSync(STORAGE_KEY);
if (cache) return;
// 没显示过,则进行展示
this.setData({
SHOW_TOP: true
SHOW:true
});
// 关闭时间
setTimeout(() => {
this.setData({
SHOW_TOP: false
})
}, this.data.duration * 1000);
},
/**
* 组件的方法列表
*/
attached(){
wx.getSystemInfo({
success: res=> {
let {statusBarHeight} = res;
this.setData({statusBarHeight});
}
})
},
methods: {
// 显示全屏添加说明
showModal: function () {
this.setData({
SHOW_TOP: false,
@ -55,7 +53,7 @@ Component({
okHandler: function () {
this.setData({
SHOW_MODAL: false
SHOW: false
});
wx.setStorage({
key: STORAGE_KEY,

@ -1,24 +1,25 @@
<view class="box" wx:if="{{SHOW_TOP}}">
<view class='arrow'></view>
<view class='body' bindtap='showModal'>
<text>{{text}}</text>
<block wx:if="{{SHOW}}">
<view class="box" wx:if="{{SHOW_TOP}}" style="top:{{statusBarHeight+23.3}}px">
<view class='arrow'></view>
<view class='body' bindtap='showModal'>
<text>{{text}}</text>
</view>
</view>
</view>
<!-- modal -->
<view class='modal' catchtap hidden="{{!SHOW_MODAL}}">
<view style='flex-direction: row;align-items:center;'>
<text>1. 点击右上角按钮</text>
<image src='cloud://educoder.6564-educoder-1300855313/images/add-tip1.png' style='width:100px;height:40px;'></image>
</view>
<view>
<text>2. 点击「添加到我的小程序」</text>
<image src='cloud://educoder.6564-educoder-1300855313/images/add-tip2.png' style='width:100%;' mode="widthFix"></image>
</view>
<!-- 知道了 -->
<view class='ok-btn' hover-class='btn-hover' bindtap='okHandler'>
<view class='modal' catchtap hidden="{{!SHOW_MODAL}}">
<view style='flex-direction: row;align-items:center;'>
<text>1. 点击右上角按钮</text>
<image src='cloud://educoder.6564-educoder-1300855313/images/add-tip1.png' style='width:100px;height:40px;' mode="aspectFit"></image>
</view>
<view>
<text>我知道了!</text>
<text>2. 点击「添加到我的小程序」</text>
<image src='cloud://educoder.6564-educoder-1300855313/images/add-tip2.png' style='width:100%;' mode="widthFix"></image>
</view>
<view class='ok-btn' hover-class='btn-hover' bindtap='okHandler'>
<view>
<text>我知道了!</text>
</view>
</view>
</view>
</view>
</block>

@ -1,35 +1,35 @@
.box {
position: fixed;
top: 0;
right: 0;
left: 0;
z-index: 999;
display: flex;
justify-content: flex-end;
align-items: flex-end;
flex-direction: column;
width: 600rpx;
}
.arrow {
width: 0;
height: 0;
margin-right: 140rpx;
border-width: 20rpx;
margin-right: 66px;
border-width: 10px;
border-style: solid;
border-color: transparent transparent #00b0f0 transparent;
border-color: transparent transparent #0080f0 transparent;
}
.body {
background-color: #00b0f0;
box-shadow: 0 5rpx 10rpx -5rpx #00b0f0;
border-radius: 12rpx;
background-color: #0080f0;
box-shadow: 0 5rpx 10rpx -5rpx #0080f0;
display: flex;
align-items: center;
justify-content: center;
height: 84rpx;
padding: 0 20rpx;
margin-right: 40rpx;
height: 34.7px;
width: 100%;
}
.body > text {
color: #FFF;
color: #fff;
font-size: 28rpx;
font-weight: 400;
}
@ -42,20 +42,23 @@
bottom: 0;
z-index: 999;
background-color: rgba(255, 255, 255, 0.9);
padding: 20px;
padding: 110px 20px 0 20px;
}
.modal > view {
margin: 10px 0;
display: flex;
/* align-items: center; */
flex-direction: column;
}
.modal > view > text {
font-size: 16px;
font-weight: 400;
margin-bottom: 5px;
color: #333;
}
.modal > view > image {
border-radius: 10px;
overflow: hidden;
@ -68,6 +71,7 @@
align-items: center;
justify-content: center;
}
.ok-btn > view {
height: 40px;
width: 120px;
@ -78,11 +82,13 @@
justify-content: center;
border-radius: 40px;
}
.ok-btn > view > text {
font-size: 14px;
color: #fff;
font-weight: 400;
}
.btn-hover {
opacity: 0.6;
}
}

@ -114,6 +114,7 @@ Component({
app.navigateTo({ url: "{course_setting}?course_id" + this.data.data.id });
},
triggerRefresh() {
//console.log("trigger refresh")
this.triggerEvent("refresh", {}, { bubbles: true })
},
}

@ -1,4 +1,6 @@
{
"component": true,
"usingComponents": {}
"usingComponents": {},
"navigationBarBackgroundColor": "#00b0f0",
"navigationBarTextStyle": "white"
}

@ -4,8 +4,8 @@ const imgUrl = config.imgDir+"blank_info_bg.png";
const scenes = {
imgUrl,
401:{
message:"请先登哦",
btnText:"点击登陆"
message:"请先登哦",
buttons:["点击登录"]
}
}
Component({
@ -20,6 +20,10 @@ Component({
bg:{
type:String,
value:"#00b0f0"
},
position:{
type:String,
value:"fixed"
}
},
data: {
@ -28,22 +32,22 @@ Component({
methods: {
refresh(){
let {config, status} = this.data;
var {message="", btnText="", imgUrl=""} = scenes;
var {message="", buttons=[], imgUrl=""} = scenes;
if(scenes[status])
var {message=message, btnText=btnText, imgUrl=imgUrl} = scenes[status];
var {message=message, buttons=buttons, imgUrl=imgUrl} = scenes[status];
if(config[status])
var {message=message, btnText=btnText, imgUrl=imgUrl} = config[status];
this.setData({message, btnText, imgUrl});
var {message=message, buttons=buttons, imgUrl=imgUrl} = config[status];
this.setData({message, buttons, imgUrl});
},
onTapBody(){
console.log("tapbody")
let {status} = this.data;
this.triggerEvent("refresh",{target:"body", status})
},
onTapButton(){
onTapButton({target:{dataset:{current}}}){
console.log("tapButton")
let {status } =this.data;
this.triggerEvent("refresh",{target:"button", status})
let {status} =this.data;
this.triggerEvent("refresh",{target:"button",current, status})
}
}
})

@ -1,7 +1,9 @@
<view wx:if="{{message}}" class="body" style="background:{{bg}}" bindtap="onTapBody">
<view wx:if="{{message}}" class="body" style="background:{{bg}};position:{{position}}" bindtap="onTapBody">
<view class="info">
<image class="image" mode="aspectFit" src="{{imgUrl}}"/>
<view class="message">{{message||'请先登陆哦'}}</view>
<button wx:if="{{btnText}}" class="button" catchtap="onTapButton" size="mini">{{btnText}}</button>
<view class="message">{{message}}</view>
<view class="buttons">
<button wx:for="{{buttons}}" class="button" data-current="{{index}}" catchtap="onTapButton" size="mini">{{item}}</button>
</view>
</view>
</view>

@ -2,7 +2,6 @@
z-index: 1000;
top: 0;
right: 0;
position: fixed;
left: 0;
display: flex;
flex-direction: column;
@ -24,10 +23,17 @@ align-content: center;
}
.message{
padding: 10px 0;
}
.buttons{
width: 100%;
display: flex;
justify-content: center;
}
.button{
color: #0080f0;
font-weight: bold;
margin: 0 12px
}

@ -26,6 +26,55 @@ Component({
hidden:true
},
methods: {
scan(){
wx.scanCode({
success:res=>{
console.log(res);
var fail = false;
if(res.scanType=="QR_CODE")
this.setData({invite_code: res.result});
else if(res.scanType=="WX_CODE"&&res.path){
var match = res.path.match(/course_invite\?(.*)$/)
console.log("match",match);
if(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){
var scene = {};
for (var i of decodeURIComponent(options.scene).split("&")) {
var index = i.indexOf("=");
var k = i.slice(0,index);
var v = i.slice(index+1);
scene[k] = v;
}
console.log(scene);
if(scene.course_id){
var {course_id} = scene;
app.api("weapps.courses.basic_info")({ course_id })
.then(({ course:{invite_code=""}}) => {
console.log(invite_code);
this.setData({invite_code});
}).catch(app.showError);
}else
fail = true;
}else
fail = true;
}else
fail= true;
}else
fail = true;
if(fail)
wx.showToast({
title:"识别失败",icon:"none"
})
}
})
},
cancel: function (event) {
this.setData({ hidden: true });
},
@ -42,16 +91,18 @@ Component({
this.setData(data)
},
join_course: function (event) {
if(this.disabled) return;
let { invite_code="", assistant_professor="", professor,student=""} = this.data;
console.log(this.data);
app.api("courses.apply_to_join_course")({ invite_code, assistant_professor, professor, student })
this.disabled = true;
app.api("courses.apply_to_join_course")({ invite_code, assistant_professor, professor, student,complete: res=>{this.disabled=false}})
.then(res => {
console.log(res);
app.showMsg(res);
app.showMsg(res,800);
if (res.course_id&&this.data.auto_navigate&&res.message.indexOf("助教申请")==-1){
app[this.data.opentype]({
url: `{course}?course_id=${res.course_id}`
});
setTimeout(()=>{
app[this.data.opentype]({
url: `{course}?course_id=${res.course_id}`
});
},800);
}
this.triggerEvent("success");
this.cancel();

@ -1,5 +1,8 @@
<modal class="join_course" wx:if="{{!hidden}}" title="加入课堂" confirm-text="提交" cancel-text="取消" bindcancel="cancel" bindconfirm="join_course">
<input type='text' bindinput="update_invite_code" value="{{invite_code}}" class="code-input {{show_code?'':'hidden'}}" placeholder="邀请码" auto-focus/>
<view class="input-wrap {{show_code?'':'hidden'}}">
<input class="code-input" type='text' bindinput="update_invite_code" value="{{invite_code}}" placeholder="邀请码" auto-focus/>
<image class="scan" src="./scan.png" mode="aspectFit" catchtap="scan"/>
</view>
<checkbox-group class="identities" bindchange="update_identities">
<text>身份:</text>
<checkbox class="identity" color="#00b0f0" disabled="{{assistant_professor}}" checked="{{professor}}" value="professor">教师</checkbox>

@ -1,7 +1,18 @@
.code-input{
.input-wrap{
display: flex;
border-radius: 5px;
border: 1rpx solid;
padding: 6px 10px;
align-items: center;
}
.code-input{
flex: auto;
}
.scan{
flex: none;
width: 18px;
height: 18px;
padding: 2px;
}
.identities{
padding-top: 8px;

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

@ -1,12 +1,13 @@
Component({
externalClasses: ["bar-class", 'item-class'],
properties: {
list:{
type:Array,
observer:function(){
//console.log("observer list",this.data);
if(this.data._itemWidth&&this.data._itemWidth>0)
this.attached();
this.refresh();
}
},
mg:Number,
@ -24,8 +25,9 @@ Component({
},
current:{
type:Number,
observer: function(current){
this.trigger();
observer: function(cur){
this.setData({cur});
this.trigger({source:""});
this.scroll();
}
},
@ -47,38 +49,43 @@ Component({
}
},
data: {
scrollLeft: 0
scrollLeft: 0,
cur:0
},
attached(){
if(this.data.itemWidth==-1)
var _itemWidth = Math.max(this.data.width/this.data.list.length,150);
else
var _itemWidth = this.data.itemWidth;
this.setData({ _itemWidth });
this.scroll();
this.refresh();
},
methods: {
refresh(){
if (this.data.itemWidth == -1)
var _itemWidth = Math.max(this.data.width / this.data.list.length, 150);
else
var _itemWidth = this.data.itemWidth;
this.setData({ _itemWidth });
this.scroll();
},
scroll(){
if(this.data.current>=0&&this.data.autoscroll&&this.data.itemWidth>0){
let scrollLeft = (this.data.current+0.5)*this.data.itemWidth+0.5*this.data.width;
this.setData({scrollLeft})
}
},
trigger(){
let {current} = this.data;
trigger({source=""}){
let {cur:current} = this.data;
if (current >= 0)
var value = this.data.list[current];
else
var value = this.data.default;
this.triggerEvent("change", { current, value});
this.triggerEvent("change", { current, value, source});
},
switchNav({currentTarget:{dataset:{current}}}){
if(current==this.data.current) {
if(current==this.data.cur) {
if(this.data.cancellable)
current=-1;
else return
}
this.setData({current});
this.setData({cur:current});
this.trigger({source:"touch"})
}
}
})

@ -1,5 +1,5 @@
<scroll-view wx:if="{{list.length>1}}" scroll-x="1" class="c-container" scroll-left="{{scrollLeft}}rpx" scroll-with-animation="1" style="width:{{width}}rpx;background:{{bg}}">
<view wx:for="{{list}}" wx:key="index" class="view common c-{{type}} {{current == index ?'active':''}}" data-current="{{index}}" bindtap="switchNav" style="margin:0 {{mg}}rpx;{{_itemWidth>0?'width:'+_itemWidth+'rpx':''}} ">
<text class="text common c-{{type}} {{current == index ?'active':''}}">{{item.text}}</text>
<scroll-view wx:if="{{list.length>1}}" scroll-x="1" class="navbar {{type}} bar-class" scroll-left="{{scrollLeft}}rpx" scroll-with-animation="1" style="width:{{width}}rpx;background:{{bg}}">
<view wx:for="{{list}}" wx:key="index" class="view common {{type}} item-class {{cur == index ?'active':''}}" data-current="{{index}}" bindtap="switchNav" style="margin-right:{{mg}}rpx;{{_itemWidth>0?'width:'+_itemWidth+'rpx':''}} ">
<text class="text common {{type}} item-class {{current == index ?'active':''}}">{{item.text}}</text>
</view>
</scroll-view>

@ -1,9 +1,10 @@
.c-container {
.navbar {
box-sizing: border-box;
line-height: 34px;
height: 34px;
font-size: 13px;
white-space: nowrap;
flex: none;
}
.view.common{
color: dimgray;
@ -12,46 +13,49 @@
height: 34px;
line-height: 34px;
}
.view.c-nav {
.view.nav {
box-sizing: border-box;
border-bottom: 2px solid transparent;
transition: all 0.26s ease;
}
.view.c-nav.active{
.view.nav.active{
color: #0080f0;
font-size: 15px;
border-bottom: 2px solid #0080f0;
}
.view.c-plain{
.view.plain{
transition: all 0.26s ease;
}
.view.c-plain.active{
.view.plain.active{
color: #0080f0;
font-size: 15px;
}
.view.c-cap{
border-radius: 36px;
background: #dbdbdb;
padding: 0 14px
.view.cap{
border-radius: 36px;
background: #dddddd;
padding: 0 14px;
height: 28px;
line-height: 28px;
margin: 3px 0;
}
.view.c-cap.active{
.view.cap.active{
background: #00a0f0;
color: white;
}
.view.c-line{
.view.line{
position: relative;
}
.text.c-line{
.text.line{
display: flex;
justify-content: center;
transition: all 0.26s ease;
}
.text.c-line.active{
.text.line.active{
font-size: 15px;
color: #0080f0;
}
.text.c-line::after{
.text.line::after{
transition: all 0.26s ease;
width: 0px;
height: 3.2px;
@ -60,7 +64,7 @@ padding: 0 14px
bottom: 0;
border-radius: 10px;
}
.text.c-line.active::after{
.text.line.active::after{
width: 22px;
background: #00a0f0;
}

@ -1,3 +1,3 @@
<navigator wx:if="{{user_id==2}}" class="nav" style="background:{{bg}}" url="/account/pages/login/login?type=login">
点击登,获取更多内容
点击登,获取更多内容
</navigator>

@ -0,0 +1,23 @@
const app = getApp();
Component({
properties: {
data:{
type:Object,
observer:function(data){
//name, image_url
this.setData({shixun:data});
}
},
origin:String
},
data: {
eduUrl:global.config.eduUrl
},
methods: {
onTap(){
app.navigateTo({url:"{shixun}?identifier="+this.data.shixun.identifier})
}
}
})

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

@ -0,0 +1,6 @@
<view class="shixun" bindtap="onTap">
<image class="shixun-img" mode="aspectFill" src="{{eduUrl}}/{{shixun.image_url}}"></image>
<view class="detail">
{{shixun.name}}
</view>
</view>

@ -0,0 +1,20 @@
.shixun{
background: white;
padding: 10px 8px;
overflow: hidden;
word-wrap: break-word;
display: flex;
}
.shixun-img{
flex:none;
width: 250rpx;
height: 170rpx;
border-radius: 4px;
}
.detail{
padding-top: 10px;
text-align: center;
flex: auto;
width: 1px;
}

@ -7,9 +7,6 @@ Component({
data: {
eduImgDir:global.config.eduImgDir
},
attached(){
console.log(this.data);
},
methods: {
}

@ -1,22 +1,10 @@
const cloudDir = "cloud://educoder.6564-educoder-1300855313/";
const eduUrl = "https://www.educoder.net";
/**
*
*/
module.exports = global.config = {
version:"0.11.0",
/**
* A 课程-普通作业模块
* A 页面不存在时进入主页
* A referrerInfo 分析
* A 主页课堂菜单操作
* A error-page组件错误处理界面
* U 加入课堂对话框功能升级
* U 课堂页面错误处理
* U 课程邀请界面改进增加已停用时的图标
* U 改变小程序码生成接口为getWXACodeUnlimited
* F 试卷题目富文本显示异常
* D 隐藏发现页入口
*
*/
version:"0.12.0",
apiRoot:eduUrl+"/api/",
cloudDir,
eduUrl,

@ -18,9 +18,16 @@ Component({
windowWidth:375,
eduImgDir: global.config.eduImgDir,
scenes:{
401:{
buttons:["点击登录","返回"]
},
409:{
message:"你不是该课堂的成员",
btnText:"加入课堂"
buttons:["加入课堂","返回"]
},
404:{
message:"该课堂已经被删除了",
buttons:["返回"]
}
}
},
@ -80,13 +87,21 @@ Component({
this.setData(data);
return data;
},
onTapError({detail:{status, target}}){
onTapError({detail:{status, target, current}}){
console.log(target);
switch(status){
case 401:
return app.navigateTo({url:"{login}"});
if(current==0)
return app.navigateTo({url:"{login}"});
else if(current==1)
return this.navigateBack();
case 409:
return this.setData({show_join_course: 1});
if(current==0)
return this.setData({show_join_course: 1});
else if(current==1)
return this.navigateBack();
case 404:
return this.navigateBack();
}
},
refresh({ refresh = 1 } = {}) {
@ -94,6 +109,14 @@ Component({
app.syncUser().then(res => {
this.pullCourse().then(res=>{
this.setData({status:200})
if(!this.flagModal){
wx.showToast({
title: '右滑更多模块哦',
image: "../../images/move-right.png",
duration: 2600
});
this.flagModal = 1;
}
})
.catch(e=>{
this.setData({status:e.code});
@ -130,11 +153,7 @@ Component({
this.refresh();
},
onReady:function(){
wx.showToast({
title: '右滑更多模块哦',
image:"../../images/move-right.png",
duration:2600
})
},
onPullDownRefresh(){
this.refresh();

@ -7,8 +7,8 @@ Page({
course:{
name:"**课堂邀请",
teacher_name:"点击最下方按钮",
teacher_school:"登后查看更多",
invite_code:"登后查看"
teacher_school:"登后查看更多",
invite_code:"登后查看"
},
imgDir:global.config.imgDir,
user:{},

@ -14,6 +14,6 @@
<button open-type="share" size="mini" plain="1">发送分享</button>
</view>
<button wx:if="{{user.user_id!=2}}" class="join-button" bindtap="joinCourse">加入课堂</button>
<button wx:else class="join-button" bindtap="login">点击登</button>
<button wx:else class="join-button" bindtap="login">点击登</button>
</view>
<join-course hidden="{{!show_join_modal}}" show_code="0" invite_code="{{course.invite_code}}" opentype="redirectTo"></join-course>

@ -36,7 +36,7 @@ Page({
console.info(res);
if(!res.course_id){
wx.showToast({
title: '创建失败,请先登',
title: '创建失败,请先登',
icon:"none"
})
return;

@ -1,7 +1,7 @@
@import "../question-common.wxss";
.main-input{
height: 120rpx;
height: 180rpx;
margin: 18rpx 0 4rpx 18rpx;
border: 1rpx solid lightgray;
border-radius: 12rpx;

@ -10,7 +10,7 @@ Component({
answer_null_question: function ({ detail: { value }, currentTarget: { dataset:{question_id, exercise_choice_id}} }) {
console.log("answer_main_question");
console.log(value);
console.log(dataset);
console.log(question_id, exercise_choice_id);
app.api("exercise_questions.exercise_answers")({ question_id, exercise_choice_id, answer_text: value })
.then(res => {
console.log("answer_main_question"); console.log(res);

@ -1,4 +1,4 @@
const app = getApp();
Component({
properties: {
@ -11,6 +11,8 @@ Component({
},
methods: {
enterShixun(){
app.navigateTo({url:"{shixun}?identifier="+this.data.data.shixun_identifier});
}
}
})

@ -1,5 +1,8 @@
<view class="question">
<view class="hint">第{{data.q_position}}题</view>
<rich-md c-class="title" nodes="{{data.question_title}}"/>
<text class="hint">对不起,暂时不支持实训题 ╥﹏╥ </text>
<view class="shixun-detail">
<button class="button-shixun" size="mini" type="main" plain bindtap="enterShixun">进入该实训</button>
<view>{{data.shixun_name}}</view>
</view>
</view>

@ -1 +1,14 @@
@import "../question-common.wxss";
@import "../question-common.wxss";
.shixun-detail{
display: flex;
align-items: center;
}
.button-shixun{
flex:none;
}
.shixun-name{
padding: 0 10px;
font-weight: bold;
flex: auto;
}

@ -1,5 +1,5 @@
var _config = require("../config");
console.log(_config);
//console.log(_config);
export const baseUrl = _config.apiRoot;
export const query = {randomcode:null, client_key:null};
export const config = {method:"POST"};
@ -88,18 +88,21 @@ main:{
myshixuns:{
challenges:{},
repository: {url:"{identifier}/*",query,form:{path:null},config},
update_file:{url:"{identifier}/*",query,form:{path:null, content: null, evaluate:null, game_id:null},config}
},
paths:{url:""},
schools:{
school_list:{query, form:{search:null}}
},
search:{query,form:{type:null, keyword:null, page:1, per_page:20}, data:{type:"subject, course, shixun, memo"}},
shixun_lists: { query, form:{type:"all", status:"published",keyword:"",diff:0,order:"desc",page:1,limit:10,sort:"wechat_myshixuns_count", no_jupyter:1}},
shixuns:{url:"" ,
challenges:{url:""},
shixuns:{url:"*/{identifier}", query, res:{tpm_modified:"代码库是否有更新"},
challenges:{url:"{identifier}/*", query},
show_right:{url:""},
shixun_exec:{url:""}
shixun_exec:{url:"{identifier}/*", query},
},
stages:{},
@ -110,22 +113,26 @@ student_works:{url:"*/{work_id}", query, form:{_:1, PUT:{description:null,attach
},
tasks:{url:"",
rep_content:{url:""},
tasks:{url:"*/{identifier}",query,
rep_content:{url:"{identifier}/*",query, form:{path:null, status:0, retry:0}},
sync_codes:{url:"{identifier}/*",query, res:{path:"newpath"}},
game_build:{url:"{identifier}/*",query,form:{first:1, resubmit:"", content_modified:null,sec_key:null}},
game_status:{url:"{identifier}/*",query,form:{port:-1, resubmit:"", time_out:false, sec_key:null}}
},
users:{
accounts: { url: "*/{login}", query, form:{school:1},
avatar:{url:"{login}/*",query, form:{image:null} ,config:{method:"PUT"}},
change_password: { url: "{login}/password", query, form:{old_password:null, password:null} ,config:{method:"PUT"}},
password: { url: "{login}/*", query, form:{old_password:null, password:null} ,config:{method:"PUT"}},
},
attendance: {query},
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"]},
get_user_info:{query,form:{school:1}},
homepage_info:{url:"{login}/*",query},
shixuns: { url: "{login}/*", query, form: {sort_by:"updated_at" ,page:1, sort_direction:"desc",per_page:16}},
system_update:{query:query},
tidings: {query},
tidings: {query, form:{type:"",page:1,per_page:10}, data:"type:course,project,interaction,apply,notice"},
unread_message_info:{url:"{login}/*", query},
home_page:{},
},

@ -34,12 +34,14 @@ export default class Client{
this.on("before", "weapps.verification_code",getSms);
var getLogin = () => ({ login: this.user.login })
this.on("before", "users.accounts", getLogin);
this.on("before", "users.accounts.password", getLogin);
this.on("before","users.courses", getLogin);
this.on("before","users.homepage_info", getLogin);
this.on("before","homepage_info", getLogin)
this.on("before","unread_message_info", getLogin);
this.on("before","users.accounts.avatar",getLogin);
this.on("before","users.unread_message_info", getLogin);
this.on("before","users.shixuns",getLogin);
this.on("success", "accounts.logout", res=>{
this.synch = 0;
wx.setStorageSync("autologin", 0);
@ -55,12 +57,23 @@ export default class Client{
this.on("success","accounts.login", res=>{
this.synch=0
this.save_cookies();
this.getTidingInfo();
});
this.on("success","first_stamp", res=>{
this.randomcode=res.message;
this.client_key = key(this.randomcode);
})
}
getTidingInfo(){
return this.api("users.unread_message_info")().then(res => {
if (res.unread_message_count)
wx.setTabBarBadge({ index: 1, text: res.unread_message_count.toString() });
else if (res.unread_tiding_count)
wx.showTabBarRedDot({ index: 1 })
}).catch(e => {
console.error("getTidingInfo", e);
});
}
async syncUser({ refresh = 0 } = {}) {
let old_id = this.user.user_id;
if(!this.synch||refresh){

@ -2,11 +2,11 @@ import apiConfig, {baseUrl} from "./apiConfig";
function handler({success, fail, resolve, reject}){
return {
success: res => {
if (res.data.status == 1) console.error("!!!api response!!! status===1");
if (res.data.status && res.data.status != 1) {
if (res.data.status <=100 ) console.error("!!!api response!!! status==="+res.data.status);
if (res.data.status && res.data.status > 100 || res.data.status<0) {
switch (res.data.status) {
case 401:
var message = "(●'◡'●)\n请先登哦";
var message = "(●'◡'●)\n请先登哦";
var code = res.data.status;
break;
default:
@ -61,7 +61,7 @@ export default function ({ name, data:_data = {}, session, success, fail, comple
data[key]=_data[key];
else{
let value = form[key];
if (value)
if (value!=null)
data[key] = value;
else if (typeof value == "object")
console.error(`${key} was not given in data`, _data);
@ -72,7 +72,7 @@ export default function ({ name, data:_data = {}, session, success, fail, comple
param[key] = _data[key];
else {
let value = query[key];
if (value)
if (value!=null)
param[key] = value;
else if (typeof value == "object")
console.error(`${key} was not given in data`, _data);

@ -21,9 +21,16 @@ export default class{
success: (res) => {
if (res.cookies && res.cookies.length)
this.processCookies(res.cookies);
if (res.header["Set-Cookie"])
else if (res.header["Set-Cookie"])
this.processCookies(res.header["Set-Cookie"]);
success(res);
//@todo: simplify;
if(res.header["set-Cookie"])
this.processCookies(res.header["set-Cookie"]);
else if (res.header["Set-cookie"])
this.processCookies(res.header["Set-cookie"]);
else if (res.header["set-cookie"])
this.processCookies(res.header["set-cookie"]);
},
fail: fail,
complete: complete

@ -0,0 +1,36 @@
const app = getApp();
Page({
data:{
statusBarHeight:20,
current:0,
show:[1],
list:[
{text:"课堂"},
{text:"实训"},
//{text:"课程"}
]
},
onLoad(){
wx.getSystemInfo({
success: res=>{
let {statusBarHeight} = res;
this.setData({statusBarHeight});
},
})
},
enterSearch(){
app.navigateTo({url:"{search}"});
},
switchNav({target:{dataset:{current}}}){
this.setData({current});
this.setData({['show['+current+']']:1})
},
switchTab({detail:{current, source, value}}){
console.log(current, source, value);
if(source=="touch"){
this.setData({ current });
this.setData({ ['show[' + current + ']']: 1 })
}
}
})

@ -0,0 +1,9 @@
{
"navigationStyle":"custom",
"usingComponents": {
"add-tips": "/components/add-tips/add-tips",
"my-course":"./my_course/my_course",
"my-shixun":"./my_shixun/my_shixun"
},
"navigationBarTextStyle": "white"
}

@ -0,0 +1,23 @@
<view class="navigation">
<view style="height:{{statusBarHeight}}px"></view>
<view class="navigation-bar">
<view class="navigation-left">
<icon class="search" type="search" size="20" color="white" bindtap="enterSearch"/>
<text class="title">我的</text>
</view>
<view class="navbar" bindtap="switchNav">
<view class="navitem {{current==index?'active':''}}" wx:for="{{list}}" data-current="{{index}}">
{{item.text}}
</view>
</view>
</view>
</view>
<swiper class="body" current="{{current}}" bindchange="switchTab" circular="1">
<swiper-item>
<my-course wx:if="{{show[0]}}"/>
</swiper-item>
<swiper-item>
<my-shixun wx:if="{{show[1]}}"/>
</swiper-item>
</swiper>
<add-tips/>

@ -0,0 +1,49 @@
page{
display: flex;
flex-direction: column;
height: 100%;
}
.navigation{
background: #00b0f0;
flex: none;
}
.navigation-bar{
height: 44px;
padding-right: 100px;
display: flex;
align-items: center;
}
.navigation-left{
width: 100px;
display: flex;
align-items: center;
color: white;
}
.search{
padding:0 12px;
}
.navbar{
flex: 1 1 1px;
width: 1px;
border-radius: 7px;
display: flex;
text-align: center;
white-space: nowrap;
overflow-x: scroll;
}
.navitem{
flex: auto;
background: #00d0f0;
color: white;
padding: 5px 1px;
transition: all ease 0.6s;
}
.navitem.active{
color: #00b0f0;
background: white;
}
.body{
flex: 1 1 1px;
height: 1px;
}

@ -0,0 +1,105 @@
const app = getApp();
/*
statuses:[]
*/
const getDataForRender = class_ => ({
name: class_.get('name'),
objectId: class_.get('objectId')
});
const categories = [{ text: "我学习的", value: "study" }, { text: "我管理的", value: "manage" }];
Component({
data: {
imgDir: global.config.imgDir,
categories,
statuses: [{ text: "正在进行", value: "processing" }, { text: "已结束", value: "end" }],
courses: [],
status: 0,
user: {},
current_cate: -1
},
attached() {
this.options = {page:1, limit:15};
this.onLoad();
},
pageLifetimes: {
show: function () {
app.syncUser().then(r => {
this.setData({ user: r.user })
});
if (this.data.current_cate >= 0) {
this.pullCourses({refresh:2});
}
}
},
methods: {
onCategoryChange: function ({ detail: { current, value } }) {
console.log("category change", current);
this.options["category"] = value.value;
this.pullCourses({refresh:2});
this.setData({ category: value.value });
},
onStatusChange: function ({ detail: { value } }) {
this.options["status"] = value.value;
this.pullCourses({refresh:2});
},
show_join_course_modal: function (event) {
this.setData({ show_join_course_modal: true });
},
enter_course: function (event) {
console.log(event);
let { id, course_name } = event.currentTarget.dataset;
wx.navigateTo({
url: "/course/pages/course/course?course_id=" + id + "&course_name=" + course_name,
})
},
pullCourses: function ({ refresh=0} = {}) {
if(refresh){
if(refresh==1){
this.options.page=1;
var {options:data}=this;
}else if(refresh==2){
var {limit, page, status, category}=this.options;
var data = {limit: page*limit, page:1, status, category};
}
}else{
this.options.page++;
var {options:data}=this;
}
return app.callApi({
name: "weapps.home", data,
complete: () => { this.setData({ loading: false }) }
})
.then(res => {
let { courses } = res;
if (data.status)
courses = courses.filter(i => {
return i.is_end == (status == "end")
});
console.log(courses);
if(!refresh)
courses = this.data.courses.concat(courses);
this.setData({ courses });
}).catch(e => {
this.setData({ courses: [] });
});
},
onLoad: function (options) {
console.log(app.user());
if (app.user().is_teacher)
var current_cate = 1;
else
var current_cate = 0;
this.setData({ current_cate });
},
onReachBottom(){
this.pullCourses();
},
onPullDownRefresh: function () {
//console.log("pulldownrefresh");
this.pullCourses({refresh:2});
},
}
})

@ -0,0 +1,9 @@
{
"component": true,
"usingComponents": {
"join-course-modal": "/components/modal/join-course/join-course",
"require-login": "/components/require-login/require-login",
"nav-bar": "/components/nav-bar/nav-bar",
"course-item": "/components/course-item/course-item"
}
}

@ -0,0 +1,19 @@
<view class="my-course">
<require-login user_id="{{user.user_id}}"/>
<nav-bar list="{{categories}}" current="{{current_cate}}" bindchange="onCategoryChange"/>
<view class="nav-wrap">
<nav-bar list="{{statuses}}" width="300" itemWidth="140" cancellable="1" current="-1" type="plain" bg='' bindchange="onStatusChange"/>
</view>
<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">
<image class="none-content" src="{{imgDir}}blank1.png" mode="aspectFit"></image>
<text class="none-content hint">空空如也!</text>
</view>
<view wx:for="{{courses}}" wx:key="id" class="course-wrap">
<course-item data="{{item}}" category="{{category}}"/>
</view>
</scroll-view>
<!-- {{imgDir}}add.png -->
<image class="add-course" src="{{imgDir}}add.png" bindtap="show_join_course_modal"></image>
<join-course-modal hidden="{{!show_join_course_modal}}"/>
</view>

@ -1,4 +1,4 @@
page{
.my-course{
height: 100%;
display: flex;
flex-direction: column;
@ -16,7 +16,7 @@ page{
}
.course-wrap{
margin-bottom: 10px;
margin: 0 12px 10px 12px;
}
.add-course{
position: fixed;
@ -29,7 +29,9 @@ page{
}
.none-content{
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
text.none-content{
font-size: 16px;
@ -41,6 +43,6 @@ image.none-content{
width: 220rpx;
}
.add-class:hover{
.add-course:hover{
opacity: 0.6;
}

@ -0,0 +1,41 @@
const app = getApp();
Component({
properties: {
},
data: {
shixuns:[]
},
attached(){
this.options = {page:0, per_page:16};
this.pullShixuns();
//console.log(this.options)
},
methods: {
async pullShixuns({refresh=0}={}){
if(refresh){
if(refresh==1){
this.options.page = 1;
var { options } = this;
}else if(refresh==2){
var {page, per_page} = this.options;
var options = {page:1, per_page: page*per_page};
}
}else{
this.options.page++;
var {options} = this;
}let {shixuns} = await app.api("users.shixuns")(options);
if(!refresh)
shixuns = this.data.shixuns.concat(shixuns);
this.setData({shixuns});
return shixuns;
},
onPullDownRefresh(){
this.pullShixuns({refresh:2});
},
onReachBottom(){
this.pullShixuns({refresh:0});
}
}
})

@ -0,0 +1,7 @@
{
"component": true,
"usingComponents": {
"nav-bar": "/components/nav-bar/nav-bar",
"shixun-item":"/components/shixun-item/shixun-item"
}
}

@ -0,0 +1,7 @@
<view class="my-shixun">
<scroll-view class="body" scroll-y="!" refresher-enabled="1" lower-threshold="120" bindrefresherrefresh="onPullDownRefresh" bindscrolltolower="onReachBottom">
<view class="shixun-wrap" wx:for="{{shixuns}}" wx:key="id">
<shixun-item data="{{item}}"/>
</view>
</scroll-view>
</view>

@ -0,0 +1,14 @@
.my-shixun{
height: 100%;
display: flex;
flex-direction: column;
}
.shixun-wrap{
margin: 2px 0;
}
.body{
flex:1 1 1px;
height: 1px;
}

@ -1,91 +0,0 @@
const app = getApp();
/*
statuses:[]
*/
const getDataForRender = class_ => ({
name: class_.get('name'),
objectId: class_.get('objectId')
});
const categories= [{ text: "我学习的", value: "study" }, { text: "我管理的", value: "manage" }];
Page({
options:{},
data: {
imgDir:global.config.imgDir,
categories,
statuses: [{ text:"正在进行", value:"processing"},{text:"已结束",value:"end"}],
courses: [],
status:0,
user:{},
current_cate:-1
},
presences: [],
classes: [],
onCategoryChange: function({detail:{current,value}}){
this.options["category"] = value.value;
if(current>=0)
this.pull_courses(this.options);
this.setData({ category: value.value });
},
onStatusChange: function ({detail: {value} }){
this.options["status"] = value.value;
this.pull_courses(this.options);
},
show_join_course_modal: function (event) {
this.setData({ show_join_course_modal: true });
},
enter_course: function(event){
console.log(event);
let {id, course_name} = event.currentTarget.dataset;
wx.navigateTo({
url: "/course/pages/course/course?course_id="+id+"&course_name="+course_name,
})
},
pull_courses: function({category="",status=""}={}){
app.callApi({name:"weapps.home",data:{
category: category,
status: status},
complete:()=>{this.setData({loading:false})}
})
.then(res=>{
//console.log(res);
let {courses} = res;
if(status)
courses = courses.filter(i=>{
return i.is_end==(status=="end")
})
this.setData({courses});
}).catch(e=>{
this.setData({courses:[]});
console.error
});
//console.log(this.data);
},
onLoad: function (options) {
console.log(app.user());
if(app.user().is_teacher)
var current_cate = 1;
else
var current_cate = 0;
this.setData({current_cate});
},
onShow: function () {
app.syncUser().then(r => {
this.setData({ user: r.user })
});
if(this.data.current_cate>=0){
this.pull_courses(this.options);
}
},
onPullDownRefresh: function () {
this.pull_courses(this.options);
},
onShareAppMessage: function () {
return app.shareApp();
}
})

@ -1,11 +0,0 @@
{
"navigationBarTitleText": "我的课程",
"usingComponents": {
"add-tips":"/components/add-tips/add-tips",
"join-course-modal":"/components/modal/join-course/join-course",
"require-login":"/components/require-login/require-login",
"nav-bar":"/components/nav-bar/nav-bar",
"course-item":"/components/course-item/course-item"
},
"enablePullDownRefresh": true
}

@ -1,21 +0,0 @@
<require-login user_id="{{user.user_id}}"/>
<nav-bar list="{{categories}}" current="{{current_cate}}" bindchange="onCategoryChange"/>
<view class="nav-wrap">
<nav-bar list="{{statuses}}" width="300" itemWidth="140" cancellable="1" current="-1" type="plain" bg='' bindchange="onStatusChange"/>
</view>
<scroll-view scroll-y="1" refresher-enabled="1" bindrefresherrefresh="onPullDownRefresh" class="body">
<view class="container" bindrefresh="onPullDownRefresh">
<view hidden="{{courses.length!=0 || loading}}" class="none-content">
<image class="none-content" src="{{imgDir}}blank1.png" mode="aspectFit"></image>
<text class="none-content hint">空空如也!</text>
</view>
<view wx:for="{{courses}}" wx:key="id" class="course-wrap">
<course-item data="{{item}}" category="{{category}}"/>
</view>
</view>
</scroll-view>
<!-- {{imgDir}}add.png -->
<image class="add-course" src="{{imgDir}}add.png" bindtap="show_join_course_modal"></image>
<add-tips/>
<join-course-modal hidden="{{!show_join_course_modal}}"/>

@ -1,5 +1,5 @@
{
"navigationBarTitleText": "我的",
"navigationBarTitleText": "个人中心",
"enablePullDownRefresh": false,
"usingComponents": {
"join-course-modal": "/components/modal/join-course/join-course"

@ -6,6 +6,7 @@
<text class="user-text" bindtap="enter_usersetting">{{user.real_name}} | {{user.user_school}}</text>
</view>
<view class="nav-list">
<navigator class="nav" url="/account/pages/change_password/change_password">修改密码</navigator>
<view class="nav addclass" bindtap="show_join_course_modal">加入课程</view>
<navigator url="/course/pages/course_setting/course_setting?intent=create" class="nav createclass">创建课程</navigator>
<button open-type="feedback" class="nav">小程序反馈</button>
@ -14,8 +15,8 @@
<navigator url="/account/pages/about/about" class="nav about">关于</navigator>
</view>
<view>
<button class="login" type="primary" wx:if="{{user.user_id==2}}" bindtap="enter_login">登陆</button>
<button class="logout" wx:else bindtap="logout">退出登</button>
<button class="login" type="main" wx:if="{{user.user_id==2}}" bindtap="enter_login">登录</button>
<button class="logout" wx:else bindtap="logout">退出登</button>
</view>
</view>
<join-course-modal hidden="{{!show_join_course_modal}}"/>

@ -1,5 +1,3 @@
.nav {
font-size: 16px;
background: white;

@ -0,0 +1,22 @@
const app = getApp();
Component({
properties: {
data:Object
},
data: {
},
methods: {
enterPage(){
let {type} = this.data.data;
switch(type){
case 'shixun':
return app.navigateTo({ url: "{shixun}?identifier=" + this.data.data.identifier});
case "course":
return app.navigateTo({url:"{course}?course_id="+this.data.data.id});
}
}
}
})

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"rich-md":"/components/rich-md/rich-md"
}
}

@ -0,0 +1,14 @@
<view class="search-item" bindtap="enterPage">
<view class="title-wrap">
<rich-text class="title" nodes="{{data.title}}"></rich-text>
</view>
<view wx:for="{{data.content}}" class="content-wrap">
<rich-text class="content" nodes="{{item[0]}}"></rich-text>
</view>
<view class="detail">
<text>{{data.author_name}}</text>
<text>{{data.author_school_name}}</text>
<text wx:if="{{data.type=='course'}}">成员数:{{data.members_count}}</text>
<text wx:if="{{data.type=='shixun'}}">学习人数:{{data.study_count}}</text>
</view>
</view>

@ -0,0 +1,26 @@
.highlight{
color: #00a0f0;
}
.search-item{
background: white;
padding: 10px;
border-radius: 6px;
}
.title-wrap{
font-weight: bolder;
font-size: 20px;
padding-bottom: 4px;
}
.content-wrap{
margin: 4px 0;
}
.detail{
padding: 8px 0 4px 0;
display: flex;
font-size: 13px;
color: dimgray;
}
.detail>text{
margin-right: 12px;
}

@ -0,0 +1,68 @@
const app = getApp();
Page({
data: {
type_text:"实训项目",
list:[
{text:"实训项目", type:"shixun"},
{text:"教学课堂", type:"course"}
]
},
onTypeChange({detail:{current, value}}){
this.options.type = value.type;
this.setData({type_text: value.text});
this.search({refresh:1});
},
onSubmit({detail:{value}}){
this.options.keyword = value.keyword;
this.search({refresh:1});
},
onConfirm({detail:{value}}){
this.options.keyword = value;
this.search({refresh:1});
},
search({refresh=0}={}){
if(refresh==1)
this.setData({loading:1});
if(refresh){
if(refresh==1){
this.options.page = 1;
var {options} = this;
}else if(refresh==2){
var {page,per_page,type, keyword} = this.options;
var options = {page:1, per_page:page*per_page, type, keyword};
}
}else{
this.options.page++;
var {options} = this;
}
return app.api("search")(options)
.then(res=>{
let {results,count} = res;
if(refresh==1&&results.length==0)
wx.showToast({
title: '没有找到相关内容',icon:"none"
});
if(!refresh)
results = this.data.results.concat(results);
this.setData({count, results, loading:0});
})
.catch(e=>{
console.error(e);
this.setData({loading:0});
})
},
onLoad: function (options) {
this.options = {page:1, per_page:20, type:"shixun"};
this.search({refresh:1});
},
_onReachBottom: function () {
this.search({refresh:0});
},
onShareAppMessage: function () {
}
})

@ -0,0 +1,9 @@
{
"usingComponents": {
"nav-bar":"/components/nav-bar/nav-bar",
"search-item":"./search-item/search-item"
},
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#0080f0",
"navigationBarTitleText": "搜索"
}

@ -0,0 +1,20 @@
<page-meta>
<navigation-bar title="搜索{{type_text}}" loading="{{loading}}"/>
</page-meta>
<form class="header" bindsubmit="onSubmit">
<view class="search">
<input name="keyword" auto-focus="1" bindconfirm="onConfirm" confirm-type="search"/>
<button form-type="submit" type="main" size="mini">
<view class="button-inner">
<icon type="search" color="white" size="17"/>
搜索
</view>
</button>
</view>
</form>
<nav-bar list="{{list}}" bindchange="onTypeChange"/>
<scroll-view class="body" scroll-y="1" lower-threshold="160" bindscrolltolower="_onReachBottom">
<view wx:for="{{results}}" class="search-item-wrap">
<search-item data="{{item}}"/>
</view>
</scroll-view>

@ -0,0 +1,43 @@
page{
display: flex;
flex-direction: column;
height: 100%;
}
.body{
flex: 1 1 1px;
height: 1px;
}
.header{
background: #0080f0;
}
.search{
display: flex;
align-items: center;
background: #0080f0;
margin: 6px 20px;
border-radius: 6px;
overflow: hidden;
}
.search>input{
flex: auto;
padding: 2px 8px;
background: white;
}
.search>button{
flex: none;
border:none;
}
.button-inner{
display: flex;
align-items: center;
}
.button-inner>icon{
padding-right: 6px;
}
.search-item-wrap{
margin: 6px 8px;
}

@ -1,24 +1,52 @@
const app = getApp();
Page({
data: {
tidings:[]
tidings:[],
list:[
{text:"课堂提醒",type:"course"},{text:"项目提醒",type:"project"},{text:"互动提醒", type:"interaction"},{text:"审核",type:"apply"},{text:"通知", type:"notice"}
]
},
changeType({detail:{current, value}}){
this.options.type=value.type;
this.refresh({refresh:2});
},
refresh({refresh=0}={}){
if(refresh){
if(refresh==1){
this.options.page=1;
var { options } = this;
}else if(refresh==2){
var { per_page, page, type} = this.options;
var options = {per_page:per_page*page, page:1, type};
}
}else{
this.options.page++;
var {options} = this;
}
console.log(options);
return app.api("users.tidings")(options).then(res => {
let {tidings} = res;
if(!refresh)
tidings = this.data.tidings.concat(tidings);
this.setData({ tidings });
})
},
onLoad: function (options) {
this.options = { page: 1, per_page: 10 };
console.log(this.options);
},
_onReachBottom(){
this.refresh();
},
onShow: function () {
app.api("users.tidings")().then(res=>{
console.log(res);
this.setData({tidings: res.tidings});
})
this.refresh({refresh:2});
wx.hideTabBarRedDot({index: 1});
wx.removeTabBarBadge({index: 1});
},
onPullDownRefresh: function () {
this.refresh({refresh:2});
},
onShareAppMessage: function () {

@ -1,6 +1,8 @@
{
"usingComponents": {
"tiding-item":"/components/tiding-item/tiding-item"
"tiding-item":"/components/tiding-item/tiding-item",
"nav-bar":"/components/nav-bar/nav-bar",
"error-page":"/components/error-page/error-page"
},
"navigationBarTitleText": "消息中心"
}

@ -1,3 +1,7 @@
<view class="tiding-wrap" wx:for="{{tidings}}" wx:key="id">
<tiding-item data="{{item}}"/>
</view>
<nav-bar bar-class="navbar" current="-1" list="{{list}}" cancellable="1" itemWidth="0" type="cap" bg="transparent" mg="8" bindchange="changeType"/>
<scroll-view class="tidings" refresher-enabled="1" scroll-y="1" bindrefresherrefresh="onPullDownRefresh" bindscrolltolower="_onReachBottom" lower-threshold="120">
<view class="tiding-wrap" wx:for="{{tidings}}" wx:key="id">
<tiding-item data="{{item}}"/>
</view>
</scroll-view>

@ -1,3 +1,16 @@
.navbar{
margin-top: 4px;
flex: none;
}
.tiding-wrap{
margin: 4px 0;
margin-top: 6px;
}
page{
display: flex;
flex-direction: column;
height: 100%;
}
.tidings{
flex: 1 1 1px;
height: 1px;
}

@ -1,23 +1,14 @@
// shixun/components/challenge-item/challenge-item.js
Component({
/**
* 组件的属性列表
*/
properties: {
data:Object
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
}
})

@ -1,2 +1,13 @@
<!--shixun/components/challenge-item/challenge-item.wxml-->
<text>shixun/components/challenge-item/challenge-item.wxml</text>
<view class="challenge">
<view class="header">
<text class="position">第{{data.position}}关</text>
</view>
<view class="name">
<text>{{data.name}}</text>
</view>
<view class="detail">
<text>{{data.playing_count}}人正在挑战</text>
<text>{{data.passed_count}}人完成挑战</text>
<text></text>
</view>
</view>

@ -1 +1,18 @@
/* shixun/components/challenge-item/challenge-item.wxss */
.challenge{
padding: 12px 14px;
border-bottom: 1px solid lightgrey;
}
.position{
font-size: 12px;
color: dimgray;
}
.detail{
padding-top: 6px;
font-size: 13px;
color: dimgrey
}
.detail>text{
margin-right: 12px;
}

@ -1,19 +1,61 @@
const app = getApp();
//status:[];
Page({
data: {
data:{
shixun:{},
challenges:[],
list:[
{text:"简介"},{text:"任务"}
],
description:""
},
log(e){
console.log(e)
},
enterChallenge(){
/*let {id, identifier} = this.data.shixun;
wx.navigateToMiniProgram({
appId: 'wx2402d86a6b534f77',
path: `/pages/shiyan/shixun?shixunDetailsid=${id}&shixunidentifier=${identifier}`});
return;*/
wx.showLoading({
title: '开启中',
});
app.api("shixuns.shixun_exec")({ identifier:this.data.identifier,complete:res=>{wx.hideLoading()}})
.then(res=>{
app.navigateTo({ url: "{task}?identifier=" + res.game_identifier});
});
},
scrollTo({scrollTop}){
//console.log(scrollTop);
wx.pageScrollTo({scrollTop,duration:200})
},
switchNav({detail:{current,source}}){
if(source=="touch")
this.setData({current});
},
apiChallenges: app.api("shixuns.challenges"),
apiShixun:app.api("shixuns"),
async pullChallenges(){
let {description, challenge_list:challenges} = await this.apiChallenges({identifier:this.data.identifier});
this.setData({description, challenges});
},
async pullShixun(){
let {identifier} = this.data;
let shixun = await this.apiShixun({identifier});
this.setData({shixun});
},
onLoad: function (options) {
let {identifier} = options;
this.setData({identifier});
this.pullChallenges();
this.pullShixun();
},
onShow: function () {
app.syncUser().then(res=>{
this.setData({user:res.user});
})
},
onPullDownRefresh: function () {
},

@ -1,3 +1,11 @@
{
"usingComponents": {}
"usingComponents": {
"rich-md":"/components/rich-md/rich-md",
"require-login":"/components/require-login/require-login",
"challenge-item":"/shixun/components/challenge-item/challenge-item",
"nav-bar":"/components/nav-bar/nav-bar"
},
"navigationBarBackgroundColor": "#0080f0",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "实训详情"
}

@ -1,2 +1,48 @@
<!--miniprogram/shixun/pages/shixun/shixun.wxml-->
<text>miniprogram/shixun/pages/shixun/shixun.wxml</text>
<page-meta>
<navigation-bar title="{{shixun.name}}"/>
</page-meta>
<wxs src="./shixun.wxs" module="handler"/>
<require-login bg="#0080f0" user_id="{{user.user_id}}"/>
<view class="header">
<view class="shixun-title">{{shixun.name}}</view>
<view class="shixun-detail">
<view>
<view class="detail-key">学习人数</view>
<view class="detail-value">{{shixun.stu_num}}</view>
</view>
<view>
<view class="detail-key">难度级别</view>
<view class="detail-value">{{shixun.diffcult}}</view>
</view>
<view>
<view class="detail-key">经验值</view>
<view class="detail-value">{{shixun.experience}}</view>
</view>
</view>
</view>
<nav-bar list="{{list}}" current="{{current}}" type="line" bindchange="switchNav"/>
<swiper class="body" current="{{current}}" bindchange="switchNav" circular="1">
<swiper-item>
<scroll-view class="scroll-body" scroll-y="{{1}}" bindscroll="{{handler.scroll}}">
<rich-md my-class="desp" nodes="{{description}}" type="markdown"/>
</scroll-view>
</swiper-item>
<swiper-item>
<scroll-view class="scroll-body" scroll-y="1" bindscroll="{{handler.scroll}}">
<view class="cate">
<view class="cate-header">
<text class="square"/>
<text class="cate-name">全部任务</text>
</view>
<view class="challenges">
<view class="challenge-wrap" wx:for="{{challenges}}" wx:key="challenge_id">
<challenge-item data="{{item}}"/>
</view>
</view>
</view>
</scroll-view>
</swiper-item>
</swiper>
<view class="operations">
<button bindtap="enterChallenge" type="main">{{shixun.task_operation[0]}}</button>
</view>

@ -0,0 +1,15 @@
var top = 126;
function scroll(e, ins){
var scrollTop = e.detail.scrollTop;
var deltaY = e.detail.deltaY;
//console.log(scrollTop);
//console.log(show,scrollTop<122);
console.log(top,scrollTop, deltaY);
if(deltaY<0){
ins.callMethod("scrollTo", { scrollTop: top });
}
}
module.exports={
scroll: scroll
}

@ -1 +1,77 @@
/* miniprogram/shixun/pages/shixun/shixun.wxss */
.header{
text-align: center;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
height: 120px;
background: white;
margin-bottom: 3px;
}
.shixun-title{
background: #0080f0;
font-weight: bolder;
padding-top: 6px;
font-size: 20px;
color: white;
height: 80px;
width: 100%;
}
.shixun-detail{
position: absolute;
top: 40px;
height: 70px;
background:white;
box-shadow: 3px 3px 10px #e7e7e7;
display: flex;
border-radius: 6px;
width: 600rpx;
}
.shixun-detail>view{
flex:auto;
display: flex;
flex-direction: column;
}
.shixun-detail>view>view{
flex: auto;
display: flex;
justify-content: center;
flex-direction: column;
}
.detail-key{
color: dimgrey;
font-size: 13px;
}
.cate{
margin-top: 3px;
background: white;
overflow: hidden;
}
.cate-header{
padding: 8px 8px 0px 8px;
}
.square{
width: 0;
height: 0px;
border-radius: 10px;
border-left: 5px solid #00b0f0;
}
.cate-name{
padding: 0 10px;
}
.body{
height: calc(100vh - 80px);
margin-top: 2px;
margin-bottom: 46px;
}
.scroll-body{
height: 100%;
background: white;
}
.operations{
position: fixed;
bottom: 0;
left:0;
right:0;
}

@ -0,0 +1,43 @@
Component({
properties: {
data:{
type:Object,
observer:function(){
this.analyse();
}
},
index: Number
},
data: {
hidden:true,
scrollTop1:0,
scrollTop2:0
},
methods: {
analyse(){
//console.log(this.data);
var outputs = this.data.data.output.split(/\n/).map(i=>({text:i}));
var actual_outputs = this.data.data.actual_output.split(/\n/).map(i=>({text:i}));
var lines = Math.min(outputs.length, actual_outputs.length);
for(var i=0;i<lines;i++){
if(outputs[i].text!=actual_outputs[i].text){
actual_outputs[i].class = outputs[i].class = "different";
}
}
this.setData({outputs, actual_outputs});
//console.log(outputs);
},
scroll1({scrollTop1}){
this.setData({scrollTop1});
},
scroll2({scrollTop2}){
this.setData({scrollTop2});
},
onTapHeader(){
var {hidden} = this.data;
hidden = !this.data.data.is_public||!hidden;
this.setData({hidden});
}
}
})

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

@ -0,0 +1,30 @@
<wxs module="handler" src="./test_set.wxs"/>
<view class="test-set">
<view class="header" bindtap="onTapHeader">
<view class="triangle {{!hidden?'rotate':''}}"></view>
<text>测试集 {{index+1}}</text>
<text class="warn" wx:if="{{!data.is_public}}">(隐藏)</text>
<text class="error" wx:if="{{!data.result}}">未通过</text>
</view>
<view class="detail {{hidden?'hidden':''}}">
<view class="input">
<view>测试输入: <text class="tip">{{data.input}}</text></view>
</view>
<view class="output">
<view class="subtitle">预期输出</view>
<scroll-view class="output-info" scroll-y="1" bindscroll="{{handler.scroll1}}" scroll-top="{{scrollTop1}}">
<view bindtouchstart="{{handler.touch1}}">
<view wx:for="{{outputs}}" class="output-line {{item.class}}">{{item.text}}</view>
</view>
</scroll-view>
</view>
<view class="actual-output">
<view class="subtitle">实际输出</view>
<scroll-view class="output-info" scroll-y="1" bindscroll="{{handler.scroll2}}" scroll-top="{{scrollTop2}}">
<view bindtouchstart="{{handler.touch2}}">
<view wx:for="{{actual_outputs}}" class="output-line {{item.class}}">{{item.text}}</view>
</view>
</scroll-view>
</view>
</view>
</view>

@ -0,0 +1,32 @@
var touch = 0;
function scroll1(e,ins){
if(touch==1){
var scrollTop = e.detail.scrollTop;
ins.callMethod("scroll2", { scrollTop2: scrollTop});
//console.log("scroll2");
}
}
function scroll2(e, ins) {
if(touch==2){
var scrollTop = e.detail.scrollTop;
ins.callMethod("scroll1", { scrollTop1: scrollTop });
//console.log("scroll1");
}
}
function touch1(e,ins){
//console.log("touch1");
touch = 1;
}
function touch2(e, ins) {
//console.log("touch2");
touch = 2;
}
module.exports = {
scroll1: scroll1,
scroll2: scroll2,
touch1:touch1,
touch2:touch2
}

@ -0,0 +1,64 @@
.test-set{
color: lightgray;
background: #1f2f3b;
border-radius: 3.6px;
padding: 8px;
}
.header{
display: flex;
align-items: center;
}
.header>text{
margin-right: 10px;
}
.triangle{
height: 0px;
width: 0px;
border-top: solid 4px transparent;
border-bottom: solid 4px transparent;
border-left: solid 5px white;
transition: 1s ease all;
margin: 2px 6px 2px 4px;
}
.rotate{
transform: rotate(90deg);
}
.input{
padding: 2px 0px;
color: #cecece;
font-size: 15px;
}
.error{
color: #FF4736
}
.warn{
color: #ff6800;
}
.tip{
color: #00b0f0;
}
.subtitle{
text-align: center;
padding: 2.6px;
font-size: 14px;
}
.detail{
transition: 0.8s all ease;
}
.output-info{
background: #111c24;
max-height: 106px;
margin: 4px 0;
border-radius: 2px;
}
.output-line{
white-space: pre;
padding: 0 8px;
}
.hidden{
height: 0px;
overflow: hidden;
}
.different{
background: #9a6868;
}

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

Loading…
Cancel
Save