Merge branches 'dev_aliyun' and 'dev_ysm' of https://bdgit.educoder.net/Hjqreturn/educoder into dev_ysm

issues25489
杨树明 5 years ago
commit 1104f3cb2c

@ -290,6 +290,7 @@ class GamesController < ApplicationController
answer_deduction = challenge.challenge_answers.where("level <= #{@answer.level}").sum(:score)
@game.update_attributes!(:answer_open => answer_open, :answer_deduction => answer_deduction)
end
GameAnswer.create!(challenge_answer_id: @answer.id, user_id: current_user.id, game_id: @game.id, view_time: Time.now)
rescue Exception => e
uid_logger_error("#######金币扣除异常: #{e.message}")
@ -317,6 +318,7 @@ class GamesController < ApplicationController
@game.update_attributes!(:answer_open => 1, :answer_deduction => 100)
end
@challenge_chooses = @challenge.challenge_chooses
GameAnswer.create!(user_id: current_user.id, game_id: @game.id, view_time: Time.now)
rescue Exception => e
uid_logger_error("#######金币扣除异常: #{e.message}")
raise ActiveRecord::Rollback

@ -5,7 +5,7 @@ class SubjectsController < ApplicationController
before_action :find_subject, except: [:index, :create, :new, :append_to_stage]
before_action :allowed, only: [:update, :edit, :destroy, :publish, :cancel_publish, :cancel_has_publish,
:search_members, :add_subject_members, :statistics, :shixun_report, :school_report,
:up_member_position, :down_member_position]
:up_member_position, :down_member_position, :update_team_title]
include ApplicationHelper
include SubjectsHelper
@ -264,6 +264,12 @@ class SubjectsController < ApplicationController
end
end
def update_team_title
tip_exception("team_title参数不能为空") if params[:team_title].blank?
@subject.update_attributes!(team_title: params[:team_title])
normal_status("更新成功")
end
def search_members
tip_exception("搜索内容不能为空") unless params[:search]
page = params[:page] || 1

@ -11,4 +11,8 @@ module MyshixunsHelper
nil
end
end
def view_answer_time game, user_id
game.game_answers.where(user_id: user_id).last&.view_time
end
end

@ -1,4 +1,9 @@
class ChallengeAnswer < ApplicationRecord
default_scope { order("challenge_answers.level asc") }
belongs_to :challenge
has_many :game_answers, :dependent => :destroy
def view_answer_time(user_id)
game_answers.where(user_id: user_id).last&.view_time
end
end

@ -15,7 +15,7 @@ class Game < ApplicationRecord
belongs_to :user
belongs_to :challenge
has_one :run_code_message, :dependent => :destroy
has_many :game_answers, :dependent => :destroy
#全部关卡数
scope :ch_games, lambda { |challenge_id| where(challenge_id:challenge_id) }

@ -0,0 +1,5 @@
class GameAnswer < ApplicationRecord
belongs_to :user
belongs_to :game
belongs_to :challenge_answer
end

@ -249,6 +249,7 @@
autoRefresh: true,
smartIndent: true,//智能换行
styleActiveLine: true,
lineWrapping: true,
lint: true,
readOnly: "nocursor"
});

@ -16,7 +16,7 @@
<td class="action-container">
<% if prize_user.leader? && prize_user.competition_prize.category == 'bonus' %>
<% bank_content = [prize_user.extra&.[]('bank'), prize_user.extra&.[]('second_bank'), prize_user.extra&.[]('card_no')].compact.join('<br>').presence || '无' %>
<%= javascript_void_link('查看银行账户', data: { toggle: 'tooltip', title: bank_content.html_safe, html: true, placement: 'left' }) %>
<%= javascript_void_link('查看银行账户', data: { toggle: 'tooltip', title: bank_content.html_safe, html: true, placement: 'left', trigger: 'click' }) %>
<% end %>
<% if prize_user.pending? %>

@ -5,6 +5,7 @@ if @leader
json.bank_account_editable @bank_account_editable
end
json.all_certified current_user.all_certified?
json.personal_certifications do
json.array! @self_prizes do |prize_user|
json.url personal_competition_certificate_path(current_competition.identifier, prize_user)

@ -7,6 +7,7 @@ json.message do
json.answer_id answer.id
json.answer_name answer.name
json.answer_score answer.score
json.view_time answer.view_answer_time(current_user.id)
# 高层级不给答案
if @power || @game.answer_open >= index
json.answer_contents answer.contents

@ -6,4 +6,6 @@ json.array! @games do |game|
json.identifier get_game_identifier(@shixun.task_pass, game, @identity)
json.get_gold game.user_get_gold_and_experience(@shixun.status, challenge)[0]
json.get_experience game.user_get_gold_and_experience(@shixun.status, challenge)[1]
json.view_answer_time view_answer_time(game, current_user.id)
json.finished_time game.end_time
end

@ -5,6 +5,8 @@ json.members @members do |member|
json.role member.role
end
json.team_title @subject.team_title
# 技能标签
json.tags @tags do |tag|
unless tag.blank?

@ -1077,6 +1077,7 @@ Rails.application.routes.draw do
post :cancel_homepage_show
post :excellent
post :cancel_excellent
post :update_team_title
end
end
end

@ -0,0 +1,13 @@
class CreateGameAnswers < ActiveRecord::Migration[5.2]
def change
create_table :game_answers do |t|
t.references :challenge_answer
t.references :user
t.references :game
t.datetime :view_time
t.timestamps
end
end
end

@ -0,0 +1,5 @@
class AddIndexForGameAnswers < ActiveRecord::Migration[5.2]
def change
add_index :game_answers, [:challenge_answer_id, :user_id], :unique => true
end
end

@ -0,0 +1,5 @@
class AddTeamTitleToSubjects < ActiveRecord::Migration[5.2]
def change
add_column :subjects, :team_title, :string, default: "教学团队"
end
end

@ -10,8 +10,9 @@
out+=this.renderer.em(this.output(cap[2]||cap[1])) -->
out+=this.renderer.em(this.output(cap[2]||cap[1]), cap.input)
*/
// 0.4.0 /^ *(#{1,6}) ——》/^ *(#{1,6}) 去掉了一个空格 TODO 行内公式带_
// 0.4.0 /^ *(#{1,6}) ——》/^ *(#{1,6}) 去掉了一个空格
/*if("string"!=typeof e)throw new Error("marked(): input parameter is of type "+Object.prototype.toString.call(e)+", string expected");*/
// 说明:左边 --> 右边 左边被替换成了右边的内容
// b(i[1].replace(/^ *| *\| *$/g,"")) --> i[1].replace(/^ *| *\| *$/g, "").split(/ *\| */) table没识别的问题
// header.length===a.align.length --> header.length table没识别的问题
// 2个table b(a.cells[p],a.header.length) -> a.cells[p].replace(/^ *\| *| *\| *$/g, "").split(/ *\| */)

@ -1 +1,17 @@
需要合并的js确保每个js的结尾都有个;号,不然合并后会报脚本错误。
js_min_all含的js
es6-shim
jQuery v1.8.3
Underscore.js 1.8.2
marked v0.3.3
Raphaël 2.1.3
js sequence diagrams 1.0.4
flowchart, v1.3.4
jQuery.flowchart.js v1.1.0
https://github.com/pandao/editor.md
http://codemirror.net
diff_match_patch
codemirror merge.js
从 edu_tpi.js 挪过来的js

@ -1503,7 +1503,7 @@ class PersonModal extends Component {
color: "#D0021B",
fontSize: "12px",
marginTop: "9px"
}}>战队导师为{GetenrollmentAPI.teacher_staff.minimum}-{GetenrollmentAPI.teacher_staff.maximum},{Thecurrentnumber}</p>
}}>导师{GetenrollmentAPI.teacher_staff.minimum}-{GetenrollmentAPI.teacher_staff.maximum},{Thecurrentnumber}</p>
: ""
) :
@ -1518,7 +1518,7 @@ class PersonModal extends Component {
color: "#D0021B",
fontSize: "12px",
marginTop: "9px"
}}>战队队员{GetenrollmentAPI.member_staff.minimum}-{GetenrollmentAPI.member_staff.maximum},{Thecurrentnumberstu}</p>
}}>战队成员需{GetenrollmentAPI.member_staff.minimum}-{GetenrollmentAPI.member_staff.maximum},{Thecurrentnumberstu}</p>
: ""
) :

@ -140,7 +140,10 @@ class CompetitionContentspdfdownload extends Component{
</Row>
<Row className={"mt30"}>
<Col>个人证书{data&&data.personal_certifications.length===0?<span><span className={"pdfpicture font-14"}>暂未生成</span> <span className={"ml20"}><span className={"pdfpicture font-14"}></span><a className={"pdfdownloadfont4CACFF"} onClick={()=>this.props.Competitioncallback("2")}></a></span></span>:
<Col>个人证书{data&&data.personal_certifications.length===0&&data&&data.all_certified===false?
<span><span className={"pdfpicture font-14"}>暂未生成</span> <span className={"ml20"}><span className={"pdfpicture font-14"}></span><a className={"pdfdownloadfont4CACFF"} onClick={()=>this.props.Competitioncallback("2")}></a></span></span>:
data&&data.personal_certifications.length===0&&data&&data.all_certified===true?
<span><span className={"pdfpicture font-14"}>暂未生成</span> <span className={"ml20"}><span className={"pdfpicture font-14"}></span></span></span>:
data&&data.personal_certifications.map((item,key)=>{
return(
<span className={"mr10"} key={key}>

@ -160,44 +160,18 @@ class Statistics extends Component{
derivefun=(url)=>{
let{group_ids}=this.state;
let data={
group_id:group_ids
}
axios.get(url,{params:
data,
paramsSerializer: function(params) {
return qs.stringify(params, {arrayFormat: 'brackets'})
}
}).then((response)=>{
if(response === undefined){
return
}
if(response.data.status&&response.data.status===-1){
this.props.showNotification(response.data.message);
}else if(response.data.status&&response.data.status===-2){
// if(response.data.message === "100"){
// // 已超出文件导出的上限数量100 ),建议:
//
// this.setState({
// DownloadType:true,
// DownloadMessageval:100
// })
// }else {
// //因附件资料超过500M
// this.setState({
// DownloadType:true,
// DownloadMessageval:500
// })
// }
this.props.showNotification(response.data.message);
}else {
// this.props.showNotification(`正在下载中`);
// window.open("/api"+url, '_blank');
this.props.slowDownload(url);
}
}).catch((error) => {
console.log(error)
});
let list=group_ids;
let urllist="";
if(list!=undefined&&list.length!=0)
list.map((item,key)=>{
if(key===0){
urllist=`group_id[]=${item}`
}else{
urllist=urllist+`&group_id[]=${item}`
}
})
this.props.slowDownload(`${url}?${urllist}`);
}

@ -20,3 +20,38 @@ TPIContextProvider
代码内容通过以下接口获取:
`/tasks/${game.identifier}/rep_content.json?path=${path}&status=${status}&retry=${isRetry ? 1 : 0}`
层次结构
TPIContextProvider
page/Index
Header
TaskListContainer
TaskList 左侧划出的任务列表
MainContentContainer
MainContent
LeftViewContainer 左侧区域
LeftView
ChooseAnswerView 选择题答案
CommentContainer
Comments 评论列表
CodeEvaluateMultiLevelAnswerUnlock 多级别解锁
// 看设置是vnc or 代码 or 选择题
CodeRepositoryViewContainer for VNC
VNCContainer
CodeEvaluateView 代码评测结果
VNCDisplay vnc显示
ActionView 评测按钮区
|| CodeRepositoryViewContainer 代码显示区
WebSSHTimer ssh倒计时
TPIMonaco monaco编辑器
|| ChooseRepositoryView 选择题显示区
// 看设置,是代码 or 选择题
CodeEvaluateView
|| ChooseEvaluateView
ActionView 评测按钮区
没用的文件
LeftNav.js
AnswerListContainer.js left 他人解答

@ -132,6 +132,7 @@ function create_editorMD(id, width, high, placeholder, imageUrl, callback, initV
// Or return editormd.toolbarModes[name]; // full, simple, mini
// Using "||" set icons align right.
const icons = ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear"];
// 试卷处用到的填空题新增按钮
if (__that.props.showNullButton) {
icons.push('nullBtton')
}
@ -228,12 +229,14 @@ export default class TPMMDEditor extends Component {
const imageUrl = `/api/attachments.json`;
// 创建editorMd
let react_id = `react_${_id}`;
// 将实例存到了window
window[react_id] = this
const answers_editormd = create_editorMD(_id, '100%', this.props.height, _placeholder, imageUrl, (_editorName) => {
const __editorName = _editorName;
react_id = `react_${__editorName.id}`;
const that = window[react_id]
// 一个延迟的recreate或resize不加这段代码md初始化可能会出现样式问题
setTimeout(() => {
if (that.props.needRecreate == true) {
__editorName.recreate() // 注意 必须在setValue之前触发不然会清空
@ -268,6 +271,7 @@ export default class TPMMDEditor extends Component {
that.props.onCMBeforeChange(cm,change)
})
that.answers_editormd = __editorName;
// 这里应该可以去掉了,方便调试加的
window[__editorName.id+'_'] = __editorName;
}, initValue, this.onEditorChange,this.props.watch, {
noStorage: this.props.noStorage,
@ -276,6 +280,7 @@ export default class TPMMDEditor extends Component {
}, this);
}
// 用在form里时validate失败时出现一个红色边框
showError = () => {
this.setState({showError: true})
}

@ -28,6 +28,8 @@ const Option = Select.Option;
const RadioGroup = Radio.Group;
const { TextArea } = Input;
function create_editorMD(id, width, high, placeholder, imageUrl, callback) {
var editorName = window.editormd(id, {
width: width,
@ -1155,19 +1157,22 @@ export default class TPMevaluation extends Component {
</a>
</Tooltip>
</p>
<textarea className="textareavalue mb15" name="test_set[input][]"
<TextArea className="textareavalue mb15" name="test_set[input][]"
placeholder="输入"
value={item.input}
id={"textareavalue"+key}
autoHeight="true"
autoSize={{ minRows: 3, maxRows: 5 }}
onInput={(e)=>this.evaluationoninputvalue(e,key,"sr")}
></textarea>
<textarea className="textareavalue" name="test_set[output][]"
></TextArea>
<TextArea className="textareavalue" name="test_set[output][]"
placeholder="预期输出"
value={item.output}
id={key+"textareavalue"}
autoHeight="true"
autoSize={{ minRows: 3, maxRows: 5 }}
onInput={(e)=>this.evaluationoninputvalue(e,key,"yq")}
></textarea>
></TextArea>
<div className="clearfix lineh-30">
<span className="fl mr10 color-grey-6">匹配规则</span>
<RadioGroup className="fl" value={item.match_rule} onChange={(e)=>this.changeEvaluationRule(e,key)}>

@ -255,8 +255,8 @@ class Infos extends Component{
isRenders && <Trialapplication {...this.props} {...this.state} Cancel={() => this.cancelModulationModels()}/>
}
<InfosBanner
{...this.props}
{...this.state}
{...this.props}
{...this.state}
{..._commonProps}
signFor={this.signFor}
followPerson={this.followPerson}
@ -274,8 +274,8 @@ class Infos extends Component{
{/* 课堂 */}
{/* http://localhost:3007/courses/1309/homework/9300/setting */}
<Route exact path="/users/:username/courses"
render={
<Route exact path="/users/:username/courses"
render={
(props) => (<InfosCourse {...this.props} {...props} {...this.state} {..._commonProps}/>)
}
></Route>
@ -287,7 +287,7 @@ class Infos extends Component{
}
></Route>
{/* 实训课程 */}
{/* 实践课程 */}
<Route exact path="/users/:username/paths"
render={
(props) => (<InfosPath {...this.props} {...props} {...this.state} {..._commonProps}/>)
@ -317,14 +317,13 @@ class Infos extends Component{
></Route>
<Route exact path="/users/:username"
render={
<Route exact path="/users/:username"
render={
(props) => (<InfosCourse {...this.props} {...props} {...this.state} {..._commonProps}/>)
}
></Route>
</Switch>
</Switch>
</div>
)
}

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { SnackbarHOC } from 'educoder';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import {Tooltip,Menu,Pagination,Spin} from 'antd';
import {Tooltip, Menu, Pagination, Spin, Dropdown} from 'antd';
import Loadable from 'react-loadable';
import Loading from '../../../Loading';
import axios from 'axios';
@ -20,7 +20,8 @@ class InfosCourse extends Component{
status:undefined,
page:1,
per_page:16,
sort_by: "updated_at",
sort_direction: "desc",
totalCount:undefined,
data:undefined,
isSpin:false
@ -31,27 +32,35 @@ class InfosCourse extends Component{
this.setState({
isSpin:true
})
let{category,status,page}=this.state;
this.getCourses(category,status,page);
let {category, status, page, sort_by, sort_direction} = this.state;
this.getCourses(category, status, page, sort_by, sort_direction);
}
getCourses=(category,status,page)=>{
getCourses = (category, status, page, sort_by, sort_direction) => {
let url=`/users/${this.props.match.params.username}/courses.json`;
axios.get((url),{params:{
category,
status,
page,
sort_by,
sort_direction,
per_page: this.props.is_current && category && page ==1?17:16
}}).then((result)=>{
if(result){
console.log("请求成功");
this.setState({
totalCount:result.data.count,
data:result.data,
sort_direction: sort_direction,
sort_by: sort_by,
isSpin:false
})
}
}).catch((error)=>{
console.log(error);
this.setState({
isSpin: false
})
})
}
@ -92,6 +101,27 @@ class InfosCourse extends Component{
}
}
updatedlist(sort_by) {
//按照什么样子排序
this.setState({
isSpin: true
});
let {category, status, page, sort_direction} = this.state;
this.getCourses(category, status, 1, sort_by, "desc");
}
updatedlists(sort_direction, i) {
// console.log("updatedlistssort_direction");
// console.log(sort_direction);
// console.log(i);
//是否是倒序
this.setState({
isSpin: true
});
let {category, status, page, sort_by} = this.state;
this.getCourses(category, status, page, sort_by, sort_direction);
}
render(){
let{
category,
@ -99,11 +129,25 @@ class InfosCourse extends Component{
page,
data,
totalCount,
isSpin
isSpin,
sort_by,
sort_direction
} = this.state;
let is_current=this.props.is_current;
// console.log(this.props.current_user&&this.props.current_user.user_identity==="学生")
const menu = (
<Menu>
<Menu.Item onClick={() => this.updatedlist("updated_at")}>
最新动态
</Menu.Item>
<Menu.Item onClick={() => this.updatedlist("created_at")}>
最新创建
</Menu.Item>
</Menu>
);
return(
<div className="educontent">
<Spin size="large" spinning={isSpin}>
@ -120,10 +164,66 @@ class InfosCourse extends Component{
<li className={status=="end" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeStatus("end")}>已结束</a></li>
</div>
}
<p className="pl25 pr25 clearfix font-12 mb20 mt20">
<p className="pl25 pr25 clearfix font-12 " style={{
lineHeight: "41px",
}}>
<span className="fl color-grey-9">共参与{totalCount}{category?category=="manage"?"发布":"学习":"课堂"}</span>
<span className="fr color-grey-9">时间最新</span>
<sapn className="relativef fr"
style={{
display: "flex",
flexDirection: "column",
height: "40px",
lineHeight: "40px",
}}
>
<span
style={{
flexDirection: "column",
textAlign: "center",
height: "10px",
lineHeight: "10px",
display: "table",
marginTop: "9px",
}}
>
<i className={sort_direction === "asc" ?
"iconfont icon-sanjiaoxing-up font-12 color-blue h10 " : "iconfont icon-sanjiaoxing-up font-12 h10"}
onClick={() => this.updatedlists("asc", 1)}></i>
</span>
<span
style={{
flexDirection: "column",
height: "10px",
lineHeight: "10px",
textAlign: "center",
display: "table",
}}
>
<i className={sort_direction === "desc" ?
"iconfont icon-sanjiaoxing-down font-12 yslbottomsj color-blue h10" : "iconfont icon-sanjiaoxing-down font-12 yslbottomsj h10"}
onClick={() => this.updatedlists("desc", 2)}></i>
</span>
</sapn>
<Dropdown overlay={menu}>
<span className="fr color-grey-9 mr10 pointer " style={{
display: "flex",
flexDirection: "initial",
}}>
<span>{sort_by === "updated_at" ? '最新动态' : sort_by === "created_at" ? '最新创建' : ""}</span>
</span>
</Dropdown>
</p>
<style>
{
`
.square-list{width: 100%;box-sizing: border-box;margin-top:10px}
`
}
</style>
<div className="square-list clearfix">
{/* 289 */}
{

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { SnackbarHOC } from 'educoder';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import {Tooltip,Menu,Pagination, Spin} from 'antd';
import {Tooltip, Menu, Pagination, Spin, Dropdown} from 'antd';
import Loadable from 'react-loadable';
import Loading from '../../../Loading';
import NoneData from '../../courses/coursesPublic/NoneData'
@ -19,11 +19,11 @@ class InfosPath extends Component{
this.state={
category:undefined,
page:1,
sort_by:'time',
status:undefined,
per_page:16,
isSpin:false,
sort_by: "updated_at",
sort_direction: "desc",
totalCount:undefined,
data:undefined
}
@ -33,28 +33,34 @@ class InfosPath extends Component{
this.setState({
isSpin:true
})
let{category,status,sort_by,page,per_page}=this.state;
this.getCourses(category,status,sort_by,page,per_page);
let {category, status, sort_by, page, per_page, sort_direction} = this.state;
this.getCourses(category, status, sort_by, page, sort_direction);
}
getCourses=(category,status,sort_by,page,per_page)=>{
getCourses = (category, status, sort_by, page, sort_direction) => {
let url=`/users/${this.props.match.params.username}/subjects.json`;
axios.get((url),{params:{
category,
status,
sort_by,
page,
sort_direction,
per_page:this.props.is_current && category && page ==1?17:16
}}).then((result)=>{
if(result){
this.setState({
totalCount:result.data.count,
data:result.data,
sort_by: sort_by,
sort_direction: sort_direction,
isSpin:false
})
}
}).catch((error)=>{
console.log(error);
this.setState({
isSpin: false
})
})
}
@ -66,18 +72,18 @@ class InfosPath extends Component{
page:1,
isSpin:true
})
let{sort_by}=this.state;
this.getCourses(cate,undefined,sort_by,1);
let {sort_by, sort_direction} = this.state;
this.getCourses(cate, undefined, sort_by, 1, sort_direction);
}
// 切换状态
changeStatus=(status)=>{
let{category,sort_by}=this.state;
let {category, sort_by, sort_direction} = this.state;
this.setState({
status,
page:1,
isSpin:true
})
this.getCourses(category,status,sort_by,1);
this.getCourses(category, status, sort_by, 1, sort_direction);
}
//切换页数
changePage=(page)=>{
@ -85,8 +91,8 @@ class InfosPath extends Component{
page,
isSpin:true
})
let{category,sort_by,status}=this.state;
this.getCourses(category,status,sort_by,page);
let {category, sort_by, status, sort_direction} = this.state;
this.getCourses(category, status, sort_by, page, sort_direction);
}
// 进入课堂
@ -101,15 +107,38 @@ class InfosPath extends Component{
sort_by:sort,
isSpin:true
})
let{category,status,page}=this.state;
this.getCourses(category,status,sort,page);
let {category, status, page, sort_direction} = this.state;
this.getCourses(category, status, sort, page, sort_direction);
}
//切换种类
updatedlist(sort_by) {
//按照什么样子排序
this.setState({
isSpin: true
});
let {category, status, page, sort_direction} = this.state;
this.getCourses(category, status, sort_by, 1, "desc");
}
//排序
updatedlists(sort_direction) {
//是否是倒序
this.setState({
isSpin: true
});
let {category, status, page, sort_by} = this.state;
this.getCourses(category, status, sort_by, page, sort_direction);
}
render(){
let{
category,
status,
sort_by,
sort_direction,
page,
data,
totalCount,
@ -117,6 +146,17 @@ class InfosPath extends Component{
} = this.state;
let isStudent = this.props.isStudent();
let is_current=this.props.is_current;
const menu = (
<Menu>
<Menu.Item onClick={() => this.updatedlist("updated_at")}>
最近更新
</Menu.Item>
<Menu.Item onClick={() => this.updatedlist("created_at")}>
最新创建
</Menu.Item>
</Menu>
);
return(
<div className="educontent">
<Spin size="large" spinning={isSpin}>
@ -142,10 +182,65 @@ class InfosPath extends Component{
<li className={status=="finished" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeStatus("finished")}>已完成</a></li>
</div>
}
<div className="pl25 pr25 clearfix font-12 mb20 mt20">
<div className="pl25 pr25 clearfix font-12 " style={{
lineHeight: "41px",
}}>
<span className="fl color-grey-9">共参与{totalCount}{category?category=="manage"?"发布":"学习":"实践课程"}</span>
<span className="fr color-grey-9">时间最新</span>
<sapn className="relativef fr"
style={{
display: "flex",
flexDirection: "column",
height: "40px",
lineHeight: "40px",
}}
>
<span
style={{
flexDirection: "column",
textAlign: "center",
height: "10px",
lineHeight: "10px",
display: "table",
marginTop: "9px",
}}
>
<i className={sort_direction === "asc" ?
"iconfont icon-sanjiaoxing-up font-12 color-blue h10 " : "iconfont icon-sanjiaoxing-up font-12 h10"}
onClick={() => this.updatedlists("asc")}></i>
</span>
<span
style={{
flexDirection: "column",
height: "10px",
lineHeight: "10px",
textAlign: "center",
display: "table",
}}
>
<i className={sort_direction === "desc" ?
"iconfont icon-sanjiaoxing-down font-12 yslbottomsj color-blue h10" : "iconfont icon-sanjiaoxing-down font-12 yslbottomsj h10"}
onClick={() => this.updatedlists("desc")}></i>
</span>
</sapn>
<Dropdown overlay={menu}>
<span className="fr color-grey-9 mr10 pointer " style={{
display: "flex",
flexDirection: "initial",
}}>
<span>{sort_by === "updated_at" ? '最近更新' : sort_by === "created_at" ? '最新创建' : ""}</span>
</span>
</Dropdown>
</div>
<style>
{
`
.square-list{width: 100%;box-sizing: border-box;margin-top:10px}
`
}
</style>
<div className="square-list clearfix">
{/* 295 */}
{

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { SnackbarHOC } from 'educoder';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import {Tooltip,Pagination,Spin} from 'antd';
import {Tooltip, Pagination, Spin, Dropdown, Menu} from 'antd';
import axios from 'axios';
import NoneData from '../../courses/coursesPublic/NoneData'
import {getImageUrl} from 'educoder';
@ -16,7 +16,8 @@ class InfosProject extends Component{
status:undefined,
page:1,
per_page:16,
sort_by: "updated_on",
sort_direction: "desc",
totalCount:undefined,
data:undefined,
isSpin:false
@ -27,16 +28,18 @@ class InfosProject extends Component{
this.setState({
isSpin:true
})
let{category,status,page}=this.state;
this.getCourses(category,status,page);
let {category, status, page, sort_by, sort_direction} = this.state;
this.getCourses(category, status, page, sort_by, sort_direction);
}
getCourses=(category,status,page)=>{
getCourses = (category, status, page, sort_by, sort_direction) => {
let url=`/users/${this.props.match.params.username}/projects.json`;
axios.get((url),{params:{
category,
status,
page,
sort_by,
sort_direction,
per_page:this.props.is_current && category && page ==1?17:16
}}).then((result)=>{
if(result){
@ -49,6 +52,8 @@ class InfosProject extends Component{
this.setState({
totalCount:result.data.count,
data:result.data,
sort_by: sort_by,
sort_direction: sort_direction,
isSpin:false
})
}
@ -56,6 +61,9 @@ class InfosProject extends Component{
}
}).catch((error)=>{
console.log(error);
this.setState({
isSpin: false
})
})
}
@ -66,8 +74,9 @@ class InfosProject extends Component{
page:1,
isSpin:true
})
let{status}=this.state;
this.getCourses(cate,status,1);
let {status, sort_by, sort_direction} = this.state;
this.getCourses(cate, status, 1, sort_by,
sort_direction);
}
//切换状态
changeStatus=(status)=>{
@ -76,8 +85,12 @@ class InfosProject extends Component{
page:1,
isSpin:true
})
let{category}=this.state;
this.getCourses(category,status,1);
let {
category, sort_by,
sort_direction
} = this.state;
this.getCourses(category, status, 1, sort_by,
sort_direction);
}
//切换页数
changePage=(page)=>{
@ -85,8 +98,12 @@ class InfosProject extends Component{
page,
isSpin:true
})
let{category,status}=this.state;
this.getCourses(category,status,page);
let {
category, status, sort_by,
sort_direction
} = this.state;
this.getCourses(category, status, page, sort_by,
sort_direction);
}
// 进入项目
@ -96,6 +113,27 @@ class InfosProject extends Component{
}
}
//切换种类
updatedlist(sort_by) {
//按照什么样子排序
this.setState({
isSpin: true
});
let {category, status, page, sort_direction} = this.state;
this.getCourses(category, status, 1, sort_by, "desc");
}
//排序
updatedlists(sort_direction) {
//是否是倒序
this.setState({
isSpin: true
});
let {category, status, page, sort_by} = this.state;
this.getCourses(category, status, page, sort_by, sort_direction);
}
render(){
let{
category,
@ -103,12 +141,24 @@ class InfosProject extends Component{
page,
data,
totalCount,
isSpin
isSpin,
sort_by,
sort_direction
} = this.state;
let isStudent = this.props.isStudent();
let is_current=this.props.is_current;
console.log(data)
const menu = (
<Menu>
<Menu.Item onClick={() => this.updatedlist("updated_on")}>
最新动态
</Menu.Item>
<Menu.Item onClick={() => this.updatedlist("created_on")}>
最新创建
</Menu.Item>
</Menu>
);
return(
<div className="educontent">
<Spin size="large" spinning={isSpin}>
@ -125,10 +175,65 @@ class InfosProject extends Component{
<li className={status=="personal" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeStatus("personal")}>私有</a></li>
</div>
}
<p className="pl25 pr25 clearfix font-12 mb20 mt20">
<p className="pl25 pr25 clearfix font-12 " style={{
lineHeight: "41px",
}}>
<span className="fl color-grey-9">共参与{totalCount}{category?category=="manage"?"发布":"学习":"项目"}</span>
<span className="fr color-grey-9">时间最新</span>
<sapn className="relativef fr"
style={{
display: "flex",
flexDirection: "column",
height: "40px",
lineHeight: "40px",
}}
>
<span
style={{
flexDirection: "column",
textAlign: "center",
height: "10px",
lineHeight: "10px",
display: "table",
marginTop: "9px",
}}
>
<i className={sort_direction === "asc" ?
"iconfont icon-sanjiaoxing-up font-12 color-blue h10 " : "iconfont icon-sanjiaoxing-up font-12 h10"}
onClick={() => this.updatedlists("asc")}></i>
</span>
<span
style={{
flexDirection: "column",
height: "10px",
lineHeight: "10px",
textAlign: "center",
display: "table",
}}
>
<i className={sort_direction === "desc" ?
"iconfont icon-sanjiaoxing-down font-12 yslbottomsj color-blue h10" : "iconfont icon-sanjiaoxing-down font-12 yslbottomsj h10"}
onClick={() => this.updatedlists("desc")}></i>
</span>
</sapn>
<Dropdown overlay={menu}>
<span className="fr color-grey-9 mr10 pointer " style={{
display: "flex",
flexDirection: "initial",
}}>
<span>{sort_by === "updated_on" ? '最近更新' : sort_by === "created_on" ? '最新创建' : ""}</span>
</span>
</Dropdown>
</p>
<style>
{
`
.square-list{width: 100%;box-sizing: border-box;margin-top:10px}
`
}
</style>
<div className="square-list clearfix">
{/* 289 */}
{
@ -174,17 +279,17 @@ class InfosProject extends Component{
<div className="edu-txt-center course-bottom">
<div className="inline color-grey-6">
{
item.members_count> 0 &&
item.members_count > 0 &&
<span className="mr10 ml10 fl squareIconSpan">
<i className="iconfont icon-chengyuan fl mr3" data-tip-down="成员"></i>{item.members_count}</span>
}
{
item.issues_count> 0 &&
item.issues_count > 0 &&
<span className="mr10 ml10 fl squareIconSpan">
<i className="iconfont icon-issue fl mr3" data-tip-down="issues数"></i>{item.issues_count}</span>
}
{
item.changesets_count> 0 &&
item.changesets_count > 0 &&
<span className="mr10 ml10 fl squareIconSpan">
<i className="iconfont icon-banbenku fl mr3" data-tip-down="版本库"></i>{item.changesets_count}</span>
}

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { Pagination , Spin } from 'antd';
import {Pagination, Spin, Dropdown, Menu} from 'antd';
import NoneData from '../../courses/coursesPublic/NoneData'
import axios from 'axios';
@ -16,11 +16,11 @@ class InfosShixun extends Component{
this.state={
category:undefined,
page:1,
sort_by:'time',
status:undefined,
per_page:16,
isSpin:false,
sort_by: "updated_at",
sort_direction: "desc",
totalCount:undefined,
data:undefined
}
@ -30,11 +30,15 @@ class InfosShixun extends Component{
this.setState({
isSpin:true
})
let{category,status,sort_by,page}=this.state;
this.getCourses(category,status,sort_by,page);
let {category, status, sort_by, page, sort_direction} = this.state;
this.getCourses(category, status, sort_by, page, sort_direction);
}
getCourses=(category,status,sort_by,page)=>{
getCourses = (category, status, sort_by, page, sort_direction) => {
// console.log("请求数据成功");
// console.log("getCourses");
// console.log(sort_by);
// console.log(sort_direction);
let url=`/users/${this.props.match.params.username}/shixuns.json`;
axios.get((url),{params:{
category,
@ -44,14 +48,21 @@ class InfosShixun extends Component{
per_page:this.props.is_current && category && page ==1?17:16
}}).then((result)=>{
if(result){
console.log("请求数据成功");
console.log(sort_by);
this.setState({
totalCount:result.data.count,
data:result.data,
sort_by: sort_by,
sort_direction: sort_direction,
isSpin:false
})
}
}).catch((error)=>{
console.log(error);
this.setState({
isSpin: false
})
})
}
@ -63,8 +74,8 @@ class InfosShixun extends Component{
page:1,
isSpin:true
})
let{sort_by}=this.state;
this.getCourses(cate,undefined,sort_by,1);
let {sort_by, sort_direction} = this.state;
this.getCourses(cate, undefined, sort_by, 1, sort_direction);
}
// 切换状态
changeStatus=(status)=>{
@ -73,8 +84,8 @@ class InfosShixun extends Component{
page:1,
isSpin:true
})
let{category,sort_by}=this.state;
this.getCourses(category,status,sort_by,1);
let {category, sort_by, sort_direction} = this.state;
this.getCourses(category, status, sort_by, 1, sort_direction);
}
//切换页数
changePage=(page)=>{
@ -82,8 +93,8 @@ class InfosShixun extends Component{
page,
isSpin:true
})
let{category,sort_by,status}=this.state;
this.getCourses(category,status,sort_by,page);
let {category, sort_by, status, sort_direction} = this.state;
this.getCourses(category, status, sort_by, page, sort_direction);
}
// 进入课堂
@ -98,8 +109,28 @@ class InfosShixun extends Component{
sort_by:sort,
isSpin:true
})
let{category,status,page}=this.state;
this.getCourses(category,status,sort,page);
let {category, status, page, sort_direction} = this.state;
this.getCourses(category, status, sort, page, sort_direction);
}
//切换种类
updatedlist(sort_by) {
//按照什么样子排序
this.setState({
isSpin: true
});
let {category, status, page, sort_direction} = this.state;
this.getCourses(category, status, sort_by, 1, "desc");
}
//排序
updatedlists(sort_direction) {
//是否是倒序
this.setState({
isSpin: true
});
let {category, status, page, sort_by} = this.state;
this.getCourses(category, status, sort_by, page, sort_direction);
}
render(){
@ -110,10 +141,22 @@ class InfosShixun extends Component{
page,
data,
totalCount,
sort_direction,
isSpin
} = this.state;
let is_current=this.props.is_current;
const menu = (
<Menu>
<Menu.Item onClick={() => this.updatedlist("updated_at")}>
最新动态
</Menu.Item>
<Menu.Item onClick={() => this.updatedlist("created_at")}>
语言类别
</Menu.Item>
</Menu>
);
return(
<div className="educontent">
<Spin size="large" spinning={isSpin}>
@ -140,17 +183,57 @@ class InfosShixun extends Component{
<li className={status=="passed" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeStatus("passed")}>已通关</a></li>
</div>
}
<div className="pl25 pr25 clearfix font-12 mb20 mt20">
<div className="pl25 pr25 clearfix font-12 " style={{
lineHeight: "41px",
}}>
<span className="fl color-grey-9">共参与{totalCount}{category?category=="manage"?"发布":"学习":"实训"}</span>
<div className="fr">
<li className="drop_down">
<span className="color-grey-9 font-12">{sort_by=="time"?"时间最新":"语言类别"}</span><i className="iconfont icon-xiajiantou font-12 ml2 color-grey-6"></i>
<ul className="drop_down_normal">
<li onClick={()=>this.changeOrder("time")}>时间最新</li>
<li onClick={()=>this.changeOrder("language")}>语言类别</li>
</ul>
</li>
</div>
<sapn className="relativef fr"
style={{
display: "flex",
flexDirection: "column",
height: "40px",
lineHeight: "40px",
}}
>
<span
style={{
flexDirection: "column",
textAlign: "center",
height: "10px",
lineHeight: "10px",
display: "table",
marginTop: "9px",
}}
>
<i className={sort_direction === "asc" ?
"iconfont icon-sanjiaoxing-up font-12 color-blue h10 " : "iconfont icon-sanjiaoxing-up font-12 h10"}
onClick={() => this.updatedlists("asc")}></i>
</span>
<span
style={{
flexDirection: "column",
height: "10px",
lineHeight: "10px",
textAlign: "center",
display: "table",
}}
>
<i className={sort_direction === "desc" ?
"iconfont icon-sanjiaoxing-down font-12 yslbottomsj color-blue h10" : "iconfont icon-sanjiaoxing-down font-12 yslbottomsj h10"}
onClick={() => this.updatedlists("desc")}></i>
</span>
</sapn>
<Dropdown overlay={menu}>
<span className="fr color-grey-9 mr10 pointer " style={{
display: "flex",
flexDirection: "initial",
}}>
<span>{sort_by === "updated_at" ? '最新动态' : sort_by === "created_at" ? '语言类别' : ""}</span>
</span>
</Dropdown>
</div>
<style>
{
@ -160,6 +243,7 @@ class InfosShixun extends Component{
left: 10px;
bottom: 100px;
}
.square-list{width: 100%;box-sizing: border-box;margin-top:10px}
`
}
</style>

@ -162,7 +162,7 @@
float:left;
}
.userpost label{
display:block;
display: block;
height: 18px;
line-height:18px;
}
@ -435,3 +435,7 @@
.educontent .square-list {
min-height: 400px;
}
.h10 {
height: 10px;
}

@ -1,6 +1,6 @@
import React, { useState, useEffect, useContext, useRef, memo } from 'react';
import {Link} from 'react-router-dom';
import { Pagination, Input, Button } from 'antd'
import {Pagination, Input, Button} from 'antd'
import { getUrl2, isDev, ThemeContext, ActionBtn, NoneData } from 'educoder'
import axios from 'axios'
import VideoInReviewItem from './VideoInReviewItem'
@ -80,13 +80,13 @@ function InfoVideo (props) {
const fetchUrl = `/users/${username}/videos.json`
const sorts = sortKey.split('-')
setLoading(true)
axios.get(fetchUrl, {
axios.get(fetchUrl, {
params: {
page: pageObj.current,
per_page: PAGE_SIZE,
sort_by: sorts[0],
sort_direction: sorts[1],
//
//
}
})
.then((response) => {
@ -96,14 +96,14 @@ function InfoVideo (props) {
setCount(response.data.count)
}
}).catch(() => {
setLoading(false)
})
}
function fetchReviewvideos() {
const fetchUrl = `/users/${username}/videos/review.json`
const sorts = sortKey.split('-')
setLoading(true)
axios.get(fetchUrl, {
axios.get(fetchUrl, {
params: {
page: pageObj.current,
per_page: PAGE_SIZE,
@ -158,9 +158,9 @@ function InfoVideo (props) {
_clipboard = new ClipboardJS('.copybtn');
_clipboard.on('success', (e) => {
showNotification('复制成功')
});
});
}
}, 200)
}, 200)
}
}, [videoModalObj.visible])
@ -173,7 +173,7 @@ function InfoVideo (props) {
}
function onEditVideo(item) {
videoId = {
videoId = {
videoId: item.id,
title: item.title
}
@ -182,7 +182,7 @@ function InfoVideo (props) {
// this.refs['editVideoModal'].setVisible(true, video);
}
function onMaskClick(item) {
videoId = {
videoId = {
videoId: item.id,
title: item.title,
file_url: item.file_url,
@ -201,7 +201,10 @@ function InfoVideo (props) {
function getCopyText (file_url, cover_url) {
return `<video src="${file_url}" controls="true" controlslist="nodownload" width="${DEFAULT_VIDEO_WIDTH_IN_MD}" height="${DEFAULT_VIDEO_HEIGHT_IN_MD}" poster="${cover_url}">您的浏览器不支持 video 标签。</video>`
}
const _inputValue = getCopyText(videoId.file_url, videoId.cover_url)
const _inputValue = getCopyText(videoId.file_url, videoId.cover_url);
const sorts = sortKey.split('-')
return (
<div className="educontent infoVideo">
<EditVideoModal {...props} {...editModalObj}
@ -214,7 +217,7 @@ function InfoVideo (props) {
className="showVideoModal"
width={800 - 1}
>
<video
<video
autoplay="true"
ref={videoEl}
src={videoId.file_url} controls="true" controlslist="nodownload">
@ -275,13 +278,15 @@ function InfoVideo (props) {
onClick={() => { toUpload() }}
className="toUploadBtn"
>
>
上传视频
</Button>
}
></InfoTab>
<div className="toolbarRow mt20 df">
<div className="toolbarRow df" style={{
lineHeight: "40px",
}}>
<span>
<span style={{color: theme.foreground_orange1}}> {count} </span>
@ -297,21 +302,58 @@ function InfoVideo (props) {
{categoryObj.category == 'all' &&<div className="fr">
<li className="drop_down">
<span className="color-grey-9 font-12">{sortKey=="published_at-desc"?"最新上传":"最早上传"}</span><i className="iconfont icon-xiajiantou font-12 ml2 color-grey-6"></i>
<ul className="drop_down_normal">
<li onClick={()=>onSortChange("published_at-desc",0)}>最新上传</li>
<li onClick={()=>onSortChange("published_at-asc",1)}>最早上传</li>
</ul>
<span className="color-grey-9 font-12" style={{
marginRight: " 5px",
}}>{"最新上传"}</span>
<sapn className="relativef fr"
style={{
display: "flex",
flexDirection: "column",
height: "40px",
lineHeight: "40px",
}}
>
<span
style={{
flexDirection: "column",
textAlign: "center",
height: "10px",
lineHeight: "10px",
display: "table",
marginTop: "9px",
}}
>
<i className={sorts[1] === "asc" ?
"iconfont icon-sanjiaoxing-up font-12 color-blue h10 " : "iconfont icon-sanjiaoxing-up font-12 h10"}
onClick={() => onSortChange("published_at-asc", 0)}></i>
</span>
<span
style={{
flexDirection: "column",
height: "10px",
lineHeight: "10px",
textAlign: "center",
display: "table",
}}
>
<i className={sorts[1] === "desc" ?
"iconfont icon-sanjiaoxing-down font-12 yslbottomsj color-blue h10" : "iconfont icon-sanjiaoxing-down font-12 yslbottomsj h10"}
onClick={() => onSortChange("published_at-desc", 0)}></i>
</span>
</sapn>
</li>
</div>}
</div>
{categoryObj.category == 'all' ?
{categoryObj.category == 'all' ?
<div className="itemWrap">
{
videos == undefined ? '' :
videos.length ?
videos == undefined ? '' :
videos.length ?
videos.map((item, index) => {
return (<VideoInReviewItem
{...props}
@ -322,7 +364,7 @@ function InfoVideo (props) {
onMaskClick={onMaskClick}
getCopyText={getCopyText}
>
>
</VideoInReviewItem>)
})
: <NoneData style={{width: '100%'}}></NoneData>
@ -332,7 +374,7 @@ function InfoVideo (props) {
<div className="itemWrap">
{
reviewvideos == undefined ? '' :
reviewvideos.length ?
reviewvideos.length ?
reviewvideos.map((item, index) => {
return (<VideoInReviewItem
{...props}
@ -349,17 +391,17 @@ function InfoVideo (props) {
}
{/* categoryObj.category == 'all' && */}
{/* categoryObj.category == 'all' && */}
{
count > PAGE_SIZE &&
count > PAGE_SIZE &&
<div className="mt30 mb50 edu-txt-center">
<Pagination showQuickJumper total={count} pageSize={PAGE_SIZE}
{...pageObj}
<Pagination showQuickJumper total={count} pageSize={PAGE_SIZE}
{...pageObj}
/>
</div>
}
</div>
</div>
)
}

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe GameAnswer, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end
Loading…
Cancel
Save