init project

This commit is contained in:
Jasder
2020-03-09 00:40:16 +08:00
commit 2937b2a94d
6549 changed files with 7215173 additions and 0 deletions
@@ -0,0 +1,37 @@
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import Loading from '../../Loading';
import Loadable from 'react-loadable';
import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
import { SnackbarHOC,getImageUrl } from 'educoder';
class Shixunauthority extends Component {
render() {
return (
<div className="newMain clearfix">
<div className=" edu-txt-center mt60 mb60">
{/*mt100 mb100*/}
<img src={getImageUrl("images/warn/pic_403.jpg")} />
<p className="font-18 mt40">
您可以稍后尝试&nbsp;<a href="/"
className="color-blue">返回首页</a>
或者&nbsp;
<a target="_blank"
href="//shang.qq.com/wpa/qunwpa?idkey=2f2043d88c1bd61d182b98bf1e061c6185e23055bec832c07d8148fe11c5a6cd"
className="color-blue">QQ反馈&gt;&gt;</a>
</p>
</div>
{/*<div style="clear:both;"></div>*/}
{/*<div id="ajax-indicator" style="display:none;"><span>载入中...</span></div>*/}
{/*<div id="ajax-modal" style="display:none;"></div>*/}
</div>
);
}
}
export default SnackbarHOC() (TPMIndexHOC ( Shixunauthority ));
@@ -0,0 +1,37 @@
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import Loading from '../../Loading';
import Loadable from 'react-loadable';
import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
import { SnackbarHOC,getImageUrl } from 'educoder';
class http500 extends Component {
render() {
return (
<div className="newMain clearfix">
<div className=" edu-txt-center mt60 mb60">
{/*mt100 mb100*/}
<img src={getImageUrl("images/warn/pic_404.jpg")} />
<p className="font-18 mt40">
您可以稍后尝试&nbsp;<a href="/"
className="color-blue">返回首页</a>
或者&nbsp;
<a target="_blank"
href="//shang.qq.com/wpa/qunwpa?idkey=2f2043d88c1bd61d182b98bf1e061c6185e23055bec832c07d8148fe11c5a6cd"
className="color-blue">QQ反馈&gt;&gt;</a>
</p>
</div>
{/*<div style="clear:both;"></div>*/}
{/*<div id="ajax-indicator" style="display:none;"><span>载入中...</span></div>*/}
{/*<div id="ajax-modal" style="display:none;"></div>*/}
</div>
);
}
}
export default SnackbarHOC() (TPMIndexHOC ( http500 ));
+37
View File
@@ -0,0 +1,37 @@
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import Loading from '../../Loading';
import Loadable from 'react-loadable';
import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
import { SnackbarHOC,getImageUrl } from 'educoder';
class Shixunnopage extends Component {
render() {
return (
<div className="newMain clearfix">
<div className=" edu-txt-center mt60 mb60">
{/*mt100 mb100*/}
<img src={getImageUrl("images/warn/pic_500.jpg")} />
<p className="font-18 mt40">
您可以稍后尝试&nbsp;<a href="/"
className="color-blue">返回首页</a>
或者&nbsp;
<a target="_blank"
href="//shang.qq.com/wpa/qunwpa?idkey=2f2043d88c1bd61d182b98bf1e061c6185e23055bec832c07d8148fe11c5a6cd"
className="color-blue">QQ反馈&gt;&gt;</a>
</p>
</div>
{/*<div style="clear:both;"></div>*/}
{/*<div id="ajax-indicator" style="display:none;"><span>载入中...</span></div>*/}
{/*<div id="ajax-modal" style="display:none;"></div>*/}
</div>
);
}
}
export default SnackbarHOC() (TPMIndexHOC ( Shixunnopage ));
@@ -0,0 +1,261 @@
.greytab-inner {
/*overflow-y: scroll;*/
}
/*评论列表*/
.panel-comment_item .t_area {
font-size: 12px;
color: #CCCCCC;
}
.panel-comment_item .orig_reply i {
font-size: 14px !important;
margin-left: 12px;
}
/* ke style 代码块*/
.panel-comment_item ol.linenums {
overflow: auto;
}
.panel-comment_item .rewarded {
color: #FF7500!important
}
.panel-comment_item .rewarded.normalUser {
cursor: inherit;
}
#tab_con_4 .-layout-v {
overflow-y: auto;
}
#tab_con_4 .rc-pagination {
margin-left: auto;
margin-right: auto;
margin-top: 12px;
margin-bottom: 20px;
}
.rc-pagination {
width: fit-content;
}
.comment_content img {
max-width: 23% !important;
}
/* ----------------------------------------------------- CommentInput START */
/*收起的时候radius变化*/
#mini_comment_section .df .ke-container {
border-radius: 15px;
}
#mini_comment_section .df .buttons {
width: 70px;
margin-bottom: 5px;
}
#mini_comment_section .buttons i {
font-size: 18px;
color: #656565;
vertical-align: baseline;
}
#mini_comment_section i.newReplyIcon {
color: #4DACFF;
cursor: pointer;
}
#mini_comment_section .buttons {
margin-bottom: 10px;
}
#mini_comment_section .buttons>p {
margin-top: 4px;
display: none;
}
#mini_comment_section .df .buttons>p {
margin-top: 14px;
display: block;
}
#mini_comment_section {
height: auto;
background-color: #FFF;
/*border-top: 1px solid #f0f1fe;*/
display: flex;
box-shadow: 0px -3px 5px 0px rgba(76, 172, 255, 0.2);
z-index: 99;
}
#mini_comment_section #editor_panel {
margin-bottom: 9px
}
/* commentInput 上传图片的图标挪动到左下*/
#mini_comment_section #editor_panel>div:nth-child(1) {
position: absolute;
bottom: 8px;
right: 88px;
}
#mini_comment_section .ke-toolbar-icon-url {
background-image: url('../../images/tpi/upload-image.png');
background-position: 0px 0px;
background-size: 30px 30px;
width: 30px;
height: 30px;
}
#mini_comment_section .ke-outline {
height: 30px;
margin-bottom: -11px;
width: 30px;
margin-right: -5px;
border: none;
margin: 0px;
padding: 0px;
/* ie上传图片看不见的问题 */
position: absolute;
top: -30px;
left: -30px;
left: -24px;
z-index: 999;
}
#mini_comment_section i.replyIcon {
font-size: 20px;
}
#shixun_comment_block .dot {
width: 4px;
height: 4px;
border-radius: 2px;
background-color: #4CACFF;
width: 4px;
position: relative;
bottom: 30px;
left: 18px;
display: block;
}
#mini_comment_section .ke-container {
border-radius: 6px;
border-color: #EAEAEA;
/*max-height: 400px; */
/*解决上传图片后看不到评论按钮的问题*/
}
.commentTxt{
width: 100%;
height: 95px;
border:1px solid #EAEAEA;
border-radius: 10px;
padding-left: 5px;
}
#game_praise_tread {
cursor: pointer;
}
.commentsbtn {
margin-top: 2px;
}
#shixun_comment_block {
margin: 0px 10px;
margin-bottom: -2px;
}
.panel-comment_item a.task-btn-orange {
background: #4CACFF;
}
/* ----------------------------------------------------- CommentInput END */
.childrenCommentsView {
background: #F4F4F4;
border-radius: 4px;
margin-bottom: 6px;
position: relative;
margin-top: 6px;
}
.childrenCommentsView .trangle{
position: absolute;
border-color: black;
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid #F4F4F4;
left: 13px;
top: -9px;
border-bottom: 10px solid #F4F4F4;
}
.childComment {
padding: 2px 8px;
}
.childComment .iconfont.icon-jiangli {
margin-top: 2px;
}
.childComment:hover {
background: #EBEBEB;
}
.childComment .iconfont {
display: none;
}
.childComment p.orig_reply {
margin-bottom: 0px;
}
.childComment:hover .iconfont{
display: inline;
}
.noCommentTitle {
text-align: center;
margin-top: 20px;
font-size: 16px;
}
.blink {
animation: blink-animation 3s steps(5, start) infinite;
-webkit-animation: blink-animation 3s steps(5, start) infinite;
}
@keyframes blink-animation {
to {
visibility: hidden;
}
}
@-webkit-keyframes blink-animation {
to {
visibility: hidden;
}
}
/*md 编辑器 emoji */
.J_Comment_Reply img.emoji {
width: 24px;
}
.noCommentTitle {
text-align: center;
margin-top: 20px;
font-size: 16px;
height: 100px;
line-height: 100px;
}
.break_word_comments{
/* 这个样式影响到了行高 */
/* white-space: pre-wrap!important; */
word-break: break-word;
/* 影响了 ul li的样式: https://testeduplus2.educoder.net/courses/1748/common_homeworks/12131/740898/appraise */
/* line-height: 14px; */
width: 100%;
word-wrap: break-word;
margin-bottom: 4px;
margin-top: 4px;
}
.childComment .break_word_comments{
line-height: 22px;
}
form.df .tips {
display: none;
}
.loadMoreChildComments {
text-align: center;
height: 24px;
background: #F1F1F1;
cursor: pointer;
}
.loadMoreChildComments i.icon-xiajiantou {
position: relative;
bottom: 5px;
}
@@ -0,0 +1,28 @@
import React, { Component } from 'react';
import Comments from './Comments'
import CommentInput from './CommentInput'
class CommentContainer extends Component {
componentDidMount() {
}
render() {
return (
<Comments
{...this.props}
showHiddenButton={true}
>
</Comments>
)
}
}
export default CommentContainer;
@@ -0,0 +1,131 @@
import React, { Component } from 'react';
import Tooltip from 'material-ui/Tooltip';
import './Comment.css'
import messageImg from '../../images/tpi/message.svg'
import messagegreyImg from '../../images/tpi/messagegrey.svg'
const $ = window.$;
function pasteListener(event) {
if (event.clipboardData.types[0] === 'Files' ) {
event.preventDefault();
// event.stopPropagation();
}
}
/*
*/
class CommentInput extends Component {
componentDidMount() {
const { challenge } = this.props;
}
componentWillReceiveProps(newProps, newContext) {
// TODO 暂没有切实训的场景
if (newProps.challenge && newProps.challenge.shixun_id
&& (!this.props.challenge.shixun_id || newProps.challenge.shixun_id != this.props.challenge.shixun_id)) {
setTimeout(()=>{
window.sd_create_editor_from_shixun_data(newProps.challenge.shixun_id, null, "100%", "Shixun");
if ( $.browser.mozilla ) {
setTimeout(() => {
const _body = $('.ke-edit-iframe')[0].contentWindow.document.body;
_body.removeEventListener('paste', pasteListener)
_body.addEventListener('paste', pasteListener)
}, 4200)
}
}, 100)
}
}
render() {
const { createNewComment, editedComment, commentOnChange, challenge, shixun, loading, praisePlus, gotNewReply, showNewReply} = this.props;
/*
onclick="game_praise('<%= @game_challenge.id %>', '<%= @game_challenge.class %>')"
onclick="game_tread('<%= @game_challenge.id %>')"
style={{display: 'none'}}
*/
return (
<li className="comment-input fl" id="shixun_comment_block">
{ !challenge.shixun_id ? '' :
<div nhname={`new_message_${challenge.shixun_id}`} className="fr" style={{ width: '99%'}}>
<form acceptCharset="UTF-8" action="/discusses?challenge_id=118&dis_id=61&dis_type=Shixun" className="df" data-remote="true" id="new_comment_form" method="post">
<div className="fl" style={{flex: 1,marginTop:'7px'}} id="editor_panel">
<div nhname={`toolbar_container_${challenge.shixun_id}`}></div>
{/*有问题或有建议,请直接给我留言吧!*/}
<textarea id={`comment_news_${challenge.shixun_id}`}
nhname={`new_message_textarea_${challenge.shixun_id}`} name="content"
value={ editedComment } onChange={ commentOnChange } className="none">
</textarea>
</div>
<div className="tips"
style={{ 'float': 'left', 'marginTop': '6px', 'fontSize': '12px', 'color': '#ff6800'}}>
请勿粘贴答案否则将造成账号禁用等后果
</div>
<div className="fr buttons" style={{ minWidth:'25px', height: '32px' }}>
<a id={`new_message_submit_btn_${challenge.shixun_id}`} href="javascript:void(0)"
style={{display: 'none'}} onClick={ createNewComment } className="commentsbtn task-btn task-btn-blue fr">
发送
</a>
<p className="fr ml10" style={{minWidth:'25px'}} >
<Tooltip title={ challenge.user_praise ? "取消点赞" : "点赞"}>
<span id="game_praise_tread" className="color-grey mr20" onClick={praisePlus}>
<i className={`mr3 ${ challenge.user_praise ? "iconfont icon-dianzan color-orange03" : "iconfont icon-dianzan-xian" } `} alt="赞" ></i>
{ challenge.praise_count ?
<span className="font-16" id="game_praise_count">{challenge.praise_count}</span> : ''}
</span>
</Tooltip>
</p>
<p className="fr ml10" style={{minWidth:'25px'}} >
{ gotNewReply ?
<React.Fragment>
<i className={`replyIcon newReplyIcon iconfont icon-tpixiaoxitixing`} onClick={showNewReply}></i>
<span className="dot blink"></span>
</React.Fragment>
:
<Tooltip title={ "暂无新消息" }>
<i className={`replyIcon iconfont icon-tpixiaoxitixing`}></i>
</Tooltip>
}
</p>
</div>
</form>
</div>
}
</li>
)
}
}
/*
<img src={messagegreyImg}/>
<span data-tip-top={ tread ? "踩" : "取消踩" } id="game_tread" className="color-grey" style={{paddingTop:'7px'}}>
<i className={`fa fa-thumbs-down font-20 mr3 ${ tread ? "" : "color-orange03" } `} ></i>
<span className="font-12 font-bd" id="game_tread_count">{tread_count}</span>
</span>
<div className="clearfix with100">
<textarea className="commentTxt"></textarea>
<p className="clearfix">
<a href="javascript:void(0)" className="fl uploadImg mt2">
<img src="/images/sendimg.svg" className="fl mr3 mt6"/><span className="up">上传图片</span>
</a>
<a href="javascript:void(0)" className="commentsbtn task-btn task-btn-blue fr">评论</a>
<span className="fr mr15 mt3"><i className="fa fa-thumbs-up color-dark-grey mr3 font-18"></i>20</span>
<img src="/images/message.svg" className="fr mr15 mt8"/>
</p>
</div>
*/
export default CommentInput;
@@ -0,0 +1,72 @@
import React, { Component } from 'react';
import { Redirect } from 'react-router';
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import PropTypes from 'prop-types';
import classNames from 'classnames'
const $ = window.$;
const _origin = window.location.origin;
class CommentItemKEEditor extends Component {
componentDidUpdate(prevProps) {
const { item, currentReplyComment } = this.props;
if ( prevProps.showReplyEditorFlag != this.props.showReplyEditorFlag &&
currentReplyComment && currentReplyComment.id == item.id ) {
this.showOrHideEditor(currentReplyComment)
}
}
// 如果未初始化,会先初始化
showOrHideEditor = (comment) => {
const { user } = this.props;
console.log('initReply ', comment)
const $ = window.$;
var id = comment.id
var reply_message_el = `#reply_message_${id}`
var reply_iconup_el = `#reply_iconup_${id}`
if($(reply_message_el).html() == "") {
$(".reply_to_message").html("");
$(reply_message_el).html(`<div className=\"orig_reply_box borderBottomNone reply_to_message\" id=\"reply_to_message_${id}\">\n <div class=\"homepagePostReplyPortrait mr15 imageFuzzy fl\" id=\"reply_image_${id}\"><a href=\"${user.user_url}\" target=\"_blank\" alt=\"用户头像\"><img alt=\"0?1442652658\" height=\"33\" src=\"${_origin}/images/${user.image_url}\" width=\"33\" /><\/a><\/div>\n <div class=\"orig_textarea fl\" style=\"margin-bottom: 0px\">\n <div nhname=\'new_message_${id}\'>\n <form accept-charset=\"UTF-8\" action=\"/discusses?challenge_id=118&amp;dis_id=61&amp;dis_type=Shixun\" data-remote=\"true\" id=\"new_comment_form\" method=\"post\"><div style=\"margin:0;padding:0;display:inline\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" /><input name=\"authenticity_token\" type=\"hidden\" value=\"HJTbMpfI8LKUpwghfkvgB2SaMmcIVyVdAezyKmzJ7FU=\" /><\/div>\n <input type=\"hidden\" id=\"dis_reply_id\" name=\"reply_id\" value=\"${id}\">\n <div nhname=\'toolbar_container_${id}\'><\/div>\n <textarea placeholder=\"有问题或有建议,请直接给我留言吧!\" id=\"comment_news_${id}\" style=\"display: none\" nhname=\'new_message_textarea_${id}\' name=\"content\"><\/textarea>\n <a id=\"new_message_submit_btn_${id}\" href=\"javascript:void(0)\" onclick=\"this.style.display=\'none\'\" class=\"mt10 task-btn task-btn-orange fr\">发送<\/a>\n <div class=\"cl\"><\/div>\n <p nhname=\'contentmsg_${id}\'><\/p>\n<\/form> <\/div>\n <div class=\"cl\"><\/div>\n <\/div>\n <div class=\"cl\"><\/div>\n<\/div>\n`); //" ide语法识别
$(reply_iconup_el).show();
$(function(){
window.sd_create_editor_from_data(id ,null,"100%", "Discuss");
});
}else {
if ($(reply_message_el).is(':visible')) {
$(reply_message_el).hide();
} else {
$(reply_message_el).show();
}
// $(reply_message_el).html("");
// $(reply_iconup_el).hide();
}
// 自动focus
setTimeout(()=>{
var iframe =$(`#reply_to_message_${id}`).find('iframe')[0]
iframe && iframe.contentDocument.body.focus()
}, 200)
}
render() {
const { match, history, item, user } = this.props
if (!item) {
return <div></div>
}
return (
<div>
<div className="cl"></div>
<div id={`reply_message_${item.id}`} className="reply_to_message"></div>
</div>
);
}
}
export default ( CommentItemKEEditor );
@@ -0,0 +1,58 @@
/*md编辑器 将toolbar高度变小变袖珍*/
.reply_to_message .editorMD .editormd-toolbar li {
padding: 0;
}
.reply_to_message .editorMD .editormd-toolbar {
min-height: 28px;
}
.reply_to_message .editorMD .editormd-toolbar .editormd-toolbar-container {
min-height: 28px;
}
.reply_to_message .editorMD .editormd-toolbar i {
margin-left: 0px;
}
.reply_to_message .editorMD .editormd-preview {
top: 30px;
}
.reply_to_message .editorMD .CodeMirror {
margin-top: 30px;
height: 90px;
}
.panel-comment_item .editor__resize {
transform: translateX(-2%);
position: absolute;
width: 120px;
height: 4px;
left: 54%;
transform: translateX(-50%);
margin-top: 2px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
cursor: row-resize;
text-indent: 110%;
white-space: nowrap;
overflow: hidden;
text-transform: capitalize;
box-sizing: border-box;
}
/*帖子回復按鈕*/
a.commentsbtn.task-btn-blue {
background: #4CACFF !important;
}
.commentTab a.commentsbtn.task-btn-blue {
margin-right: 22px;
margin-top: 6px;
}
.editormd-grid-table-row a.editormd-emoji-btn.selected {
border-bottom: 2px solid #4CACFF!important;
}
@@ -0,0 +1,138 @@
import React, { Component } from 'react';
import { Redirect } from 'react-router';
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import PropTypes from 'prop-types';
import classNames from 'classnames'
import { getImageUrl, toPath, getUrl,getUploadActionUrl } from 'educoder';
import './CommentItemMDEditor.css'
const $ = window.$;
const _origin = window.location.origin;
class CommentItemMDEditor extends Component {
componentDidUpdate(prevProps) {
const { item, currentReplyComment } = this.props;
if ( prevProps.showReplyEditorFlag != this.props.showReplyEditorFlag &&
currentReplyComment && currentReplyComment.id == item.id ) {
this.showOrHideEditor(currentReplyComment)
}
}
// 如果未初始化,会先初始化
showOrHideEditor = (comment) => {
const { user } = this.props;
console.log('initReply ', comment)
const $ = window.$;
var commentId = comment.id
var reply_message_el = `#reply_message_${commentId}`
const initMD_ID = `reply_message_editorMd_${commentId}`
const view_selector = `.commentItemMDEditorView_${commentId}`
const commitBtnSelector = `#commitBtn_${commentId}`
if( $(`#${initMD_ID} textarea`).length === 1 ) { // 没有初始化
const placeholder = '我要回复...'
// const imageUrl = `/upload_with_markdown?container_id=${commentId}&container_type=Memo`;
const imageUrl = `${getUploadActionUrl()}`;
const otherOptions = {
watch: false,
htmlDecode: "style,script,iframe", // you can filter tags decode
// taskList: true,
mode: 'markdown',
toolbar: true,
markdown: '',
readOnly: false,
// preview: false,
tex: true, // 数学公式
flowChart: false, // 默认不解析
sequenceDiagram: false, // 默认不解析
dialogLockScreen: false,
}
const commentMDEditor = window.create_editorMD_4comment(`${initMD_ID}`, '', 120, placeholder, imageUrl, () => {
// onload callback
commentMDEditor.cm.focus()
window.initMDEditorDragResize(".editor__resize", commentMDEditor, {
initHeight: 120
})
}, otherOptions);
commentMDEditor.state.preview = false;
this.commentMDEditor = commentMDEditor;
$('.commentItemMDEditorView').hide();
$(view_selector).show();
}else { // 初始化了,显示隐藏切换
if ($(reply_message_el).is(':visible')) {
$( view_selector ).hide()
} else {
$('.commentItemMDEditorView').hide();
$(view_selector).show();
window._currentChildcommentMDEditor && window._currentChildcommentMDEditor.resize()
// 自动focus
setTimeout(()=>{
this.commentMDEditor && this.commentMDEditor.cm && this.commentMDEditor.cm.focus()
this.commentMDEditor.resize() // 解决切换显示、隐藏多次后出现的样式错乱的问题
}, 200)
}
}
window._currentChildcommentMDEditor = this.commentMDEditor; // tpi resize 的时候需要做调用editor.resize
}
onCommit = () => {
window.$(document).trigger("onReply", { commentContent: this.commentMDEditor.getValue()
, id: this.props.item.id, editor: this.commentMDEditor } );
}
render() {
const { match, history, item, user, buttonText } = this.props
if (!item) {
return <div></div>
}
return (
<div className={`commentItemMDEditorView commentItemMDEditorView_${item.id}`} style={{display:'none'}}>
<div className="homepagePostReplyPortrait mr15 fl imageFuzzy" id="reply_image_3097"
style={{ marginTop: '2px', marginRight: '-20px' }} >
<a href={`${user.user_url}`} target="_blank" alt="用户头像">
{/* 'https://testeduplus2.educoder.net/images/avatars/User/50571'|| */}
<img alt="0?1442652658" height="33" src={`/images/${user.image_url}`} width="33"></img>
</a>
</div>
<div id={`reply_message_${item.id}`} className="reply_to_message commentItemMDEditor editormd-image-click-expand"
style={{ paddingTop: '0px', paddingBottom: '0px', marginTop: '36px' }}
>
<div id={`reply_message_editorMd_${item.id}`} className="editorMD" style={{ marginBottom: '0px'}}>
<textarea style={{'display': 'none'}}>
</textarea>
</div>
<div className="editor__resize" href="javascript:void(0);" style={{display: ''}}>调整高度</div>
<div class="clearfix">
<a id={`commitBtn_${item.id}`} href="javascript:void(0)"
onClick={this.onCommit} style={{ marginRight: '44px' }}
className="commentsbtn task-btn task-btn-blue fr " style={{display: ''}}>
{ buttonText || '发送'}
</a>
</div>
</div>
</div>
);
}
}
export default ( CommentItemMDEditor );
// style={{ margin: '10px 44px', marginBottom: '0px'}}
@@ -0,0 +1,616 @@
import React, { Component } from 'react';
import Dialog, {
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
} from 'material-ui/Dialog';
import Button from 'material-ui/Button';
import Tooltip from 'material-ui/Tooltip';
import Pagination from 'rc-pagination';
import 'rc-pagination/assets/index.css';
import { getImageUrl, toPath } from 'educoder';
import Input, { InputLabel } from 'material-ui/Input';
import { FormControl, FormHelperText } from 'material-ui/Form';
import CommentItemKEEditor from './CommentItemKEEditor';
import CommentItemMDEditor from './CommentItemMDEditor';
import './Comment.css'
import Modals from '../modals/Modals'
import { InputNumber } from 'antd'
/*
-------------------------- 样式相关
class 改成 className
style 需要传入json对象 style="margin:0px"> -> style={{ margin:"0px" }}>
margin-top -> marginTop
onclick -> onClick 驼峰
-------------------------- 模板语法相关
页面都在前端组装、渲染
rails模板的 if等逻辑改成js实现
客户端需要能判断 User.current.manager_of_shixun?(comment.dis_id)
-------------------------- 现有ui控件的使用方式
方案1: 换成对应的react组件,然后再调用(控件简单时推荐)
方案2: 在react环境中使用若干jquery插件(控件复杂,又无react环境下的替代方案时考虑使用)
--------------------------
需要服务端提供对应的rest api
*/
const _origin = window.location.origin;
/*
tpi讨论、交流问答帖子详情讨论、课堂讨论的公用模块:
https://www.educoder.net/tasks/n2ejvaowk6l9
https://www.educoder.net/forums/2629
注意不同模块使用时的参数的不同 usingAntdModal onlySuperAdminCouldHide isChildCommentPagination等等
用到的props:
user user_url image_url
loadingComments--
comment_count_without_reply
currentPage
comments
buttonText 发送按钮 显示文本
showRewardButton 是否显示奖励按钮
showHiddenButton 是否显示隐藏按钮
onlySuperAdminCouldHide 只有超级管理员才显示隐藏、取消隐藏
isChildCommentPagination 是否子回复分页
loadMoreChildComments function 加载所有子回复
usingAntdModal 是否使用antd的弹框
接口
deleteComment 删除
onPaginationChange 翻页变化
commentPraise 点赞
hiddenComment 隐藏
rewardCode 奖励
*/
class Comments extends Component {
constructor(props) {
super(props)
this.handleDialogClose = this.handleDialogClose.bind(this)
this.handleGoldRewardDialogClose = this.handleGoldRewardDialogClose.bind(this)
this.state = {
dialogOpen: false,
goldRewardDialogOpen: false,
goldRewardInput: '',
showReplyEditorFlag: false, // false->true or true->false时切换editor显示或隐藏
currentReplyComment: null
}
}
componentWillUnmount() {
const $ = window.$;
$(document).off("onReply");
}
componentDidMount() {
setTimeout(()=>{
const $ = window.$;
// 绑定后会自动off 加timeout试试
$(document).on("onReply", (e, args)=>{
const { commentContent, id, editor } = args;
this.props.replyComment(commentContent, id, editor)
});
}, 1000)
}
initReply(comment) {
this.props.initReply && this.props.initReply(comment)
// 如果配置的使用kindEditor
if (window.__useKindEditor === true) {
const { user } = this.props;
console.log('initReply ', comment)
const $ = window.$;
var id = comment.id
var reply_message_el = `#reply_message_${id}`
var reply_iconup_el = `#reply_iconup_${id}`
if($(reply_message_el).html() == "") {
$(".reply_to_message").html("");
$(reply_message_el).html(`<div className=\"orig_reply_box borderBottomNone reply_to_message\" id=\"reply_to_message_${id}\">\n <div class=\"homepagePostReplyPortrait mr15 imageFuzzy fl\" id=\"reply_image_${id}\"><a href=\"${user.user_url}\" target=\"_blank\" alt=\"用户头像\"><img alt=\"0?1442652658\" height=\"33\" src=\"${_origin}/images/${user.image_url}\" width=\"33\" /><\/a><\/div>\n <div class=\"orig_textarea fl\" style=\"margin-bottom: 0px\">\n <div nhname=\'new_message_${id}\'>\n <form accept-charset=\"UTF-8\" action=\"/discusses?challenge_id=118&amp;dis_id=61&amp;dis_type=Shixun\" data-remote=\"true\" id=\"new_comment_form\" method=\"post\"><div style=\"margin:0;padding:0;display:inline\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" /><input name=\"authenticity_token\" type=\"hidden\" value=\"HJTbMpfI8LKUpwghfkvgB2SaMmcIVyVdAezyKmzJ7FU=\" /><\/div>\n <input type=\"hidden\" id=\"dis_reply_id\" name=\"reply_id\" value=\"${id}\">\n <div nhname=\'toolbar_container_${id}\'><\/div>\n <textarea placeholder=\"有问题或有建议,请直接给我留言吧!\" id=\"comment_news_${id}\" style=\"display: none\" nhname=\'new_message_textarea_${id}\' name=\"content\"><\/textarea>\n <a id=\"new_message_submit_btn_${id}\" href=\"javascript:void(0)\" onclick=\"this.style.display=\'none\'\" class=\"mt10 task-btn task-btn-orange fr\">${this.props.buttonText || '发送'}<\/a>\n <div class=\"cl\"><\/div>\n <p nhname=\'contentmsg_${id}\'><\/p>\n<\/form> <\/div>\n <div class=\"cl\"><\/div>\n <\/div>\n <div class=\"cl\"><\/div>\n<\/div>\n`); //" ide语法识别
$(reply_iconup_el).show();
$(function(){
window.sd_create_editor_from_data(id ,null,"100%", "Discuss");
});
}else {
if ($(reply_message_el).is(':visible')) {
$(reply_message_el).hide();
} else {
$(reply_message_el).show();
}
// $(reply_message_el).html("");
// $(reply_iconup_el).hide();
}
} else { // MD
this.setState({
currentReplyComment: comment,
showReplyEditorFlag: !this.state.showReplyEditorFlag
})
}
}
// enableReplyTo
// onClick={() => this.replyTo(item.user_id)}
replyTo = (toUserId) => {
}
renderChildenComments(comment) {
if (!comment.children || comment.children.length === 0) {
return ''
}
const { user } = this.props;
let childCommentsElement = comment.children.map((item, index) =>{
let _content = this.parseCommentContent(item.content);
return (
<div key={index} className="childComment" >
<div className="J_Comment_Info clearfix mt3">
<div className="t_info fl">
<a href={`${_origin}/users/${item.user_login}`} className="content-username hide fl">
{item.username}
</a>
<span className="t_area fl">{item.time}</span>
{/* { item.position ? <span className="fl color-light-green font-14 ml15">[第{item.position}关]</span> : ""} */}
{
item.reward ?
<Tooltip title={ `已奖励金币${item.reward}` } disableFocusListener={true}>
<a href="javascript:void(0);" style={{ marginLeft: '20px', cursor: 'default'}}
className={`rewarded color-grey-8 font-12 fl ${ item.admin === true ? '': 'normalUser'}`}
>
<i className="iconfont icon-gift mr5 color-orange fl" style={{display: 'inline'}}></i><span className="fl">{item.reward}</span>
</a>
</Tooltip> : ''
}
</div>
<p className="fr orig_reply lineh-20">
<span id="hidden_discuss_btn_952">
</span>
{this.props.showRewardButton != false && comment.admin === true ?
<a href="javascript:void(0);" className="color-grey-8" onClick={() => this.showGoldRewardDialog(comment, item) }>
<Tooltip title={ "给TA奖励金币" } disableFocusListener={true}>
<i className="iconfont icon-jiangli fl"></i>
</Tooltip>
</a>
:""}
{/*子回复还没hidden字段*/}
{false && comment.admin === true ?
<Tooltip title={ item.hidden ? "取消隐藏" : "隐藏评论" }>
<a href="javascript:void(0);" className="color-grey-8" onClick={() => this.onCommentBtnClick(comment, item, item.hidden ? 'hiddenCancel' : 'hidden') }>
<i className={`fa ${item.hidden ? 'fa-eye' : 'fa-eye-slash'} mr5`}></i>
</a>
</Tooltip>
:""}
{/* 新版user_id不准,获取方式不对 */}
{ comment.admin === true || item.can_delete || item.user_id === user.user_id || item.user_login == user.login ?
<a href="javascript:void(0);" className="color-grey-8" id="delete_reply_118_952" onClick={() => this.onCommentBtnClick(comment, item, 'delete') }>
<Tooltip title={ "删除" } disableFocusListener={true}>
<i className="iconfont icon-shanchu mr5"></i>
</Tooltip>
</a>
: ''}
{/*
<span className="ml5 mr5 color-grey-8">|</span>
<span className="reply_praise_count_952">
<a href="/praise_tread/praise_plus?obj_id=952&amp;obj_type=Discuss&amp;type=reply&amp;user_activity_id=952" data-remote="true" className="fr mr5 color-grey-8" data-tip-down="点赞">
<i className="fa fa-thumbs-up mr5"></i>赞
<span className="ml5 fr"></span>
</a>
</span>*/}
</p>
{/* currentUser.id == item.creator_user.id && (
<p className="fr mr10 orig_repll">
<span id={`hidden_discuss_btn_${item.id}`}>
<a href="javascript:void(0);" className="color-grey-8" onClick={ this.onCommentBtnClick(comment, item, 'delete') } >
<i className="far fa-trash-alt mr5"></i>删除
</a>
<span className="ml5 mr5 color-grey-8">|</span>
</span>
</p>
)*/}
</div>
<div className="comment_content clearfix" id={`reply_content_${item.id}`}>
<div className="color-grey-3" id={`reply_content_${item.id}`}>
<div className={"break_word_comments markdown-body"} dangerouslySetInnerHTML={{__html: _content}}></div>
<div className="cl"></div>
</div>
</div>
</div>
)
})
return childCommentsElement
}
parseCommentContent = (oldContent) => {
if (oldContent && oldContent.startsWith('<') && oldContent.endsWith('>')) {
} else if (window.$('#md_div').length) { // 有这个临时处理md内容的dom
window.$('#md_div').html('')
// markdown to html
try {
var markdwonParser = window.editormd.markdownToHTML("md_div", {
markdown: oldContent,
emoji: true,
htmlDecode: "style,script,iframe", // you can filter tags decode
taskList: true,
tex: true, // 默认不解析
flowChart: true, // 默认不解析
sequenceDiagram: true // 默认不解析
});
oldContent = window.$('#md_div').html()
} catch (e) {
// TODO 可能公式parse时报错了
console.error(e)
}
}
return oldContent;
}
renderComments() {
let { comments, currentUser, deleteComment, commentPraise, hiddenComment, user } = this.props;
const { showReplyEditorFlag, currentReplyComment } = this.state;
if (!comments || comments.length === 0) {
return;
}
// "https://www.educoder.net/users/m02945638"
let commentsElement = comments.map((item, index) => {
let _content
_content = this.parseCommentContent(item.content);
return (
<div className="comment_item_cont df clearfix" key={index}>
<div className="J_Comment_Face fl">
<a href={`${_origin}/users/${item.user_login}`} target="_blank">
<img alt="用户头像" height="50" src={getImageUrl(`images/${item.image_url}`)} width="50"/>
</a>
</div>
<div className="t_content fl">
<div className="J_Comment_Reply">
<div className="comment_orig_content" style={{ margin:"0px" }}>
<div className="J_Comment_Info clearfix mt3">
<div className="t_info fl">
<a href={`${_origin}/users/${item.user_login}`} className="content-username hide fl">
{item.username}
</a>
<span className="t_area fl">{item.time}</span>
{ item.position && <span className="fl color-light-green font-14 ml15">[{item.position}]</span> }
{ item.game_url ?
<Tooltip title={ `点击查看TA的代码页面` } disableFocusListener={true}>
<a href={item.game_url} target="_blank" className="fl font-14 ml15"
style={{ color: "#4CACFF", cursor: "pointer" }}
>查看</a>
</Tooltip> : ""}
{
item.reward ?
<Tooltip title={ `已奖励金币${item.reward}` } disableFocusListener={true}>
<a href="javascript:void(0);" style={{ marginLeft: '20px', cursor: 'default'}}
className={`rewarded color-grey-8 font-12 fl ${ item.admin === true ? '': 'normalUser'}`}
>
<i className="iconfont icon-gift mr5 color-orange fl"></i><span className="fl">{item.reward}</span>
</a>
</Tooltip> : ''
}
</div>
{/* currentUser.id == item.creator_user.id && (
<p className="fr mr10 orig_repll">
<span id={`hidden_discuss_btn_${item.id}`}>
<a href="javascript:void(0);" className="color-grey-8" onClick={ deleteComment.bind(this, item, index) } >
<i className="far fa-trash-alt mr5"></i>删除
</a>
<span className="ml5 mr5 color-grey-8">|</span>
</span>
</p>
)*/}
</div>
<div className="comment_content clearfix" id={`reply_content_${item.id}`}>
<div className="color-grey-3" id={`reply_content_${item.id}`}>
{/* 改成后端返回了的,都是要显示的,不管hidden的值是true还是false */}
{/* { item.hidden && ((this.props.onlySuperAdminCouldHide && !item.isSuperAdmin)
|| !this.props.onlySuperAdminCouldHide && item.admin === false && (item.manager === false || item.manager == undefined))
? <p className="color-orange font-16">违规评论已被屏蔽!</p>
: */}
<div className={"break_word_comments markdown-body"} dangerouslySetInnerHTML={{__html: _content}}></div>
{/* } */}
<div className="cl"></div>
</div>
</div>
<div className="childrenCommentsView">
{(item && item.children && item.children.length) ? <div className="trangle"></div>: ''}
{this.renderChildenComments(item)}
{ item.isAllChildrenLoaded != true && item.children && this.props.isChildCommentPagination == true && item.child_message_count > 5?
<Tooltip title={ "点击查看更多回复" } disableFocusListener={true}>
<div className="loadMoreChildComments" onClick={() => {this.props.loadMoreChildComments && this.props.loadMoreChildComments(item)}}>
<i className="iconfont icon-xiajiantou"></i>
</div>
</Tooltip>
: ''}
</div>
{/*mr10 */}
<p className="fr orig_reply">
<span id="hidden_discuss_btn_952">
</span>
{/* && !item.reward */}
{this.props.showRewardButton != false && item.admin === true ?
<a href="javascript:void(0);" className="color-grey-8 fl mt2" onClick={() => this.showGoldRewardDialog(item) }>
<Tooltip title={ "给TA奖励金币" } disableFocusListener={true}>
<i className="iconfont icon-jiangli mr5 fl"></i>
</Tooltip>
</a>
:""}
{ this.props.showHiddenButton == true
&& (this.props.onlySuperAdminCouldHide && item.isSuperAdmin || !this.props.onlySuperAdminCouldHide && item.admin === true) ?
<Tooltip title={ item.hidden ? "取消隐藏" : "隐藏评论" } disableFocusListener={true}>
<a href="javascript:void(0);" className="color-grey-8 fl mt1" onClick={() => this.onCommentBtnClick(item, '', item.hidden ? 'hiddenCancel' : 'hidden') }>
<i className={` ${item.hidden ? 'iconfont icon-yincangbiyan' : 'fa fa-eye'} mr5`}></i>
</a>
</Tooltip>
:""}
{ item.admin && ( !item.children || item.children.length === 0 )?
<a href="javascript:void(0);" className="color-grey-8" onClick={() => this.onCommentBtnClick(item, '', 'delete') }>
<Tooltip title={ "删除" } disableFocusListener={true}>
<i className="iconfont icon-shanchu mr5"></i>
</Tooltip>
</a> : '' }
{/* <span className="ml5 mr5 color-grey-8">|</span>*/}
{(this.props.showReply == undefined || this.props.showReply == true) && <a href={`javascript:void(0)`} className="color-grey-8"
onClick={() => this.initReply(item) } >
<Tooltip title={ "回复" }>
<i className="iconfont icon-huifu1 mr5"></i>
</Tooltip>
</a>}
{/* <span className="ml5 mr5 color-grey-8">|</span>*/}
<span className="reply_praise_count_952">
<Tooltip title={ item.user_praise ? "取消点赞" : "点赞" }>
<a href={`javascript:void(0)`} className={`fr mr5 ${item.user_praise ? 'color-orange03' : 'color-grey-8'}`} onClick={()=>commentPraise(item.id)}>
<i className={ item.user_praise ? "iconfont icon-dianzan mr3" : "iconfont icon-dianzan-xian mr3" }></i>
<span className="fr font-14" style={{ marginTop: '1px'}}>{item.praise_count?item.praise_count:''}</span>
</a>
</Tooltip>
</span>
</p>
{/* __useKindEditor暂时没用到了,TODO 可以去掉 */}
{ window.__useKindEditor ? <CommentItemKEEditor showReplyEditorFlag={showReplyEditorFlag}
currentReplyComment={currentReplyComment}
item={item}
user={user}
>
</CommentItemKEEditor>
:
<CommentItemMDEditor showReplyEditorFlag={showReplyEditorFlag}
currentReplyComment={currentReplyComment}
item={item}
user={user}
buttonText={this.props.buttonText}
>
</CommentItemMDEditor>
}
{/* <div id={`reply_message_${item.id}`} className="reply_to_message"></div> */}
</div>
</div>
</div>
</div>
)
})
/*
/users/reply_to?reply_id=${item.id}&amp;type=Challenge&amp;user_activity_id=118
*/
/*
onclick="delete_confirm_box_2('<%= discuss_path(comment, :challenge_id => @game_challenge) %>', '确定要删除该条回复吗?')"
delete按钮
id=`delete_reply_<%=@game_challenge.id %>_<%=comment.id %>`
*/
return commentsElement;
}
onCommentBtnClick(comment, childComment, dialogType) {
this.comment = comment;
this.childComment = childComment;
this.setState({dialogOpen: true, dialogType: dialogType})
}
handleDialogClose() {
this.setState({
dialogOpen: false
})
}
onDialogOkBtnClick = () => {
const { deleteComment, hiddenComment } = this.props;
const { dialogType } = this.state;
if (dialogType === 'delete') {
deleteComment(this.comment, this.childComment ? this.childComment.id : '')
} else if (dialogType === 'hidden' || dialogType === 'hiddenCancel' ) {
hiddenComment(this.comment, this.childComment ? this.childComment.id : '')
}
this.setState({dialogOpen: false})
}
showGoldRewardDialog(comment, childComment) {
if (comment.admin === true) {
this.comment = comment;
this.childComment = childComment;
this.setState({goldRewardDialogOpen: true})
}
}
handleGoldRewardDialogClose() {
this.setState({
goldRewardDialogOpen: false
})
}
onGoldRewardDialogOkBtnClick() {
console.log('onGoldRewardDialogOkBtnClick')
const { goldRewardInput } = this.state;
// || goldRewardInput.indexOf('-') !== -1
if (!goldRewardInput || goldRewardInput === '0' ) {
this.setState({ goldRewardInputError: true})
return;
} else {
this.setState({goldRewardDialogOpen: false})
this.props.rewardCode(this.comment, this.childComment, goldRewardInput)
}
}
onGoldRewardInputChange(value) {
// e.target.value
const number = parseInt(value || 0, 10);
if (Number.isNaN(number)) {
return;
}
this.setState({ goldRewardInput: number, goldRewardInputError: false });
}
render() {
const { deleteComment, onPaginationChange, comment_count_without_reply, currentPage, comments, usingAntdModal } = this.props;
const { dialogOpen, goldRewardDialogOpen, dialogType, goldRewardInputError } = this.state;
const goldRewardInputErrorObj = goldRewardInputError ? {'error': 'error'} : {}
return (
<div className="fit -scroll" style={{ 'overflow-x': 'hidden'}}>
{ usingAntdModal ? <Modals
modalsType={dialogOpen}
modalsTopval={
dialogType === 'delete' ? '确定要删除该条回复吗?'
: dialogType === 'hidden' ? '确定要隐藏该条回复吗?'
: dialogType === 'hiddenCancel' ? '确定要取消隐藏该条回复吗?' : ''
}
modalsBottomval={""}
modalCancel={this.handleDialogClose}
modalSave={this.onDialogOkBtnClick}
>
</Modals> :
<Dialog
open={dialogOpen}
disableEscapeKeyDown={true}
onClose={this.handleDialogClose}
>
<DialogTitle id="alert-dialog-title">{"提示"}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description" style={{textAlign: 'center'}}>
{
dialogType === 'delete' ? '确定要删除该条回复吗?'
: dialogType === 'hidden' ? '确定要隐藏该条回复吗?'
: dialogType === 'hiddenCancel' ? '确定要取消隐藏该条回复吗?' : ''
}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.handleDialogClose} color="primary">
取消
</Button>
<Button variant="raised"
onClick={() => this.onDialogOkBtnClick() } color="primary" autoFocus>
确定
</Button>
</DialogActions>
</Dialog>
}
<Dialog
open={goldRewardDialogOpen}
disableEscapeKeyDown={true}
onClose={this.handleGoldRewardDialogClose}
>
<DialogTitle id="alert-dialog-title">{"奖励设置"}</DialogTitle>
<DialogContent>
<InputNumber placeholder="请输入奖励的金币数量" id="goldReward" type="number" value={this.state.goldRewardInput}
onChange={(e) => this.onGoldRewardInputChange(e)} width={228} style={{ width: '228px'}} />
{/* <FormControl { ...goldRewardInputErrorObj } aria-describedby="name-error-text">
<InputLabel htmlFor="goldReward">请输入奖励的金币数量</InputLabel>
<Input id="goldReward" type="number" value={this.state.goldRewardInput} onChange={(e) => this.onGoldRewardInputChange(e)} />
{ goldRewardInputError ? <FormHelperText id="name-error-text">奖励金币不能为空或负数</FormHelperText> : ''}
</FormControl> */}
{/*<DialogContentText id="alert-dialog-description" style={{textAlign: 'center'}}> </DialogContentText>*/}
</DialogContent>
<DialogActions>
<Button onClick={this.handleGoldRewardDialogClose} color="primary">
取消
</Button>
<Button variant="raised"
onClick={() => this.onGoldRewardDialogOkBtnClick() } color="primary" autoFocus>
确定
</Button>
</DialogActions>
</Dialog>
<div className="-layout-v -fit">
<div className="panel-comment_item" >
{this.renderComments()}
</div>
{ (comment_count_without_reply > 10 ) ?
<div className="paginationSection">
<Pagination showQuickJumper onChange={onPaginationChange} current={currentPage} total={comment_count_without_reply} />
</div>
: '' }
{ (comment_count_without_reply == 0) ? <div>
<div className="edu-tab-con-box clearfix edu-txt-center">
<img className="edu-nodata-img mb20"
src={getImageUrl("images/educoder/nodata.png")} />
<p className="edu-nodata-p mb20">暂时还没有相关数据哦</p>
</div>
</div>
: '' }
</div>
</div>
)
}
}
export default Comments;
@@ -0,0 +1,625 @@
import React from 'react'
import ReactDOM from 'react-dom'
import axios from 'axios'
import { notification } from 'antd'
import update from 'immutability-helper'
import ImageLayer from '../page/layers/ImageLayer'
const $ = window.$
/* 需要的props:
shixun
id
myshixun
id
challenge
id
user
"image_url": user.image_url,
"username": user.username,
showSnackbar
*/
/*
这里提供props给WrappedComponent,用于封装特定的state和功能(评论列表)
不要直接使用this.xxx调用WrappedComponent内的方法,尽量避免互相依赖,封装好后供不同场景使用(tpi、tpm)
*/
export function commentHOC(WrappedComponent) {
// 这里如果extends WrappedComponent 会出现 WrappedComponent mount twice的问题
return class II extends React.Component {
constructor(props) {
super(props)
// ------------------------------------
this.createNewComment = this.createNewComment.bind(this)
this.fetchCommentIfNotFetched = this.fetchCommentIfNotFetched.bind(this)
this.replyComment = this.replyComment.bind(this)
this.deleteComment = this.deleteComment.bind(this)
this.commentPraise = this.commentPraise.bind(this)
this.hiddenComment = this.hiddenComment.bind(this)
this.rewardCode = this.rewardCode.bind(this)
this.showNewReply = this.showNewReply.bind(this)
this.newMessage = this.newMessage.bind(this)
// ------------------------------------
this.onPaginationChange = this.onPaginationChange.bind(this)
this.state = {
// 评论
comments: [],
comment_count_without_reply: 0,
// 默认pageSize为10
currentPage: 1,
loadingComments: true,
// 图片最大化
showImage: false,
imageSrc: '',
}
}
// ----------------------------------------------------------------------------------------------评论 Start
// ----------------------------------------------------------------------------------------------评论 Start
onPaginationChange(page) {
this.fetchComment(page)
this.setState({
currentPage: page,
});
}
/*
email: "1843820944@qq.com"
grade: 2138
image_url: "avatar/User/36497"
login: "p24891375"
name: "杨俊男"
user_url: "/users/p24891375"
*/
handleComments(comments, responseData) {
comments.forEach(element => {
if (element.children) {
this.handleComments(element.children, responseData)
}
element.admin = responseData.all
element.manager = element.manage
element.username = element.author.name
element.user_login = element.author.login
element.image_url = element.author.image_url
element.user_id = element.author.user_id
});
}
responseDataParse(response) {
const comments = response.data.comments
this.handleComments(comments, response.data)
}
fetchComment(page = 1) {
const { challenge, shixun, match } = this.props;
// const url = `/api/v1/shixuns/${shixun.id}/shixun_discusses?page=${page-1}&container_type=Shixun`
const url =
`/discusses.json?page=${page-1}&container_identifier=${shixun && shixun.identifier ?
shixun.identifier : match.params.shixunId}&container_type=Shixun`
this.setState({
loadingComments: true,
});
// test
// if (true) {
if (false) {
var data = {"children_list":[{"id":1165,"content":"\u7535\u8111\u6ca1\u6709pi\u554a","time":"4\u5929\u524d","position":2,"user_id":31542,"reward":null,"image_url":"avatars/User/b","username":"\u8d75\u94f6\u7fd4","user_login":"p81572036","shixun_id":61,"hidden":false,"manager":false,"praise_count":0,"user_praise":false,"admin":false,"children":null},{"id":1142,"content":"\u6700\u540e\u4e00\u9898\u4e09\u5929\u6253\u9c7c\uff0c\u4e24\u5929\u6652\u7f51\uff0c\u4e0d\u5e94\u8be5\u662f1.01*1.01*1.01*0.99*0.99\uff0c\u800c\u4e0d\u662f\u52a0\u5417\uff1f","time":"4\u5929\u524d","position":1,"user_id":33714,"reward":null,"image_url":"avatars/User/b","username":"\u843d\u5c18","user_login":"p52769048","shixun_id":61,"hidden":false,"manager":false,"praise_count":0,"user_praise":false,"admin":false,"children":[{"content":"\u672c\u5173\u5e76\u6ca1\u6709\u5199\u5230\u201c<span style=\"color:#333333;font-family:&quot;background-color:#FFFFFF;\">1.01*1.01*1.01*0.99*0.99</span>\u201d\uff0c\u4e0d\u77e5\u9053\u4f55\u6765\u6b64\u95ee\uff1f","time":"4\u5929\u524d","position":1,"reward":null,"image_url":"avatars/User/1","username":"Coder","user_id":1,"user_login":"innov","can_delete":false,"id":1144}]},{"id":1112,"content":"\u8fd9\u4e2a\u5b9e\u8bad\u5f88\u8d5e\uff01","time":"10\u5929\u524d","position":2,"user_id":12,"reward":null,"image_url":"avatars/User/12","username":"\u9ec4\u4e95\u6cc9","user_login":"Hjqreturn","shixun_id":61,"hidden":false,"manager":false,"praise_count":1,"user_praise":false,"admin":false,"children":null},{"id":1057,"content":"\u4e0d\u9519","time":"20\u5929\u524d","position":2,"user_id":30403,"reward":null,"image_url":"avatars/User/b","username":"Jimmy","user_login":"p69243850","shixun_id":61,"hidden":false,"manager":false,"praise_count":1,"user_praise":false,"admin":false,"children":null},{"id":975,"content":"q","time":"1\u4e2a\u6708\u524d","position":2,"user_id":30403,"reward":null,"image_url":"avatars/User/b","username":"Jimmy","user_login":"p69243850","shixun_id":61,"hidden":false,"manager":false,"praise_count":1,"user_praise":false,"admin":false,"children":[{"content":"\u4e0b\u6b21\u518d\u53d1\u65e0\u610f\u4e49\u7684\u5185\u5bb9\uff0c\u5c06\u88ab\u6263\u91d1\u5e01\u54e6","time":"1\u4e2a\u6708\u524d","position":2,"reward":null,"image_url":"avatars/User/1","username":"Coder","user_id":1,"user_login":"innov","can_delete":false,"id":980}]},{"id":974,"content":"k","time":"1\u4e2a\u6708\u524d","position":2,"user_id":30403,"reward":null,"image_url":"avatars/User/b","username":"Jimmy","user_login":"p69243850","shixun_id":61,"hidden":false,"manager":false,"praise_count":0,"user_praise":false,"admin":false,"children":[{"content":"\u4e0b\u6b21\u518d\u53d1\u65e0\u610f\u4e49\u7684\u5185\u5bb9\uff0c\u5c06\u88ab\u6263\u91d1\u5e01\u54e6","time":"1\u4e2a\u6708\u524d","position":2,"reward":null,"image_url":"avatars/User/1","username":"Coder","user_id":1,"user_login":"innov","can_delete":false,"id":981}]},{"id":859,"content":"\u5f88\u68d2\uff01\uff01\uff01","time":"1\u4e2a\u6708\u524d","position":4,"user_id":1,"reward":null,"image_url":"avatars/User/1","username":"Coder","user_login":"innov","shixun_id":61,"hidden":false,"manager":false,"praise_count":0,"user_praise":false,"admin":false,"children":null},{"id":802,"content":"\u4e0d\u9519\uff01","time":"2\u4e2a\u6708\u524d","position":1,"user_id":30403,"reward":null,"image_url":"avatars/User/b","username":"Jimmy","user_login":"p69243850","shixun_id":61,"hidden":false,"manager":false,"praise_count":1,"user_praise":false,"admin":false,"children":null},{"id":619,"content":"\u770b\u6765\u8001\u5e08\u5bf9\u9f50\u7528\u7684\u90fd\u662f\u7a7a\u683c","time":"5\u4e2a\u6708\u524d","position":3,"user_id":29145,"reward":null,"image_url":"avatars/User/b","username":"yang","user_login":"m02945638","shixun_id":61,"hidden":false,"manager":false,"praise_count":1,"user_praise":false,"admin":false,"children":null},{"id":553,"content":"<p>\r\n\t666\r\n</p>\r\n<p>\r\n\t<br />\r\n</p>","time":"6\u4e2a\u6708\u524d","position":4,"user_id":26838,"reward":null,"image_url":"avatars/User/b","username":"\u5189\u529b","user_login":"m54013296","shixun_id":61,"hidden":false,"manager":false,"praise_count":0,"user_praise":false,"admin":false,"children":null}],"disscuss_count":183}
this.setState({
comments: data.children_list,
comment_count_without_reply: data.disscuss_count,
currentPage: page,
loadingComments: false,
})
return;
}
axios.get(url,
{
// withCredentials: true
}
).then((response) => {
// test
if (response.data) {
this.responseDataParse(response)
this.setState({
comments: response.data.comments,
comment_count_without_reply: response.data.disscuss_count,
currentPage: page,
loadingComments: false,
}, ()=>{
// keditor代码美化
window.prettyPrint()
})
}
// console.log(response)
}).catch((error) => {
console.log(error)
})
}
// 切换关卡时,清空state
clearCommentsInState = () => {
this.setState({ comments: [] })
}
fetchCommentIfNotFetched() {
if (this.state.comments && this.state.comments.length) {
return;
}
// http://localhost:3000/api/v1/shixuns/cz7yw3en/shixun_discusses?page=0
this.fetchComment();
}
// 新增父级评论
createNewComment() {
console.log('createNewComment...')
const { challenge, shixun, showSnackbar } = this.props;
let content = window._commentInput.html();
if (!content || content.length === 0) {
showSnackbar('必须填写内容!')
return;
}
// const url = `/api/v1/discusses`
const url = `/discusses.json`
if(content != undefined){
var beforeImage = content.split("<img");
var afterImage = content.split("/>");
if(beforeImage[0] == "" && afterImage[1] == ""){
window.notice_box('不支持纯图片评论<br/>请在评论中增加文字信息');
return;
}
}
$("#new_message_submit_btn_" + this.props.challenge.shixun_id).hide();
if (content) { // 去掉尾部的回车换行
content = content.replace(/(\n<p>\n\t<br \/>\n<\/p>)*$/g,'');
}
axios.post(url, {
container_type: "Shixun",
container_id: shixun.id,
challenge_id: challenge.id,
content,
position: challenge.position
},
{
// withCredentials: true
}
).then((response) => {
if (response.data.discuss) {
window._commentInput.html('');
window._commentInput.afterBlur()
// this.setState({
// runTesting: false,
// })
this.fetchComment()
}
console.log(response)
}).catch((error) => {
console.log(error)
})
return true;
}
_findCommentById(id, arg_comments) {
let comments;
if (!arg_comments) {
comments = this.state.comments;
} else {
comments = arg_comments;
}
for(let i = 0; i < comments.length; i++) {
if (id === comments[i].id) {
return i;
}
}
}
replyComment(commentContent, id, editor) {
// http://localhost:3000/api/v1/discusses/812/reply
const { challenge, shixun, user, showSnackbar, match } = this.props;
if (!commentContent || commentContent.length === 0) {
showSnackbar('必须填写内容!')
return;
}
// const url = `/api/v1/discusses/${id}/reply`
const url = `/discusses/${id}/reply.json`
if (commentContent) {
commentContent = commentContent.replace(/(\n<p>\n\t<br \/>\n<\/p>)*$/g,'');
}
if (!user.login && user.user_url) {
const loginAr = user.user_url.split('/')
user.login = loginAr[loginAr.length - 1]
}
axios.post(url, {
content: commentContent,
// TODO
"container_id": match.params.shixunId,
"container_type": "Shixun",
},
{
// withCredentials: true
}
).then((response) => {
if (response.data.discuss) {
let newDiscuss = response.data.discuss;
let { comments } = this.state;
var commentIndex = this._findCommentById(id);
let comment = comments[commentIndex]
comment = Object.assign({}, comment)
if (!comment.children ) {
comment.children = []
} else {
comment.children = comment.children.slice(0);
}
// TODO userName iamge_url
comment.children.push( {
"can_delete": true,
"content": commentContent,
"image_url": user.image_url,
"username": user.username,
"user_login": user.login,
"id": newDiscuss.id,
"position": newDiscuss.position,
"time": "1分钟前",
"praise_count": newDiscuss.praise_count,
"user_id": newDiscuss.user_id,
})
comments = comments.slice(0)
comments[commentIndex] = comment
// ke
editor.html && editor.html('')
// md
if (editor.setValue) {
editor.setValue('')
const $ = window.$
var view_selector = `.commentItemMDEditorView_${id}`
$(view_selector).hide();
}
this.setState({
// runTesting: false,
comments: comments
}, ()=>{
// keditor代码美化
editor.html && window.prettyPrint()
})
}
console.log(response)
}).catch((error) => {
console.log(error)
})
}
hiddenComment(item, childCommentId) {
const id = item.id
const { challenge, shixun, user, showSnackbar, match } = this.props;
// const url = `/api/v1/discusses/${id}/hidden`
const url = `/discusses/${id}/hidden.json`
const commentIndex = this._findCommentById(id);
const { comments } = this.state;
const comment = Object.assign({}, comments[commentIndex]);
axios.post(url, {
hidden: !comment.hidden ? "1" : "0",
container_identifier: match.params.shixunId || shixun.identifier
},
{
// withCredentials: true
}
).then((response) => {
if (response.data.status === -1) {
showSnackbar(response.data.message)
return;
}
if (response.data.status === 1) {
if (!childCommentId) {
comment.hidden = !comment.hidden;
const commentsCopy = this.state.comments.slice(0)
commentsCopy[commentIndex] = comment
this.setState({
comments: commentsCopy
})
} else { // TODO 目前子回复没hidden字段
let childCommentIndex = this._findCommentById(childCommentId, comments[commentIndex].children);
const childComment = comments[commentIndex].children[childCommentIndex]
childComment.hidden = !childComment.hidden;
this.setState({ comments })
}
}
// {"message":"Couldn't find Discuss with id=911","status":-1}
console.log(response)
}).catch((error) => {
console.log(error)
})
}
deleteComment(parrentComment, childCommentId) {
const { challenge, shixun } = this.props;
let deleteCommentId = parrentComment.id
if (childCommentId) {
deleteCommentId = childCommentId;
}
// const url = `/api/v1/discusses/${deleteCommentId}`
const url = `/discusses/${deleteCommentId}.json`
axios.delete(url,
{
// withCredentials: true
}
).then((response) => {
// TODO 删除成功或失败
if (response.data && response.data.status === 1) {
const commentIndex = this._findCommentById(parrentComment.id);
// https://stackoverflow.com/questions/29527385/removing-element-from-array-in-component-state
if (!childCommentId) {
this.setState((prevState) => ({
comments: update(prevState.comments, {$splice: [[commentIndex, 1]]})
}))
if (this.state.comments.length <= 5) {
this.fetchComment()
}
} else {
let comments = this.state.comments;
let newComments = Object.assign({}, comments)
let childCommentIndex = this._findCommentById(childCommentId, newComments[commentIndex].children);
newComments[commentIndex].children = update(newComments[commentIndex].children, {$splice: [[childCommentIndex, 1]]})
this.setState({ newComments })
}
}
console.log(response)
}).catch((error) => {
console.log(error)
})
}
rewardCode(parrentComment, childComment, amount) {
const { challenge, shixun, showSnackbar } = this.props;
let handleComment = parrentComment
if (childComment) {
handleComment = childComment;
}
let handleCommentId = handleComment.id;
// const url = `/api/v1/discusses/${handleCommentId}/reward_code`
const url = `/discusses/${handleCommentId}/reward_code.json`
axios.post(url, {
// id: handleCommentId,
// container_id: shixun.id,
container_type: 'Discusses',
score: amount,
user_id: handleComment.user_id
},
{
// withCredentials: true
}
).then((response) => {
if (response.data && response.data.code) {
const commentIndex = this._findCommentById(parrentComment.id);
let { comments } = this.state;
const newComments = comments.slice(0)
if (childComment) {
const childCommentIndex = this._findCommentById(handleComment.id, parrentComment.children);
const newChildComment = Object.assign({}, childComment);
newChildComment.reward = response.data.code
parrentComment = Object.assign({}, parrentComment)
parrentComment.children = parrentComment.children.slice(0)
parrentComment.children[childCommentIndex] = newChildComment
newComments[commentIndex] = parrentComment;
} else {
const newComment = Object.assign({}, newComments[commentIndex]);
newComment.reward = response.data.code;
newComments[commentIndex] = newComment;
}
this.setState({
comments: newComments
})
}
}).catch((error) => {
console.log(error)
showSnackbar('奖励失败,请联系系统管理员!')
})
}
// 评论点赞
commentPraise(discussId) {
const commentIndex = this._findCommentById(discussId);
const { comments } = this.state;
const { challenge } = this.props;
// const url = `/api/v1/discusses/${discussId}/plus`
const url = `/discusses/${discussId}/plus.json`
axios.post(url, {
// id: discussId,
// container_id: challenge.id,
container_type: 'Discuss', //Discuss
type: comments[commentIndex].user_praise === true ? 0 : 1, // "踩0;赞1"
},
{
// withCredentials: true
}
).then((response) => {
if (response.data.praise_count === 0 || response.data.praise_count) {
const newComments = comments.slice(0)
const comment = Object.assign({}, newComments[commentIndex])
comment.user_praise = !comment.user_praise;
comment.praise_count = response.data.praise_count;
newComments[commentIndex] = comment;
this.setState({
comments: newComments
})
}
console.log(response)
}).catch((error) => {
console.log(error)
})
}
newMessage() {
return; // newMessage 暂时不用了
const { shixun, myshixun } = this.props;
const url = `/api/v1/shixuns/${shixun.id}/new_message?container_type=Shixun&myshixun_id=${myshixun.id}`
// this.setState({
// gotNewReply: true
// })
axios.get(url,
{
// withCredentials: true
}
).then((response) => {
const new_message = response.data.new_message;
if (response.data.new_message) {
this.setState({
gotNewReply: new_message
})
}
// console.log(response)
}).catch((error) => {
console.log(error)
})
}
showNewReply() {
const { shixun, myshixun } = this.props;
const commentId = 929 // TODO
const url = `/api/v1/shixuns/${shixun.id}/anchor?container_type=Shixun&myshixun_id=${myshixun.id}&discuss_id=${commentId}`
this.setState({
loadingComments: true,
gotNewReply: false,
});
axios.get(url,
{
// withCredentials: true
}
).then((response) => {
const data = response.data;
const new_message = data.new_message;
this.setState({
comments: data.children_list,
comment_count_without_reply: data.disscuss_count,
currentPage: data.page,
loadingComments: false,
}, () => {
if (response.data.find_status) { // 没找到评论的话,默认会返回第一页的评论
const comment_J = window.$(`#reply_content_${commentId}`);
if (comment_J.length) {
comment_J[0].scrollIntoView()
comment_J.parents('.comment_item_cont').css('border', '1px solid #4CACFF')
}
}
})
console.log(response)
}).catch((error) => {
console.log(error)
})
}
// ----------------------------------------------------------------------------------------------评论 End
// ----------------------------------------------------------------------------------------------评论 End
componentDidMount() {
// commentsDelegateParent #game_left_contents #tab_con_4
$(".commentsDelegateParent")
.delegate(".J_Comment_Reply .comment_content img, .J_Comment_Reply .childrenCommentsView img","click", (event) => {
const imageSrc = event.target.src
// 非回复里的头像图片; 非emoticons
if (imageSrc.indexOf('/images/avatars/User') === -1 &&
imageSrc.indexOf('kindeditor/plugins/emoticons') === -1 ) {
this.setState({
showImage: true,
imageSrc,
})
}
});
}
onImageLayerClose = () => {
this.setState({
showImage: false,
imageSrc: '',
})
}
showNotification = (description, message = "提示", icon) => {
const data = {
message,
description
}
if (icon) {
data.icon = icon;
}
notification.open(data);
}
render() {
return (
<React.Fragment>
<ImageLayer {...this.state} onImageLayerClose={this.onImageLayerClose}></ImageLayer>
<WrappedComponent {...this.props} {...this.state}
createNewComment={this.createNewComment}
fetchCommentIfNotFetched={this.fetchCommentIfNotFetched}
clearCommentsInState={this.clearCommentsInState}
replyComment={this.replyComment}
deleteComment={this.deleteComment}
commentPraise={this.commentPraise}
hiddenComment={this.hiddenComment}
rewardCode={this.rewardCode}
onPaginationChange={this.onPaginationChange}
showNotification= { this.showNotification }
newMessage={this.newMessage}
showNewReply={this.showNewReply}
></WrappedComponent>
</React.Fragment>
)
}
}
}
/*
<div>
<h2>
HOC Debugger Component
</h2>
<p>
Props
</p>
<pre>{stringify(this.props)}</pre>
<p>
State
</p>
<pre>{stringify(this.state)}</pre>
{super.render()}
</div>
*/
@@ -0,0 +1,202 @@
import React, { Component } from 'react';
import ieImg from './imgs/internet-explorer@2x.png'
import chromeImg from './imgs/google-chrome@2x.png'
import ffImg from './imgs/firefox_icon.png'
import bannerImg from './imgs/icon@2x.png'
import { TPMIndexHOC } from '..//tpm/TPMIndexHOC';
class CompatibilityPage extends Component {
constructor(props) {
super(props)
this.state = {
}
}
onGoldRewardInputChange(event) {
this.setState({ goldRewardInput: event.target.value, goldRewardInputError: false });
}
render() {
const { goldRewardDialogOpen } = this.props;
const { goldRewardInputError } = this.state;
return (
<React.Fragment>
<style>{`
.Header {
overflow: hidden;
background-color: #fff;
box-shadow: 0 3px 4px rgba(0, 0, 0, 0.06);
}
.Header-inner {
width: 1008px;
margin: 0 auto;
padding: 0 16px;
}
.Header-logo {
width: 64px;
height: 30px;
margin: 15px 0;
}
.Header-logo img {
display: block;
width: 100%;
height: 100%;
}
.Compatibility {
margin-top: 60px;
background: #fff;
width: 1008px;
margin: 0 auto;
padding: 16px;
margin-bottom: 220px;
}
.Compatibility-icon {
width: 112px;
height: 83px;
margin: 64px auto;
}
.Compatibility-icon img {
display: block;
width: 100%;
height: 100%;
}
.Compatibility-tip {
font-size: 24px;
@mixin bold;
text-align: center;
}
.CompatibilityBrowsers {
overflow: hidden;
margin-top: 64px;
}
.CompatibilityBrowsers-item {
position: relative;
float: left;
width: 33%;
padding: 16px 0;
}
a.CompatibilityBrowsers-item {
text-decoration: none;
color: #3e7ac2;
}
.CompatibilityBrowsers-divider {
position: absolute;
right: 0;
top: 24px;
height: 64px;
border-right: 1px solid #ebeef5;
}
.CompatibilityBrowsers-icon {
width: 80px;
height: 80px;
margin: 0 auto;
}
.CompatibilityBrowsers-icon img {
display: block;
}
.CompatibilityBrowsers-ieIcon img {
width: 88px;
height: 81px;
margin-left: -8px;
margin-top: -1px;
}
.CompatibilityBrowsers-chromeIcon img {
width: 83px;
height: 83px;
margin: -1.5px;
}
.CompatibilityBrowsers-text {
margin-top: 32px;
text-align: center;
}
`}</style>
{/* <div className="Header">
<div className="Header-inner">
<div className="Header-logo">
<img src="https://testeduplus2.educoder.net/images/educoder/headNavLogo.png?1526520218"
></img>
</div>
</div>
</div> */}
<div className="Compatibility">
<div className="Compatibility-icon">
<img src={ bannerImg }
></img>
</div>
<div className="Compatibility-tip">你正在使用的浏览器版本过低将不能正常浏览和使用Educoder</div>
<div className="CompatibilityBrowsers">
{/* https://www.google.cn/intl/zh-CN/chrome/ */}
{/* <a href="http://www.google.com/chrome/" className="CompatibilityBrowsers-item">
srcset="/compatibility/images/internet-explorer.png 1x, /compatibility/images/internet-explorer@2x.png 2x, /compatibility/images/internet-explorer@3x.png 3x"
srcset="/compatibility/images/google-chrome.png 1x, /compatibility/images/google-chrome@2x.png 2x, /compatibility/images/google-chrome@3x.png 3x"
*/}
<a href="https://www.google.cn/intl/zh-CN/chrome/" className="CompatibilityBrowsers-item"
target="_blank">
<div className="CompatibilityBrowsers-icon CompatibilityBrowsers-chromeIcon">
<img src={ chromeImg }
>
</img>
</div>
<div className="CompatibilityBrowsers-text">使用 Google Chrome 浏览器</div>
<div className="CompatibilityBrowsers-divider"></div>
</a>
<a href="http://www.firefox.com.cn/" className="CompatibilityBrowsers-item"
target="_blank">
<div className="CompatibilityBrowsers-icon CompatibilityBrowsers-ieIcon">
<img src={ ffImg }
>
</img>
</div>
<div className="CompatibilityBrowsers-text">使用 Firefox 浏览器</div>
<div className="CompatibilityBrowsers-divider"></div>
</a>
<a href="https://www.microsoft.com/zh-cn/download/internet-explorer.aspx" className="CompatibilityBrowsers-item"
target="_blank">
<div className="CompatibilityBrowsers-icon CompatibilityBrowsers-ieIcon">
<img src={ ieImg }
>
</img>
</div>
<div className="CompatibilityBrowsers-text">升级 IE 浏览器</div>
</a>
</div>
</div>
</React.Fragment>
);
}
}
export default TPMIndexHOC ( CompatibilityPage );
@@ -0,0 +1,105 @@
import React, { Component } from 'react';
import Dialog, {
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
} from 'material-ui/Dialog';
import Button from 'material-ui/Button';
import { FormControl, FormHelperText } from 'material-ui/Form';
import Input, { InputLabel } from 'material-ui/Input';
import { InputNumber } from 'antd'
class RewardDialog extends Component {
constructor(props) {
super(props)
this.handleGoldRewardDialogClose = this.handleGoldRewardDialogClose.bind(this)
this.state = {
// goldRewardDialogOpen: false,
goldRewardInput: '',
}
}
showGoldRewardDialog(comment, childComment) {
if (comment.admin === true) {
this.comment = comment;
this.childComment = childComment;
this.setState({goldRewardDialogOpen: true})
}
}
handleGoldRewardDialogClose() {
this.props.setRewardDialogVisible(false)
}
onGoldRewardDialogOkBtnClick() {
console.log('onGoldRewardDialogOkBtnClick')
const { goldRewardInput } = this.state;
if (!goldRewardInput || goldRewardInput === '0' || goldRewardInput < 0) {
this.props.showNotification('奖励金币不能为空或负数')
// this.setState({ goldRewardInputError: true})
return;
} else {
this.props.setRewardDialogVisible( false )
this.props.rewardCode(goldRewardInput)
}
}
onGoldRewardInputChange(value) {
// || event.target.value
const number = parseInt(value || 0, 10);
if (Number.isNaN(number)) {
return;
}
this.setState({ goldRewardInput: number , goldRewardInputError: false });
}
render() {
const { goldRewardDialogOpen } = this.props;
const { goldRewardInputError } = this.state;
const goldRewardInputErrorObj = goldRewardInputError ? {'error': 'error'} : {}
return (
<Dialog
open={goldRewardDialogOpen}
disableEscapeKeyDown={true}
onClose={this.handleGoldRewardDialogClose}
className={'rewardDialog'}
>
<style>{`
.rewardDialog>div:last-child {
width: 280px;
}
`}</style>
<DialogTitle id="alert-dialog-title">{"奖励设置"}</DialogTitle>
<DialogContent>
{/* <FormControl { ...goldRewardInputErrorObj } aria-describedby="name-error-text"> */}
{/* <InputLabel htmlFor="goldReward">请输入奖励的金币数量</InputLabel>
<Input id="goldReward" type="number" value={this.state.goldRewardInput} onChange={(e) => this.onGoldRewardInputChange(e)} />
{ goldRewardInputError ? <FormHelperText id="name-error-text">奖励金币不能为空或负数</FormHelperText> : ''} */}
<InputNumber placeholder="请输入奖励的金币数量" id="goldReward" type="number" value={this.state.goldRewardInput}
onChange={(e) => this.onGoldRewardInputChange(e)} width={228} style={{ width: '228px'}} />
{/* </FormControl> */}
{/*<DialogContentText id="alert-dialog-description" style={{textAlign: 'center'}}> </DialogContentText>*/}
</DialogContent>
<DialogActions>
<Button onClick={this.handleGoldRewardDialogClose} color="primary">
取消
</Button>
<Button variant="raised"
onClick={() => this.onGoldRewardDialogOkBtnClick() } color="primary" autoFocus>
确定
</Button>
</DialogActions>
</Dialog>
);
}
}
export default RewardDialog;
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

@@ -0,0 +1,163 @@
import React, {Component} from 'react';
import competition from './comcss/competition.css';
import {getImageUrl} from 'educoder';
// 团队竞赛报名大图
class CompetitionMaxImg extends React.Component {
constructor(props) {
super(props)
this.state = {
GetenrollmentAPI: undefined
}
}
componentDidMount() {
}
componentDidUpdate = (prevProps) => {
if (prevProps.GetenrollmentAPI != this.props.GetenrollmentAPI) {
this.setState({
GetenrollmentAPI: this.props.GetenrollmentAPI,
})
}
}
render() {
let {type, pint} = this.props;
return (
<div>
<style>
{
`
`
}
</style>
{
type === 1 || type === 2 ?
<div className="registrationback"
style={{"background": `url(${getImageUrl(`images/educoder/competitions/tipregistit.jpg`)})`}}
>
<p className="registrationbackp1">在线竞赛平台</p>
<p className="registrationbackp2">在线竞赛平台是一个面向计算机类的互联网IT教育和实战平台</p>
<p className="registrationbackp3">提供企业级工程实训以实现工程化专业教学的自动化和智能化</p>
<div className="registrationbackp2button">
<div className="registbut1">
<p onClick={() => this.props.Jointheteam()}>加入战队</p>
</div>
<div className="registbut2">
<p onClick={() => this.props.Createateam()}>创建战队</p>
</div>
</div>
</div>
:
type === 3 ?
<div className="registrationback"
style={{"background": `url(${getImageUrl(`images/educoder/competitions/tipregistit.jpg`)})`}}
>
<p className="registrationbackp1">在线竞赛平台</p>
<p className="registrationbackp2">在线竞赛平台是一个面向计算机类的互联网IT教育和实战平台</p>
<p className="registrationbackp3">提供企业级工程实训以实现工程化专业教学的自动化和智能化</p>
<div className="registrationbackp2button">
<div className="registbut1">
<p onClick={() => this.props.Jointheteam()}>加入战队</p>
</div>
<div className="registbut2">
<p onClick={() => this.props.Createateam()}>创建战队</p>
</div>
</div>
</div>
:
type === 4 || type === 5 ?
<div className="registrationback"
style={{"background": `url(${getImageUrl(`images/educoder/competitions/tipregistit.jpg`)})`}}
>
<p className="registrationbackp1">在线竞赛平台</p>
<p className="registrationbackp2">在线竞赛平台是一个面向计算机类的互联网IT教育和实战平台</p>
<p className="registrationbackp3">提供企业级工程实训以实现工程化专业教学的自动化和智能化</p>
<div className="registrationbackp2button">
<div className="registbut1">
<p onClick={() => this.props.Jointheteam()}>加入战队</p>
</div>
<div className="registbut2">
<p onClick={() => this.props.Createateam()}>创建战队</p>
</div>
</div>
</div>
: type === 6 ?
<div className="registrationback"
style={{"background": `url(${getImageUrl(`images/educoder/competitions/tipregistit.jpg`)})`}}>
<p className="registrationbackp1">在线竞赛平台</p>
<p className="registrationbackp2">在线竞赛平台是一个面向计算机类的互联网IT教育和实战平台</p>
<p className="registrationbackp3">提供企业级工程实训以实现工程化专业教学的自动化和智能化</p>
<div className="registrationbackp2button">
{
pint === 1 ?
<div className="registbut1" onClick={() => this.props.Personalregistration()}>
<p onClick={() => this.props.Personalregistration()}>立即报名</p>
</div>
: pint === 2 ?
<div className="personreg1">
<p>已报名</p>
</div>
:
<div className="personreg1">
<p>报名已截止</p>
</div>
}
</div>
</div>
:
<div className="registrationback"
style={{"background": `url(${getImageUrl(`images/educoder/competitions/tipregistit.jpg`)})`}}>
</div>
}
</div>
)
}
}
export default CompetitionMaxImg;
// // type3
// /*<div className="registrationback"*/
// /* style={{"background": `url(${getImageUrl(`images/educoder/competitions/tipregistit.jpg`)})`}}>*/
// /* <p className="registrationbackp1">Educoder竞赛平台</p>*/
// /* <p className="registrationbackp4">高校智能课堂与综合实训平台</p>*/
// /* <div className="registrationbackp2button2">*/
// /* <div className="registbut11">*/
// /* <p onClick={() => this.props.Jointheteam()}>加入战队</p>*/
// /* </div>*/
// /* <div className="registbut22">*/
// /* <p onClick={() => this.props.Createateam()}>创建战队</p>*/
// /* </div>*/
//
// /* </div>*/
// /*</div>*/
//
//
// // type === 4 || type === 5
// // <div className="registrationback1"
// // style={{"background": `url(${getImageUrl(`images/educoder/competitions/tipregistit.jpg`)})`}}>
// // <p className="registrationbackp11">Educoder竞赛平台</p>
// // <p className="registrationbackp22">高校智能课堂与综合实训平台</p>
// // <div className="registrationbackp2button3">
// // <div className="registbut111">
// // <p onClick={() => this.props.Jointheteam()}>加入战队</p>
// // </div>
// // <div className="registbut222">
// // <p onClick={() => this.props.Createateam()}>创建战队</p>
// // </div>
// //
// // </div>
// // </div>
@@ -0,0 +1,50 @@
import React, {Component} from 'react';
import {
BrowserRouter as Router,
Route,
Switch
} from 'react-router-dom';
import axios from 'axios';
import moment from 'moment';
import competition from './comcss/competition.css';
import {Checkbox, Table, Pagination, Menu, Icon} from "antd";
import {getImageUrl} from 'educoder';
// 团队竞赛报名无报名子组件团队 在线竞赛 > 全国高校计算机大赛-项目挑战>
class RegisListview extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div className="reglistviewdivs">
<div className="reglistviewdivss " style={{
width: "100%",
display: "flex",
justifyContent: " space-around",
alignItems: "center",
background: `url(${getImageUrl(`images/educoder/competitions/Rectanglex.png`)})`,
height: "50px",
backgroundPosition: "center",
backgroundSize: "110% 100%",
}}>
<p className=" "
style={{fontSize: "16px", color: "#05101A", width: "79px", textAlign: "center"}}>创建者</p>
<p className=" "
style={{fontSize: "16px", color: "#05101A", width: "160px", textAlign: "center"}}>战队名称</p>
<p className=" "
style={{fontSize: "16px", color: "#05101A", width: "487px", textAlign: "center"}}>战队成员</p>
<p className=" "
style={{fontSize: "16px", color: "#05101A", width: "134px", textAlign: "center"}}>学校</p>
<p className=" "
style={{fontSize: "16px", color: "#05101A", width: "151px", textAlign: "center"}}>时间</p>
</div>
</div>
)
}
}
export default RegisListview;
@@ -0,0 +1,170 @@
import React, {Component} from 'react';
import {
BrowserRouter as Router,
Route,
Switch
} from 'react-router-dom';
import axios from 'axios';
import moment from 'moment';
import {SnackbarHOC, WordsBtn, getImageUrl} from 'educoder';
import {TPMIndexHOC} from '../tpm/TPMIndexHOC';
import competition from './comcss/competition.css';
import {Button} from 'antd';
// 团队竞赛报名无报名子组件团队 竞赛报名-已创建战队
class RegisListviewdata extends React.Component {
constructor(props) {
super(props)
this.state = {
item: undefined
}
}
componentDidMount() {
////console.log("RegisListviewdata");
////console.log(this.props.item)
this.setState({
item: this.props.item
})
}
getotiku = (url) => {
window.open(url, '_blank');
}
render() {
const {item} = this.props;
return (
<div>
{
item !== undefined ?
<div className={"yslborderbottom"}>
<div className="regitem22">
<div className="regitemimg1 ">
<img className="regitemimg2" src={getImageUrl("images/" + item.creator.image_url)}>
</img>
<a className="maxnamewidth78" title={item.creator.name} style={{
color: "#999999", fontSize: "14px",
width: "78px",
textAlign: "center"
}}>{item.creator.name}</a>
</div>
<div style={{
marginTop: "29px",
marginLeft: "37px",
width: "160px",
textAlign: "center",
}} className="textsize maxnamewidth160">
{
this.props.admin?
this.props.admin===true?
<a className="maxnamewidth160" title={item.name} style={{
fontSize: "16px",
width: "160px",
textAlign: "center",
cursor:"pointer"
}}
onClick={()=>this.getotiku(`/competitions/${this.props.match.params.identifier}/competition_teams/${item.id}`)}
>{item.name}</a>
: <p className="maxnamewidth160" style={{
color: "#05101A",
fontSize: "16px",
width: "160px",
textAlign: "center"
}}>{item.name}</p>
: <p className="maxnamewidth160" style={{
color: "#05101A",
fontSize: "16px",
width: "160px",
textAlign: "center"
}}>{item.name}</p>
}
</div>
<div style={{
marginLeft: "37px",
display: "flex",
flexDirection: "initial",
width: "487px"
}}>
{
item && item.team_members.map((item, index) => {
return (
index === 0 ?
<img className="regitemimgs" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 1 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 2 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 3 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 4 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 5 ?
<div>
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
<img className="regitemimgs22"
src={getImageUrl(`images/educoder/competitions/pexjiazai.png`)}>
</img>
</div>
: ""
)
})
}
</div>
<div style={{
marginLeft: "41px",
width: "134px",
marginTop: "29px",
textAlign: "center",
}} className="maxnamewidth134">
<a className="maxnamewidth134" title={item.school_name} style={{
color: "#05101A",
fontSize: "16px",
textAlign: "center"
}}>{item.school_name}</a>
</div>
<div style={{
marginLeft: "37px",
width: "151px",
marginTop: "29px",
}}>
<p style={{
color: "#999999",
fontSize: "16px",
textAlign: "center"
}}>{item.created_at}</p>
</div>
</div>
</div>
: ""
}
</div>
)
}
}
export default RegisListviewdata;
@@ -0,0 +1,34 @@
import React, {Component} from 'react';
import competition from './comcss/competition.css';
import {getImageUrl} from 'educoder';
// 团队竞赛报名无报名子组件
class RegisNodata extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div className="bootom">
<div className="bootomimg" style={{
height: "80px",
width: "125px",
marginTop: "107px",
background: `url(${getImageUrl(`images/educoder/competitions/Noentry.jpg`)})`,
backgroundPosition: "center",
backgroundSize: "110% 100%",
}}>
</div>
<p className="bootomtext">暂无战队参与报名哦赶紧来成为第一个挑战的吧~</p>
</div>
)
}
}
export default RegisNodata;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,75 @@
import React, {Component} from 'react';
import {
BrowserRouter as Router,
Route,
Switch
} from 'react-router-dom';
import axios from 'axios';
import moment from 'moment';
import competition from './comcss/competition.css';
import {Checkbox, Input, Table, Pagination, Menu, Icon} from "antd";
const Search = Input.Search;
// 团队竞赛报名无报名子组件团队 在线竞赛 > 全国高校计算机大赛-项目挑战>
class RegistrationSearch extends React.Component {
constructor(props) {
super(props)
this.state = {
keywords: ""
}
}
setdatafunsval = (e) => {
this.setState({
keywords: e.target.value
})
};
setdatafuns = (value) => {
//console.log("setdatafuns点击了搜索");
//console.log(value);
this.setState({
keywords: value
})
this.props.RegistrationSearchvalue(value);
};
myonPressEnter = (e) => {
//console.log("点击了回车setdatafunsval点击了搜索");
//console.log(e.target.value);
this.props.RegistrationSearchvalue(e.target.value);
}
render() {
return (
<div className="searchhead">
<Search
className="packinputs"
value={this.state.keywords}
enterButton={<span>搜索</span>}
onInput={(e) => this.setdatafunsval(e)}
onSearch={(value) => this.setdatafuns(value)}
onPressEnter={(e) => this.myonPressEnter(e)}
/>
<p style={{
width: "583px",
color: "#07140E",
fontSize: '16px',
marginTop: " 7px",
marginLeft: "300px",
textAlign: "right",
lineHeight: " 24px"
}}>战队总数<span
style={{color: "#459BE5", fontSize: "16px"}}>{this.props.count}</span><span
style={{marginLeft: "5px", marginRight: "15px",}}></span> <span
style={{color: "#459BE5"}}>{this.props.members_count}</span><span
style={{marginLeft: "5px"}}></span></p>
</div>
)
}
}
export default RegistrationSearch;
@@ -0,0 +1,84 @@
import React, {Component} from 'react';
import {
BrowserRouter as Router,
Route,
Switch
} from 'react-router-dom';
import axios from 'axios';
import moment from 'moment';
import {SnackbarHOC, WordsBtn, getImageUrl} from 'educoder';
import {TPMIndexHOC} from '../tpm/TPMIndexHOC';
import competition from './comcss/competition.css';
import {Button} from 'antd';
import RegisListviewdata from "./RegisListviewdata";
// 团队竞赛报名无报名子组件团队 竞赛报名-已创建战队
class Registrationitem extends React.Component {
constructor(props) {
super(props)
}
render() {
let {item} = this.props;
return (
<div className="yslborderbottom">
{
item !== undefined ?
<div className="regitem2">
<div className="perregitemimg1 ">
<img className="personregitemimg" src={getImageUrl("images/" + item.creator.image_url)}>
</img>
</div>
<div className="" style={{
marginTop: "19px",
marginLeft: "54px",
display: "flex",
flexDirection: "initial",
}}>
<p className="" style={{
color: "#05101A",
fontSize: "16px",
width: "160px",
textAlign: "center"
}}>{item.creator.name}</p>
{
item.manage_permission === true ?
<p className="" style={{
color: "#459BE5",
fontSize: "16px",
textAlign: "center",
marginLeft: "25px",
}}>已报名</p>
:
""
}
</div>
<div className="" style={{
marginLeft: "632px",
width: "151px",
marginTop: "19px",
marginRight: "35px",
}}>
<p style={{
color: "#999999",
fontSize: "16px",
textAlign: "center",
}}>{item.created_at}</p>
</div>
</div>
: ""
}
</div>
)
}
}
export default Registrationitem;
@@ -0,0 +1,900 @@
/*All*/
.borders {
border: 0.5px solid;
}
.borders2 {
border: 1px solid #D9D9D9;
}
.newMainybot {
padding-bottom: 0px !important;
}
/*All*/
/*Registration.js*/
/*.registrationback {*/
/* height: 368px;*/
/* width: 1200px;*/
/* border: 0.5px solid;*/
/* display: flex;*/
/* display: -webkit-flex;*/
/* flex-direction: column;*/
/* align-items: center;*/
/* background:url(../../../../../images/regis/tipregistit.jpg)*/
/*}*/
.registrationbackcenter {
display: flex;
display: -webkit-flex;
flex-direction: column;
align-items: center;
}
/*.registrationback1 {*/
/* height: 368px;*/
/* width: 1200px;*/
/* border: 0.5px solid;*/
/* display: flex;*/
/* flex-direction: column;*/
/* background:url(../../../../../images/regis/tipregistit.jpg)*/
/*}*/
.registrationbackp1 {
color: #ffffff;
font-size: 42px;
margin-top: 80px;
line-height: 42px;
font-weight: bold;
}
.registrationbackp11 {
color: #ffffff;
font-size: 48px;
margin-top: 71px;
line-height: 48px;
font-weight: bold;
margin-left: 251px;
}
.registrationbackp2 {
color: #ffffff;
font-size: 18px;
margin-top: 16px;
line-height: 20px;
}
.registrationbackp22 {
margin-left: 251px;
color: #ffffff;
font-size: 36px;
margin-top: 25px;
line-height: 36px;
}
.registrationbackp3 {
color: #ffffff;
font-size: 18px;
line-height: 20px;
margin-top: 7px;
}
.registrationbackp4 {
color: #ffffff;
font-size: 26px;
margin-top: 25px;
line-height: 26px;
}
.registrationbackp5 {
color: #ffffff;
font-size: 26px;
margin-top: 25px;
}
.registrationbackp2button {
display: flex;
align-items: center;
margin-top: 36px;
}
.registrationbackp2button2 {
display: flex;
align-items: center;
margin-top: 40px;
}
.registrationbackp2button3 {
display: flex;
align-items: center;
margin-top: 44px;
margin-left: 251px;
}
.registbut1 {
margin-right: 46px;
text-align: center;
background: #0BDEE6;
border-radius: 10px;
height: 54px;
width: 156px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer
}
.personreg1 {
margin-right: 46px;
text-align: center;
background: #C3C1C1;
height: 54px;
width: 156px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer
}
.registbut11 {
margin-right: 46px;
text-align: center;
font-size: 16px;
color: #ffffff;
height: 48px;
width: 156px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 1px;
border-style: solid;
border-color: #ffffff;
}
.registbut111 {
margin-right: 46px;
text-align: center;
color: #ffffff;
height: 41px;
width: 146px;
font-size: 16px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 2px;
border-style: solid;
border-color: #ffffff;
}
.registbut1 p {
width: 100%;
height: 100%;
color: #ffffff;
line-height: 54px;
font-size: 20px;
font-weight: bold;
cursor: pointer
}
.registbut11 p {
width: 100%;
height: 100%;
line-height: 48px;
}
.registbut22 p {
width: 100%;
height: 100%;
line-height: 48px;
}
.registbut2 p {
line-height: 54px;
width: 100%;
height: 100%;
color: #ffffff;
font-size: 20px;
font-weight: bold;
cursor: pointer
}
.personreg1 p {
color: #ffffff;
font-size: 20px;
cursor: not-allowed;
}
.registbut111 p {
width: 100%;
height: 100%;
line-height: 38px;
}
.registbut222 p {
width: 100%;
height: 100%;
line-height: 38px;
}
.registbut2 {
text-align: center;
color: #05101A;
font-size: 20px;
background: #F8EC25;
height: 54px;
width: 156px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer
}
.registbut22 {
text-align: center;
color: #ffffff;
font-size: 16px;
height: 48px;
width: 156px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 1px;
border-style: solid;
border-color: #ffffff;
}
.registbut222 {
text-align: center;
color: #ffffff;
font-size: 16px;
height: 41px;
width: 146px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 2px;
border-style: solid;
border-color: #ffffff;
}
.bootom {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.bootomimg {
height: 80px;
width: 125px;
margin-top: 107px;
}
.bootomtext {
color: #999999;
font-size: 16px;
margin-top: 33px;
}
/*Registration.js*/
/*Registrationitem.js*/
/*横向*/
.regitem {
display: flex;
flex-direction: initial;
margin-top: 44px;
}
.myregitem11 {
display: flex;
flex-direction: initial;
margin-top: 20px;
}
.regitem22 {
display: flex;
flex-direction: initial;
margin-top: 27px;
margin-bottom: 19px;
}
.yslborderbottom {
border-bottom: 1px solid #EDEDED;
}
.registrationback {
height: 368px;
width: 1200px;
display: flex;
display: -webkit-flex;
flex-direction: column;
align-items: center;
}
.registrationback1 {
height: 368px;
width: 1200px;
display: flex;
flex-direction: column;
}
.regitem2 {
display: flex;
flex-direction: initial;
padding-bottom: 18px;
margin-top: 19px;
}
/*垂直*/
.regitemimg1 {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-left: 17px;
}
.perregitemimg1 {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-left: 26px;
}
.regitemimg2 {
height: 78px;
width: 78px;
background-color: transparent;
margin-bottom: 9px;
border-radius: 50%;
}
.personregitemimg {
height: 64px;
width: 64px;
border-radius: 50%;
}
.regitemimg2 p {
border: 0.5px solid;
color: #999999;
font-size: 14px;
}
.regitemimgs {
width: 69px;
height: 69px;
margin-top: 3px;
background-color: transparent;
border-radius: 50%;
}
.regitemimgs2 {
margin-top: 16px;
width: 49px;
height: 51px;
margin-left: 25px;
border-radius: 50%;
}
.regitemimgs22 {
margin-top: 27px;
width: 28px;
height: 28px;
margin-left: 20px;
border-radius: 50%;
}
.regitemimgs222 {
margin-top: 12px;
width: 28px;
height: 28px;
margin-left: 20px;
border-radius: 50%;
}
.regitemimgs3 {
/* border: 0.5px solid; */
height: 22px;
width: 22px;
font-size: 21px !important;
margin-top: 19px;
color: #1C91E8;
}
.regitemimgs4 {
width: 156px;
display: flex;
flex-direction: row-reverse;
margin-top: 18px;
}
.regitemimgs444 {
display: flex;
flex-direction: row-reverse;
margin-top: 18px;
}
.regitemimgs5 {
margin-left: 10px;
text-align: center;
background: #ffffff;
height: 40px;
width: 72px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
border: 1px;
border-style: solid;
border-color: #459BE5;
cursor: pointer
}
.regitemimgs555 {
margin-left: 33px;
text-align: center;
background: #ffffff;
height: 40px;
width: 72px;
color: #459be5;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
border: 1px;
border-style: solid;
border-color: #459BE5;
cursor: pointer
}
.regitemimgs555 p {
line-height: 39px;
color: #459be5;
}
.regitemimgs6 {
text-align: center;
background: #ffffff;
height: 40px;
width: 72px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
border: 1px;
cursor: pointer;
border-style: solid;
border-color: #459BE5;
}
.regitemimgs5 p {
color: #459BE5;
font-size: 14px;
cursor: pointer;
line-height: 39px;
}
.regitemimgs6 p {
color: #459BE5;
font-size: 14px;
cursor: pointer;
line-height: 39px;
}
/*Registrationitem.js*/
/*RegistrationSearch.js*/
.searchhead {
display: flex;
flex-direction: initial;
margin-top: 24px;
}
.packinputs button {
background: #459BE5;
width: 100% !important;
}
.packinputs {
width: 317px;
height: 34px;
}
/*RegistrationSearch.js*/
.reglistviewdiv {
display: flex;
flex-direction: initial;
margin-top: 25px;
}
/*RegisListview.js*/
.reglistviewdivs {
margin-top: 25px;
}
.reglistviewdivss {
display: flex;
flex-direction: initial;
}
.reglistviewdivss2 {
display: flex;
flex-direction: initial;
}
.reglistviewdivss2c {
display: flex;
flex-direction: initial;
cursor: not-allowed;
}
.reglistviewdivss2p {
width: 90px;
font-size: 12px;
color: #666666;
text-align: center;
}
.reglistviewdivss4p {
width: 90px;
font-size: 12px;
color: #666666;
text-align: center;
}
.reglistviewdivss5p {
width: 110px;
font-size: 12px;
color: #666666;
text-align: center;
}
.reglistviewdivss33p {
width: 25px;
font-size: 12px;
color: #666666;
text-align: center;
}
.reglistviewdivss333p {
width: 31px;
font-size: 12px;
color: #666666;
text-align: center;
}
.reglistviewdivss3p {
width: 31px;
font-size: 12px;
color: #666666;
text-align: center;
}
reglistviewdivs2 {
margin-top: 27px;
}
/*RegisListview.js*/
/*RegisListviewdata.js*/
.reglistimg1 {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.reglistimg2 {
border: 0.5px solid;
height: 78px;
width: 78px;
}
/*RegisListviewdata.js*/
/*PersonModal.js*/
.yslanswerList {
}
.yslanswerList li {
}
.yslanswerList li:hover {
background-color: #F6F4F4;
}
.permaindiv {
display: flex;
flex-direction: column;
}
.demo-loading-container {
position: absolute;
bottom: 40px;
width: 100%;
text-align: center;
}
.demo-infinite-container {
border-radius: 2px;
overflow: auto;
height: 215px;
width: 485px;
}
.demo-infinite-containerdiv {
margin-top: 12px;
}
.demo-infinite-containerdiv2 {
margin-top: 24px;
}
.backgroundspersondiv {
background: #ffffff;
}
.cpersondiv1 {
height: 161px;
width: 410px;
}
.demo-infinite-container2 {
border-radius: 2px;
overflow: auto;
height: 161px;
width: 410px;
}
.demo-infinite-container33 {
border-radius: 2px;
height: 161px;
width: 410px;
}
.cpersondiv1Items {
color: #05101A;
font-size: 12px;
}
.personbut1 {
background: #F2F2F2;
border-color: #F2F2F2;
margin-right: 26px;
width: 120px;
height: 38px;
color: #4A4A4A;
font-size: 16px;
border-radius: 3px;
cursor: pointer;
}
.personbut1 p {
width: 100%;
height: 100%;
text-align: center;
margin-top: 10px;
color: #4A4A4A;
font-size: 16px;
cursor: pointer;
}
.personbut2 {
background: #459BE5;
border-color: #459BE5;
margin-right: 26px;
width: 120px;
height: 38px;
color: #ffffff;
font-size: 16px;
border-radius: 3px;
cursor: pointer;
}
.personbut2 p {
width: 100%;
height: 100%;
text-align: center;
margin-top: 10px;
color: #ffffff;
font-size: 16px;
cursor: pointer;
}
/*PersonModal.js*/
/*PersonalModalteam.js*/
.personaldiv {
display: flex;
display: -webkit-flex;
flex-direction: column;
align-items: center;
}
.personaldivbutt1 {
background: #F2F2F2;
border-color: #F2F2F2;
margin-right: 23px;
width: 100px;
height: 38px;
color: #4A4A4A;
font-size: 16px;
border-radius: 3px;
cursor: pointer;
}
.personaldivbutt1 p {
width: 100%;
height: 100%;
text-align: center;
margin-top: 10px;
color: #666666;
font-size: 16px;
cursor: pointer;
}
.personaldivbutt2 {
background: #459BE5;
border-color: #459BE5;
width: 100px;
height: 38px;
color: #ffffff;
font-size: 16px;
border-radius: 3px;
cursor: pointer;
}
.personaldivbutt2 p {
width: 100%;
height: 100%;
text-align: center;
margin-top: 10px;
color: #ffffff;
font-size: 16px;
cursor: pointer;
}
.task-btn-orange {
background: #4CACFF !important;
color: #fff !important;
}
.task-btn {
cursor: pointer;
display: inline-block;
border: none;
padding: 0 12px;
letter-spacing: 1px;
text-align: center;
font-size: 14px;
height: 30px;
line-height: 30px;
border-radius: 2px;
}
/*PersonalModalteam.js*/
/*文字长度限制*/
.maxnamewidth100 {
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.maxnamewidth78 {
width: 78px !important;
max-width: 78px !important;
min-width: 78px !important;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.maxnamewidth85 {
max-width: 85px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.maxnamewidth90 {
max-width: 90px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.maxnamewidth110 {
max-width: 110px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.maxnamewidth160 {
width: 160px !important;
min-width:160px !important;
max-width: 160px !important;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.maxnamewidth134 {
max-width: 134px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
/*文字长度限制?*/
.textsize{
width: 160px !important;
min-width: 160px !important;
max-width: 160px !important;
text-align: center;
}
@@ -0,0 +1,59 @@
import React, {Component} from 'react';
import {getImageUrl} from 'educoder';
import {Modal, Input, Spin, Tooltip, Icon, Dropdown, Button} from 'antd';
import axios from 'axios';
import competition from '../comcss/competition.css';
import Registrationitem from "../Registrationitem";
import InfiniteScroll from 'react-infinite-scroller';
// import PersonModaltion from "./PersonModaltion";
const {Search} = Input;
//退出战队
class ExittheteamModel extends React.Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
const {
addonAfter, test, test3, Numberofteammentors, Thecurrentnumber, person1, person2
} = this.state;
//Modal
//keyboard是否支持键盘 esc 关闭
//closable 是否显示右上角的关闭按钮
//底部内容,当不需要默认底部按钮时,可以设为 footer={null}
//destroyOnClose 关闭时销毁 Modal 里的子元素
//centered 垂直居中展示 Modal
//visible 弹出框是否显示
return (
<Modal
keyboard={false}
closable={false}
footer={null}
destroyOnClose={true}
title={this.props.messageexit}
centered={true}
visible={this.props.messageexitol === undefined ? false : this.props.messageexitol}
width="480px"
>
<div className="task-popup-content">
<div className="task-popup-text-center font-14">{this.props.exitintpermessages}</div>
</div>
<div className="task-popup-submit clearfix">
<a className="pop_close task-btn mb10 mr40 colorFFF"
onClick={() => this.props.Exittheteam(false)}>取消</a>
<a className="task-btn task-btn-orange fr" onClick={() => this.props.Exittheteam(true)}>确定</a>
</div>
</Modal>
)
}
}
export default ExittheteamModel;
@@ -0,0 +1,63 @@
import React, {Component} from 'react';
import {getImageUrl} from 'educoder';
import {Modal, Input, Spin, Tooltip, Icon, Dropdown, Button} from 'antd';
import axios from 'axios';
import competition from '../comcss/competition.css';
import Registrationitem from "../Registrationitem";
import InfiniteScroll from 'react-infinite-scroller';
// import PersonModaltion from "./PersonModaltion";
const {Search} = Input;
class MessagePersonModal extends React.Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
const {
addonAfter, test, test3, Numberofteammentors, Thecurrentnumber, person1, person2
} = this.state;
//Modal
//keyboard是否支持键盘 esc 关闭
//closable 是否显示右上角的关闭按钮
//底部内容,当不需要默认底部按钮时,可以设为 footer={null}
//destroyOnClose 关闭时销毁 Modal 里的子元素
//centered 垂直居中展示 Modal
//visible 弹出框是否显示
return (
<Modal
keyboard={false}
closable={false}
footer={null}
destroyOnClose={true}
title={this.props.messagePer}
centered={true}
visible={this.props.messagePerbool === undefined ? false : this.props.messagePerbool}
width="480px"
>
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}}
>
<div style={{width: "100%", textAlign: "center"}}
className="task-popup-text-center font-16 ">{this.props.intpermessages}</div>
<div style={{textAlign: "center", marginTop: "30px"}}
className="task-popup-text-center task-btn task-btn-orange"
onClick={() => this.props.messagePerboolbuton()}>确认
</div>
</div>
</Modal>
)
}
}
export default MessagePersonModal;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,50 @@
import React, {Component} from 'react';
import {
BrowserRouter as Router,
Route,
Switch
} from 'react-router-dom';
import axios from 'axios';
import moment from 'moment';
import competition from './comcss/competition.css';
import {Checkbox, Table, Pagination, Menu, Icon} from "antd";
// 团队竞赛报名无报名子组件团队 在线竞赛 > 全国高校计算机大赛-项目挑战>
class PersonModaltion extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div className="reglistviewdivs">
<div className="reglistviewdivss " style={{
width: "100%",
display: "flex",
justifyContent: " space-around",
alignItems: "center",
height: "50px",
backgroundPosition: "center",
backgroundSize: "110% 100%",
backgroundColor: "#E0E0E0",
}}>
<p className="borders"
style={{fontSize: "16px", color: "#05101A", textAlign: "center"}}>姓名</p>
<p className="borders"
style={{fontSize: "16px", color: "#05101A", textAlign: "center"}}>角色</p>
<p className="borders"
style={{fontSize: "16px", color: "#05101A", textAlign: "center"}}>单位</p>
<p className="borders"
style={{fontSize: "16px", color: "#05101A", textAlign: "center"}}>其他</p>
<p className="borders"
style={{fontSize: "16px", color: "#05101A", textAlign: "center"}}>操作</p>
</div>
</div>
)
}
}
export default PersonModaltion;
@@ -0,0 +1,146 @@
import React, {Component} from 'react';
import {getImageUrl} from 'educoder';
import {Modal, Input, Spin, Tooltip, Icon, Dropdown, Button} from 'antd';
import axios from 'axios';
import competition from '../comcss/competition.css';
import Registrationitem from "../Registrationitem";
import InfiniteScroll from 'react-infinite-scroller';
// import PersonModaltion from "./PersonModaltion";
const {Search} = Input;
//立即申请试用
class PersonalModalteam extends Component {
constructor(props) {
super(props);
this.state = {
yslzxueshiskmcdm1: "",
}
}
Tmoconfirmto = () => {
try {
if (this.state.yslzxueshiskmcdm1) {
if (this.state.yslzxueshiskmcdm1.length === 0) {
this.props.showNotification(`请输入您的邀请码`);
return
}
}
} catch (e) {
}
let url = `/competitions/${this.props.match.params.identifier}/competition_teams/join.json`;
axios.post(url, {
invite_code: this.state.yslzxueshiskmcdm1
}).then((result) => {
if (result) {
if (result.data) {
try {
if (result.data.status === 0) {
this.props.showNotification(`报名成功,预祝您夺得桂冠`);
}
} catch (e) {
}
this.props.Tmoconfirm(true)
}
}
}).catch((error) => {
});
}
studentsonChange = (e) => {
this.setState({
yslzxueshiskmcdm1: e.target.value,
});
}
render() {
const {
addonAfter, test, test3, Numberofteammentors, Thecurrentnumber, person1, person2
} = this.state;
//Modal
//keyboard是否支持键盘 esc 关闭
//closable 是否显示右上角的关闭按钮
//底部内容,当不需要默认底部按钮时,可以设为 footer={null}
//destroyOnClose 关闭时销毁 Modal 里的子元素
//centered 垂直居中展示 Modal
//visible 弹出框是否显示
return (
<Modal
keyboard={false}
closable={false}
footer={null}
destroyOnClose={true}
title="加入战队"
centered={true}
visible={this.props.tmodalsTypes === undefined ? false : this.props.tmodalsTypes}
width="600px"
>
<div className="personaldiv">
<div style={{
display: "flex",
flexDirection: "initial",
marginTop: "10px",
}}>
<p style={{
width: "67px",
fontSize: "16px",
color: '#05101A',
height: "40px",
lineHeight: "40px",
}}>
邀请码
</p>
<style>
{
`
.yslzxueshiskmcdm1 {
height: 40px !important;
width: 329px !important;
}
`
}
</style>
<Input className={"yslzxueshiskmcdm1"} value={this.state.yslzxueshiskmcdm1}
onChange={(e) => this.studentsonChange(e)} placeholder="请输入您的邀请码"/>
</div>
<div style={{
marginTop: "48px",
display: "flex",
alignItems: "center",
paddingBottom: "12px",
}}>
<div style={{
display: "flex",
justifyContent: "center",
width: "100%"
}}>
<div className="personaldivbutt1">
<p onClick={() => this.props.Tmoconfirm(false)}>取消</p>
</div>
<div className="personaldivbutt2">
<p onClick={() => this.Tmoconfirmto()}>确定</p>
</div>
</div>
</div>
</div>
</Modal>
)
}
}
export default PersonalModalteam;
@@ -0,0 +1,97 @@
import React, {Component} from 'react';
import {SnackbarHOC, WordsBtn} from 'educoder';
import {TPMIndexHOC} from '../../tpm/TPMIndexHOC';
import competition from '../comcss/competition.css';
import {Button, Pagination,} from 'antd';
import CompetitionMaxImg from '../CompetitionMaxImg';
import Registrationitem from '../Registrationitem';
// 团队竞赛报名无报名
class PersonalCompetit extends React.Component {
constructor(props) {
super(props)
this.state = {
loadingstate: false,
page: 1,
limit: 20,
type: 6,
pint: 1,
test: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
}
}
paginationonChangestwo = (pageNumber) => {
this.setState({
page: pageNumber,
loadingstate: true,
})
}
render() {
const {test, page, limit, type, pint} = this.state;
const listItems = test.map((item, index) =>
<Registrationitem></Registrationitem>
);
return (
<div className="newMain clearfix ">
<div className={"educontent mb20"} style={{width: "1200px", marginTop: "26px"}}>
<div className="educontent mb20 ">
<p className="clearfix mb20 mt10">
<a className="btn colorgrey fl hovercolorblue ">在线竞赛</a>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<a
className=" btn colorgrey fl hovercolorblue ">全国高校计算机大赛-项目挑战</a>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn className="fl">报名</WordsBtn>
</p>
</div>
{/*大图*/}
{
type === 6 ?
<CompetitionMaxImg type={type} pint={pint}></CompetitionMaxImg>
:
""
}
{
pint === 1 || pint === 3 ?
<div style={{
marginTop: "22px"
}}>
<p>参赛总人数<span style={{color: "#459BE5"}}>132</span><span style={{marginLeft: "5px"}}></span></p>
</div>
: ""}
{/*列表*/}
{
pint === 1 || pint === 3 ?
<div
style={{
marginTop: "31px"
}}
>
{
listItems
}
</div>
: ""}
{
pint === 1 || pint === 3 ?
<div className="edu-txt-center ysyslxh mt56 " style={{marginBottom: "192px",}}>
<Pagination showQuickJumper current={page}
onChange={this.paginationonChangestwo} pageSize={limit}
total={30}></Pagination>
</div>
: ""
}
</div>
</div>
)
}
}
export default SnackbarHOC()(TPMIndexHOC(PersonalCompetit));
@@ -0,0 +1,445 @@
import React, {Component} from 'react';
import {
BrowserRouter as Router,
Route,
Switch
} from 'react-router-dom';
import axios from 'axios';
import moment from 'moment';
import {SnackbarHOC, WordsBtn, getImageUrl} from 'educoder';
import {TPMIndexHOC} from '../../tpm/TPMIndexHOC';
import competition from '../comcss/competition.css';
import {Button, message, Tooltip} from 'antd';
// 点击按钮复制功能
function jsCopy(s) {
var e = document.getElementById(s);
e.select();
document.execCommand("Copy");
codesuccess()
// const range=document.createRange();
// window.getSelection().removeAllRanges();
// range.selectNode(e.target);
// window.getSelection().addRange(range);
// const successful =document.execCommand('copy');
// if(successful){
// codesuccess()
// }
}
function codesuccess() {
message.success('复制成功');
};
// 团队竞赛报名无报名子组件团队 竞赛报名-已创建战队
class PersonalCompetititem extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
// // ////console.log(this.props.data)
}
componentDidUpdate = (prevProps) => {
//
// if (prevProps.data != this.props.data) {
//
// }
}
render() {
const {key, item, type, mode} = this.props;
// ////console.log("PersonalCompetititem");
// ////console.log(data);
// ////console.log(data[0]);
// ////console.log(data&&data[0].creator.image_url);
// const listItems = mydatas.map((item, index) =>
//
// );
// console.log("PersonalCompetititem");
// console.log(data);
// console.log("PersonalCompetititem");
// console.log(type);
// console.log("sdasjdhahsdas");
// console.log(this.props);
return (
<div>
{
item !== undefined || item !== null ? <div className={"yslborderbottom"}>
<div
className={this.props.index === undefined || this.props.index === null ? "myregitem11" : this.props.index === 0 ? "regitem" : "myregitem11"}
style={{
marginBottom: "19px",
}}>
<div className="regitemimg1 ">
<img className="regitemimg2" src={getImageUrl("images/" + item.creator.image_url)}>
</img>
<a className="maxnamewidth78" title={item.creator.name} style={{
color: "#999999",
fontSize: "14px",
width: "78px",
textAlign: "center"
}}>{item.creator.name}</a>
</div>
<div className=" maxnamewidth160" style={{
marginTop: "29px",
marginLeft: "37px",
width: "160px",
textAlign: "center"
}} >
<a className="maxnamewidth160" title={item.name} style={{
color: "#05101A",
fontSize: "16px",
width: "160px",
textAlign: "center"
}}>{item.name}</a>
</div>
{this.props.type === 5 || this.props.type === 2 ?
(this.props.mode === 3 ?
<div style={{
marginLeft: "37px",
display: "flex",
flexDirection: "initial",
width: "340px"
}}>
{
item && item.team_members.map((item, index) => {
return (
index === 0 ?
<img className="regitemimgs" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 1 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 2 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 3 ?
<div>
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
<img className="regitemimgs222"
src={getImageUrl(`images/educoder/competitions/pexjiazai.png`)}>
</img>
</div>
: ""
)
})
}
</div>
:
<div className=" " style={{
marginLeft: "37px",
display: "flex",
flexDirection: "initial",
width: "487px"
}}>
{
item && item.team_members.map((item, index) => {
return (
index === 0 ?
<img className="regitemimgs" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 1 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 2 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 3 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 4 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 5 ?
<div>
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
<img className="regitemimgs222"
src={getImageUrl(`images/educoder/competitions/pexjiazai.png`)}>
</img>
</div>
: ""
)
})
}
</div>
)
:
<div className=" " style={{
marginLeft: "37px",
display: "flex",
flexDirection: "initial",
width: "487px"
}}>
{
item && item.team_members.map((item, index) => {
return (
index === 0 ?
<img className="regitemimgs" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 1 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 2 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 3 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 4 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 5 ?
<div>
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
<img className="regitemimgs222"
src={getImageUrl(`images/educoder/competitions/pexjiazai.png`)}>
</img>
</div>
: ""
)
})
}
</div>
}
<div style={this.props.type === 5 || this.props.type === 2 ?
(this.props.mode === 3 ?
{
marginLeft: "37px",
}
:
{
marginLeft: "18px",
}
)
: {
marginLeft: "16px",
}}>
<div style={{marginTop: "23px", width: '140px'}}>
<span style={{
color: "#05101A",
fontSize: "16px",
}}>邀请码</span>
<span
style={{
color: "#05101A",
fontSize: "16px",
}}>{item.invite_code === null || item.invite_code === undefined ? "" : item.invite_code}</span>
</div>
<div style={{
overflow: 'hidden',
height: "1px",
width: "1px"
}}>
<input id={"copy_invite_code" + this.props.index}
value={item.invite_code === null || item.invite_code === undefined ? "" : item.invite_code}/>
</div>
</div>
{item.invite_code === null || item.invite_code === undefined ?
<i className="regitemimgs3 iconfont icon-fuzhi1 font-14 font-n"
style={{
marginLeft: "18px",
}}
>
</i>
:
<Tooltip placement="bottom" title={"复制邀请码"}>
<i className="regitemimgs3 iconfont icon-fuzhi1 font-14 font-n"
style={{
marginLeft: "18px",
}}
onClick={() => {
jsCopy("copy_invite_code" + this.props.index)
}}
>
</i>
</Tooltip>
}
{
this.props.type === 5 ?
(this.props.mode === 3 ?
<div className="regitemimgs444"
style={{
marginLeft: "32px",
}}
>
<div className="regitemimgs555">
<a
href={`/competitions/${this.props.match.params.identifier}/competition_teams/${item.id}`}
style={{
color: "#459be5",
width: "100%",
height: "100%",
lineHeight: "39px",
}}>战队详情</a>
</div>
<div className="regitemimgs555"
style={item.manage_permission === true ? {display: "block"} : {display: "none"}}
onClick={() => this.props.Exittheteamshow(item.id, true)}>
<p onClick={() => this.props.Exittheteamshow(item.id, true)}>删除战队</p>
</div>
<div className="regitemimgs6"
style={item.manage_permission === true ? {display: "block"} : {display: "none"}}
onClick={() => this.props.Createateamedit(item)}>
<p onClick={() => this.props.Createateamedit(item)}>编辑战队</p>
</div>
<div className="regitemimgs6"
style={item.manage_permission === true ? {display: "none"} : {display: "block"}}
onClick={() => this.props.Exittheteamshow(item.id, false)}>
<p onClick={() => this.props.Exittheteamshow(item.id, false)}>退出战队</p>
</div>
</div>
:
<div className="regitemimgs444"
style={{
marginLeft: "32px",
}}
>
<div className="regitemimgs5"
style={item.manage_permission === true ? {display: "block"} : {display: "none"}}
onClick={() => this.props.Exittheteamshow(item.id, true)}>
<p onClick={() => this.props.Exittheteamshow(item.id, true)}>删除战队</p>
</div>
<div className="regitemimgs6"
style={item.manage_permission === true ? {display: "block"} : {display: "none"}}
onClick={() => this.props.Createateamedit(item)}>
<p onClick={() => this.props.Createateamedit(item)}>编辑战队</p>
</div>
<div className="regitemimgs6"
style={item.manage_permission === true ? {display: "none"} : {display: "block"}}
onClick={() => this.props.Exittheteamshow(item.id, false)}>
<p onClick={() => this.props.Exittheteamshow(item.id, false)}>退出战队</p>
</div>
</div>
)
: this.props.type === 2 ?
(this.props.mode === 3 ?
<div className="regitemimgs444 "
style={{
marginLeft: "32px",
}}
>
<div className="regitemimgs555">
<a
href={`/competitions/${this.props.match.params.identifier}/competition_teams/${item.id}`}
style={{
color: "#459be5",
width: "100%",
height: "100%",
lineHeight: "39px",
}}>战队详情</a>
</div>
<div className="regitemimgs555"
style={item.manage_permission === true ? {display: "block"} : {display: "none"}}
onClick={() => this.props.Exittheteamshow(item.id, true)}>
<p onClick={() => this.props.Exittheteamshow(item.id, true)}>删除战队</p>
</div>
<div className="regitemimgs6"
style={item.manage_permission === true ? {display: "block"} : {display: "none"}}
onClick={() => this.props.Createateamedit(item)}>
<p onClick={() => this.props.Createateamedit(item)}>编辑战队</p>
</div>
<div className="regitemimgs6"
style={item.manage_permission === true ? {display: "none"} : {display: "block"}}
onClick={() => this.props.Exittheteamshow(item.id, false)}>
<p onClick={() => this.props.Exittheteamshow(item.id, false)}>退出战队</p>
</div>
</div>
:
<div className="regitemimgs444 "
style={{
marginLeft: "32px",
}}
>
<div className="regitemimgs5"
style={item.manage_permission === true ? {display: "block"} : {display: "none"}}
onClick={() => this.props.Exittheteamshow(item.id, true)}>
<p onClick={() => this.props.Exittheteamshow(item.id, true)}>删除战队</p>
</div>
<div className="regitemimgs6"
style={item.manage_permission === true ? {display: "block"} : {display: "none"}}
onClick={() => this.props.Createateamedit(item)}>
<p onClick={() => this.props.Createateamedit(item)}>编辑战队</p>
</div>
<div className="regitemimgs6"
style={item.manage_permission === true ? {display: "none"} : {display: "block"}}
onClick={() => this.props.Exittheteamshow(item.id, false)}>
<p onClick={() => this.props.Exittheteamshow(item.id, false)}>退出战队</p>
</div>
</div>
)
:
this.props.type === 3 ?
<div className="regitemimgs4"
style={{
marginLeft: "32px",
}}
>
<div className="regitemimgs6" onClick={() => this.props.Exittheteamshow(item.id, false)}>
<p onClick={() => this.props.Exittheteamshow(item.id, false)}>退出战队</p>
</div>
</div>
:
""
}
</div>
</div>
: ""
}
</div>
)
}
}
export default PersonalCompetititem;
@@ -0,0 +1,225 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { Menu, Icon, List, Avatar,Row, Col,Tag,Pagination,Alert} from 'antd';
import {getImageUrl} from 'educoder';
import axios from 'axios';
import './Competitionsindex.css';
import NoneData from "../../courses/coursesPublic/NoneData";
import LoadingSpin from '../../../common/LoadingSpin';
class CompetitionsIndex extends Component{
constructor(props) {
super(props)
this.state={
current: 'all',
datas:undefined,
page:1,
category:undefined
}
}
componentDidMount(){
window.document.title = '竞赛';
let{category,page}=this.state;
this.getdata(category,page)
}
getdata=(category,page)=>{
const Url =`/competitions.json`;
axios.get(Url,{params:{
category:category,
page:page,
per_page:15,
}
}).then((response) => {
if(response.status===200){
this.setState({
datas:response.data.competitions,
count:response.data.count,
})
}
})
.catch(function (error) {
console.log(error);
});
}
handleClick = e => {
this.setState({
current: e.key,
datas:undefined
});
let{category,page}=this.state;
this.getdata(e.key,page)
};
PaginationCourse=(pageNumber)=>{
let {category}=this.state;
this.setState({
page: pageNumber,
})
this.getdata(category,pageNumber);
}
render() {
let {datas,page,count}=this.state;
return (
<div>
<div className="clearfix">
<div>
<div className="clearfix">
<style>
{
`
.courses-head{
width: 100%;
height: 300px;
background-image: url(${getImageUrl(this.props.mygetHelmetapi && this.props.mygetHelmetapi.competition_banner_url === null ?`images/educoder/competitions/courses.jpg`:this.props.mygetHelmetapi&&this.props.mygetHelmetapi.competition_banner_url)});
background-color: #081C4B;
background-position: center;
background-repeat: no-repeat;
}
`
}
</style>
<div className="courses-head pr Competitionshead ">
<div className="edu-txt-center pathNavLine">
<div className="inline path-nav"></div>
</div>
</div>
<div className="competitionstitle mb20">
<div className="competitionstitle2">
<Menu onClick={this.handleClick} selectedKeys={[this.state.current]} mode="horizontal">
<Menu.Item key="all" className={"competitionmr50"}>
<span className={"competitionsvalue"}>全部</span>
</Menu.Item>
<Menu.Item key="nearly_published" className={"competitionmr50"}>
<span className={"competitionsvalue"}>即将发布</span>
</Menu.Item>
<Menu.Item key="progressing" className={"competitionmr50"}>
<span className={"competitionsvalue"}>进行中</span>
</Menu.Item>
<Menu.Item key="ended" className={"competitionmr50"}>
<span className={"competitionsvalue"}>往期比赛</span>
</Menu.Item>
</Menu>
</div>
</div>
<div className={"educontent clearfix mtf10 CompetitionsIndex mb20"}>
{datas===undefined?"":datas.length===0?"":<List
itemLayout="vertical"
size="large"
dataSource={datas&&datas}
renderItem={(item,key) => (
<a target="_blank" href={item.competition_status==="ended"?`/competitions/${item.identifier}`:item.competition_status==="nearly_published"?item.permission.editable==true?`/competitions/${item.identifier}`:null:item.competition_status==="progressing"?`/competitions/${item.identifier}`:null}
className={item.competition_status==="ended"?"competitionstitlesshou":item.competition_status==="nearly_published"?
item.permission.editable==true?"competitionstitlesshou":"endedfont":"competitionstitlesshou"}
>
<div className={"CompetitionsList"} >
{item.competition_status==="nearly_published"?
item.permission.editable==true?"":<div className={"CompetitionsListzhezhao"}>即将发布 敬请期待</div>:""}
{/*<div className={"CompetitionsListzhezhao"}>即将发布 敬请期待</div>*/}
{/*{item.description===null||item.description===undefined||item.description===""?<style>*/}
{/*{*/}
{/*`*/}
{/*// .CompetitionsIndex .ant-list-vertical .ant-list-item-action{*/}
{/*// margin-top:50px;*/}
{/*// }*/}
{/*`*/}
{/*}*/}
{/*</style>:""}*/}
<img className={"competitonimg"}
src={item.competition_status==="ended"?getImageUrl(`images/educoder/competitions/groups1.png`):item.competition_status==="nearly_published"?getImageUrl(`images/educoder/competitions/groups2.png`):item.competition_status==="progressing"?getImageUrl(`images/educoder/competitions/groups3.png`):""} />
<List.Item
key={key}
actions={[
<span>竞赛时间: {item.start_time}{item.end_time}</span>,
<span>报名截止时间{item.enroll_end_time}</span>,
]}
extra={
<div className={"pt50"} style={{"width":'314px'}}>
<Row gutter={16}>
<Col className="gutter-row" span={6}>
<div className="gutter-box CompetitionsIndexdadels">奖金</div>
</Col>
<Col className="gutter-row" span={6}>
<div className="gutter-box CompetitionsIndexdadels">浏览数</div>
</Col>
<Col className="gutter-row" span={6}>
<div className="gutter-box CompetitionsIndexdadels">报名数</div>
</Col>
</Row>
<Row gutter={16}>
<Col className="gutter-row" span={6}>
<div className="gutter-box CompetitionsIndexbottomvalue">¥{item.bonus}</div>
</Col>
<Col className="gutter-row" span={6}>
<div className="gutter-box CompetitionsIndexbottomvalue">{item.competition_status==="nearly_published"?"--":item.visits_count}</div>
</Col>
<Col className="gutter-row" span={6}>
<div className="gutter-box CompetitionsIndexbottomvalue">{item.competition_status==="nearly_published"?"--":item.member_count}</div>
</Col>
</Row>
</div>
}
>
<List.Item.Meta
title={<a className={item.competition_status==="ended"?"competitionstitlesshou":item.competition_status==="nearly_published"?
item.permission.editable==true?"competitionstitlesshou":"endedfont":"competitionstitlesshou"}>
<a target="_blank" className={"competitionstitles"}
href={item.competition_status==="ended"?`/competitions/${item.identifier}`:item.competition_status==="nearly_published"? item.permission.editable==true?`/competitions/${item.identifier}`:null:item.competition_status==="progressing"?`/competitions/${item.identifier}`:null}
>{item.name}{item.sub_title===null?"":`——${item.sub_title}`}</a>
{/*<span>{item.sub_title===null?"":*/}
{/*<Tag className="competitionsrelative" color="#87d068">{item.sub_title}</Tag>}*/}
{/*</span>*/}
</a>}
/>
<span className={"span666"}>{item.description}</span>
</List.Item>
</div>
</a>
)
}
/>}
{datas===undefined?"":count===undefined?"":count >15 ?<div className="mb40 edu-txt-center padding20-30"
>
<Pagination
showQuickJumper
defaultCurrent={1}
pageSize={15}
total={count===undefined?"":count}
current={page}
onChange={this.PaginationCourse}
/>
</div>:""}
{
datas===undefined?<LoadingSpin/>:datas && datas.length===0? <NoneData></NoneData>:""
}
</div>
</div>
</div>
</div>
</div>
)
}
}
export default CompetitionsIndex;
@@ -0,0 +1,163 @@
.teamsLayout{background: transparent !important;}
.competitionstitle{
height:50px !important;
border-radius: 6px;
background: #fff;
display: flex;
justify-content: center;
}
.competitionstitle2{
height:50px !important;
margin-left: 30px !important;
background: #fff;
width: 1200px;
}
.CompetitionsList{
position: relative;
/*max-height: 210px;*/
}
.competitonimg{
position: absolute;
right: -5px;
width: 80px;
top: 20px;
}
.ant-menu-horizontal {
border-bottom:none !important;
}
.competitionsvalue{
font-size: 16px;
font-family: PingFangSC-Medium,PingFangSC;
font-weight: 500;
}
.competitionmr50 {
margin-right: 50px !important;
}
.CompetitionsIndex .ant-list-item{
background: #fff !important;
margin-top: 20px;
border: none !important;
}
.CompetitionsIndex .ant-list-item{
padding:25px;
}
.CompetitionsIndex .ant-list-item-meta-title{
height:28px;
font-size:28px;
font-family:PingFangSC-Regular,PingFangSC;
font-weight:400;
color:rgba(5,16,26,1);
line-height:28px;
}
.CompetitionsIndex .ant-list-vertical .ant-list-item-meta{
margin-bottom: 20px !important;
}
.CompetitionsIndex .ant-list-vertical .ant-list-item-action {
margin-top: 20px;
margin-left: auto;
}
.CompetitionsIndex .ant-list-item-action-split{
display: none !important;
}
.CompetitionsIndexdadels{
font-family: PingFangSC-Regular,PingFangSC;
font-weight: 400;
color: #777777;
margin-bottom: 14px;
text-align: center;
}
.CompetitionsIndexbottomvalue{
font-size: 24px;
font-family: ArialMT;
color: rgba(5,16,26,1);
text-align: center;
}
.CompetitionsIndex .gutter-row{
/*margin-right:20px;*/
width: 33%;
}
.pt50{
padding-top: 50px;
}
.competitionstitles{
max-width: 789px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
margin-right: 15px;
color:#000 !important;
}
.competitionsrelative{
position: absolute;
top: 28px;
}
.CompetitionsList:hover{
/*box-shadow: 0 2px 6px rgba(51,51,51,.09);*/
box-shadow:3px 4px 10px 2px rgba(229,229,229,0.5);
opacity: 1;
border-radius: 2px;
}
.endedfont{
color:#000 !important;
}
.CompetitionsListzhezhao{
position: absolute;
top: 0px;
left: 0px;
width: 1206px;
height: 100%;
z-index: 10000;
display: none;
background: rgba(0,0,0,0.33);
text-align: center;
color: #fff;
font-size: 22px;
align-items: center;
justify-content: space-around;
flex-direction: column;
}
.CompetitionsList:hover .CompetitionsListzhezhao{
display: block;
display: flex;
}
.competitionstitlesshou:hover a{
cursor: pointer;
color: #1c91e8 !important;
}
.competitionstitlesshou{
cursor: pointer;
}
.Competitionshead{
background-color: #2d28ba !important;
background-position: center !important;
background-position: 50% !important;
background-repeat: no-repeat !important;
}
.span666{
color:#666666 !important;
}
@@ -0,0 +1,53 @@
.teamsLayout{background: transparent !important;}
.teamsLayout .teamsLayoutitle{
font-size:18px;
font-family:PingFangSC-Semibold,PingFang SC;
font-weight:600;
color:rgba(5,16,26,1);
line-height:25px;
margin-top: 10px;
margin-bottom: 10px;
}
.teamsLayoutTable .ant-table-bordered .ant-table-thead > tr > th, .ant-table-bordered .ant-table-tbody > tr > td {
border-right: 1px solid transparent !important;
}
.teamsLayoutTable .ant-table-body .ant-table-thead > tr> th:nth-last-child(1){
border-right: 1px solid #e8e8e8 !important;
}
.teamsLayoutTable .ant-table-body .ant-table-tbody > tr> td:nth-last-child(1){
border-right: 1px solid #e8e8e8 !important;
}
.teamsLayoutTable .ant-table-bordered .ant-table-thead > tr > th{
background:#EEEEEE;
font-size: 14px;
font-family: PingFangSC-Regular,PingFang SC;
font-weight: 400;
color: rgba(102,102,102,1);
line-height: 20px;
}
.teamsLayoutTable .ant-table-bordered .ant-table-tbody > tr > th{
background:#EEEEEE;
font-size:14px;
font-family:PingFangSC-Regular,PingFang SC;
font-weight:400;
color:rgba(5,16,26,1);
line-height:20px;
}
.teamsLayout .mt40{
margin-top: 40px !important;
}
.teamsLayoutheji{
color: #878787;
font-size: 16px;
}
.teamsLayoucolor-orange {
color: #ff6800!important;
font-size: 16px;
}
@@ -0,0 +1,267 @@
import React, { Component } from 'react';
import { Breadcrumb,Layout,Table, Divider, Tag,Badge,Tooltip} from 'antd';
import { Link } from 'react-router-dom';
import axios from 'axios';
import './Competitionteams.css';
const { Content } = Layout;
class Competitionteams extends Component{
constructor(props) {
super(props)
this.state={
shixundata: undefined,
coursedata:undefined,
}
}
componentDidMount(){
window.document.title = '竞赛';
if(this.props.match.params.identifier!=null){
let url=`/competitions/${this.props.match.params.identifier}/common_header.json`;
axios.get(url).then((response) => {
if(response.status===200){
this.setState({
data:response.data,
})
}
}).catch((error) => {
console.log(error)
})
}
this.getshixundata();
this.getcoursedata();
}
getshixundata=()=>{
const Url =`/competitions/${this.props.match.params.identifier}/competition_teams/${this.props.match.params.competition_team_id}/shixun_detail.json`;
axios.get(Url).then((response) => {
if(response.status===200){
// let data={
// shixuns: [
// {
// creator: "黄井泉", // 创建者
// shixun_name: "单链表的学习与应用(I", // 实训名称
// shixun_identifier: "mnf6b7z3",
// forked: false, // false:原创
// myshixuns_count: 179, // 学习人数
// forked_myshixun_count: 0, // 被fork发布的学习人数
// valid_count: 82, // 有效作品数
// score: 1320 // 应用值
// }
// ],
// shixun_count: 1, // 实训总计
// total_myshixun_count: 179, // 学习人数总计
// total_forked_myshixun_count: 0, // 被fork发布的学习人数总计
// total_valid_count: 82, // 有效作品数总计
// total_shixun_score: 1320 // 应用值总计
// }
let data=response.data;
let newarr=data.shixuns;
let newobj={
creator:"合计:",
shixun_name:data.shixun_count,
myshixuns_count:data.total_myshixun_count,
forked_myshixun_count:data.total_forked_myshixun_count,
valid_count:data.total_valid_count,
score:data.total_shixun_score
}
newarr.push(newobj)
this.setState({
shixundata:newarr
})
}
})
.catch(function (error) {
console.log(error);
});
}
getcoursedata=()=>{
const Url =`/competitions/${this.props.match.params.identifier}/competition_teams/${this.props.match.params.competition_team_id}/course_detail.json`;
axios.get(Url).then((response) => {
if(response.status===200){
// let data={
// courses: [
// {
// creator: "周海芳", // 创建者
// creator_login: "Nancy", // login
// course_name: "大学计算机基础2018年秋季",
// course_id: 1502,
// students_count: 122, // 学生数量
// shixun_homework_count: 8, // 发布的实训作业数量
// valid_count: 977, // 有效作品数
// score: 29810 // 应用值
// }
// ],
// total_course_count: 1, // 课堂总计
// total_students_count: 122, // 学生数总计
// total_shixun_homework_count: 8, // 实训作业数总计
// total_valid_count: 977, // 有效作品数总计
// total_course_score: 29810 // 应用值总计
// }
let data=response.data;
let newarr=data.courses;
let newobj={
creator:"合计:",
course_name:data.total_course_count,
students_count:data.total_students_count,
shixun_homework_count:data.total_shixun_homework_count,
valid_count:data.total_valid_count,
score:data.total_course_score
}
newarr.push(newobj)
this.setState({
coursedata:newarr
})
}
})
.catch(function (error) {
console.log(error);
});
}
render() {
let {data}=this.state;
const shixuncolumns = [
{
title: '创建者',
dataIndex: 'creator',
key: 'creator',
render: (text, record) => <div className={record.creator==="合计:"?"teamsLayoutheji":""}>{text}</div>,
},
{
title: '名称',
dataIndex: 'shixun_name',
key: 'shixun_name',
render: (text, record) =>
<div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}{record.forked===true?<Badge count={"原创"} style={{ backgroundColor: '#459BE5' }} />:""}</div>,
},
{
title: '学习人数',
dataIndex: 'myshixuns_count',
key: 'myshixuns_count',
render: (text, record) => <div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>,
},
{
title: '被fork发布的学习人数',
dataIndex: 'forked_myshixun_count',
key: 'forked_myshixun_count',
render: (text, record) =>
<Tooltip placement="bottom" title={"fork该实训产生的新实训,学习总人数"}>
<div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>
</Tooltip>,
},
{
title: '有效作品数',
dataIndex: 'valid_count',
key: 'valid_count',
render: (text, record) =>
<Tooltip placement="bottom" title={"至少完成了1个关卡"}>
<div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>
</Tooltip>,
},
{
title: '应用值',
dataIndex: 'score',
key: 'score',
render: (text, record) => <div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>,
},
];
const coursecolumns = [
{
title: '创建者',
dataIndex: 'creator',
key: 'creator',
render: (text, record) => <div className={record.creator==="合计:"?"teamsLayoutheji":""}>{text}</div>,
},
{
title: '名称',
dataIndex: 'course_name',
key: 'course_name',
render: (text, record) => <div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>,
},
{
title: '学生数量',
dataIndex: 'students_count',
key: 'students_count',
render: (text, record) => <div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>,
},
{
title: '发布的实训作业数量',
dataIndex: 'shixun_homework_count',
key: 'shixun_homework_count',
render: (text, record) =>
<div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>
,
},
{
title: '有效作品数',
dataIndex: 'valid_count',
key: 'valid_count',
render: (text, record) =>
<Tooltip placement="bottom" title={"至少完成了1个关卡"}>
<div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>
</Tooltip>,
},
{
title: '应用值',
dataIndex: 'score',
key: 'score',
render: (text, record) => <div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>,
},
];
// console.log(this.state.shixundata)
return (
<div className={"educontent clearfix mt20 "}>
<Breadcrumb separator=">">
<Breadcrumb.Item><Link to={"/competitions"}>{data&&data.name}</Link></Breadcrumb.Item>
<Breadcrumb.Item><Link to={`/competitions/${this.props.match.params.identifier}/enroll`}>报名</Link></Breadcrumb.Item>
<Breadcrumb.Item>战队详情</Breadcrumb.Item>
</Breadcrumb>
<Layout className={"teamsLayout"}>
<Content className={"teamsLayoutitle"}>实训项目</Content>
<Content className={"teamsLayoutContent"}>
<Table className="teamsLayoutTable" columns={shixuncolumns} dataSource={this.state.shixundata} bordered pagination={false}/>
</Content>
<Content className={"teamsLayoutitle mt40"}>翻转课堂</Content>
<Content className={"teamsLayoutContents"}>
<Table className="teamsLayoutTable" columns={coursecolumns} dataSource={this.state.coursedata} bordered pagination={false}/>
</Content>
</Layout>
</div>
)
}
}
export default Competitionteams;
@@ -0,0 +1,438 @@
.teamsLayout{background: transparent !important;}
.teamsLayout .ant-layout-sider{
background: transparent !important;
flex: 0 0 180px !important;
max-width: 180px !important;
min-width: 180px !important;
width: 180px !important;
}
.teamsLayout .teamsLayoutitle{
font-size:18px;
font-family:PingFangSC-Semibold,PingFang SC;
font-weight:600;
color:rgba(5,16,26,1);
line-height:25px;
margin-top: 10px;
margin-bottom: 10px;
}
.teamsLayoutTable .ant-table-bordered .ant-table-thead > tr > th, .ant-table-bordered .ant-table-tbody > tr > td {
border-right: 1px solid transparent !important;
}
.teamsLayoutTable .ant-table-body .ant-table-thead > tr> th:nth-last-child(1){
border-right: 1px solid #e8e8e8 !important;
}
.teamsLayoutTable .ant-table-body .ant-table-tbody > tr> td:nth-last-child(1){
border-right: 1px solid #e8e8e8 !important;
}
.teamsLayoutTable .ant-table-bordered .ant-table-thead > tr > th{
background:#EEEEEE;
font-size: 14px;
font-family: PingFangSC-Regular,PingFang SC;
font-weight: 400;
color: rgba(102,102,102,1);
line-height: 20px;
}
.teamsLayoutTable .ant-table-bordered .ant-table-tbody > tr > th{
background:#EEEEEE;
font-size:14px;
font-family:PingFangSC-Regular,PingFang SC;
font-weight:400;
color:rgba(5,16,26,1);
line-height:20px;
}
.teamsLayout .mt40{
margin-top: 40px !important;
}
.teamsLayoutheji{
color: #878787;
font-size: 16px;
}
.teamsLayoucolor-orange {
color: #ff6800 !important;
font-size: 16px;
}
.CompetitionCommonbanner{
padding: 20px;
background:rgba(255,255,255,1);
box-shadow:3px 2px 12px 2px rgba(0,0,0,0.05);
position: relative;
}
.CompetitionCommonbannerfont{
height:100%;
width: 365px !important;
line-height: 34px;
}
.CompetitionCommonbannerfont .competitionbannerdiv:nth-child(1){
max-height:100px;
font-size:25px;
font-weight:400;
color:rgba(5,16,26,1);
line-height: 30px;
}
.CompetitionCommonbannerfont .competitionbannerdiv:nth-child(2){
max-height: 70px;
font-size:16px;
font-weight:400;
/*color:rgba(155,155,155,1);*/
color:#05101A;
}
.CompetitionCommonbannerfont .competitionbannerdiv:nth-child(3){
max-height: 70px;
font-size: 16px;
font-weight: 400;
/*color: rgba(155,155,155,1);*/
color:#05101A;
}
.Competitioncolor9b{
color: #9B9B9B;
}
.Competitioncolor77{
color: #777777;
font-size: 14px;
}
.Competitioncolor516{
font-size:24px;
color:rgba(5,16,26,1);
}
.Competitionfontsize22{
font-size:22px;
font-weight:500;
color:rgba(255,255,255,1);
}
.Competitionfontsize16{
font-size: 16px;
font-weight: 400;
color: rgba(102,102,102,1);
}
.ant-layout-sider {
position: relative;
min-width: 0;
background: #001529;
-webkit-transition: all 0.2s;
-o-transition: all 0.2s;
transition: all 0.2s;
}
.CompetitionMenu .ant-menu-item::after {
left: 0px !important;
right: auto;
border-right: 5px solid #4CACFF;
}
.CompetitionMenu .ant-menu-item{
height: 30px;
line-height: 30px;
background:none;
color:#666;
}
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
background-color: transparent;
}
.CompetitionMenu .ant-menu-item:not(:last-child){
margin-bottom: 40px;
background: transparent;
color:#666;
}
.CompetitionMenu .ant-menu-item{
font-size: 18px;
}
.CompetitionMenu .ant-menu-item-selected {
color: rgba(76,172,255,1) !important;
}
.CompetitionMenu{
width: 145px;
background: #fff;
border: 1px solid rgba(239,239,239,1);
padding-top: 20px;
padding-bottom: 40px !important;
}
.teamsLayoutleft{
background: transparent !important;
}
.Competitioncharts{
font-size: 24px;
color: rgba(5,16,26,1);
}
.Competitionfirst{
width:233px;
height:298px;
background:rgba(250,250,250,1);
box-shadow:0px 2px 8px 2px rgba(255,134,34,0.5);
border-radius:5px;
}
.Competitionsecondary{
width:234px;
height:298px;
background:rgba(250,250,250,1);
box-shadow:0px 3px 5px 0px rgba(254,190,154,1);
border-radius:5px;
}
.Competitionthird{
width: 234px;
height: 298px;
background: rgba(250,250,250,1);
box-shadow: 0px 4px 5px 0px rgba(200,200,202,1);
border-radius: 5px;
}
.Competition399{
height:399px;
}
.Competitiontransparent table{
background: transparent;
}
.Commonimg{
position: absolute;
right: -5px;
width:93px;
top: 10px;
}
.Competitionthirdbox{
width:234px;
height:167px;
background:rgba(223,223,225,1);
position: relative;
}
.Competitionfirstbox{
width:233px;
height:167px;
background:rgba(255,231,160,1);
position: relative;
}
.Competitionsecondarybox{
width:234px;
height:167px;
background:rgba(253,230,217,1);
position: relative;
}
.rankingimg{
width: 60px;
height: 60px;
border-radius: 50% !important;
box-shadow: 0px 0px 12px rgba(0,0,0,0.2);
border: 2px solid #459BE5;
}
.Competitioncenter{
text-align: center;
padding-top: 20px;
}
.jinshaifont{
font-size: 16px;
color: rgba(5,16,26,1);
margin-top: 13px !important;
}
.Competitionthird .ant-card-body {
padding: 12px;
zoom: 1;
}
/*.Competitionthird .ant-card-meta-title{*/
/*margin-bottom: 0px !important;*/
/*}*/
.Competitionfirst .ant-card-body {
padding: 12px;
zoom: 1;
}
.Competitionsecondary .ant-card-body {
padding: 12px;
zoom: 1;
}
.center{
text-align: center;
}
.rankfonttop{
font-size:14px;
color:rgba(102,102,102,1);
}
.rankfontmid{
font-size:18px;
color:rgba(102,102,102,1);
}
.rankfontbottom{
font-size:26px;
color:rgba(165,91,41,1);
text-align: center;
}
.rankfontbottoms{
font-size:28px;
color:rgba(165,91,41,1);
text-align: center;
}
.Competitionuserimg{
width: 64px;
height: 63px;
border-radius: 50%;
border: 2px solid #459BE5;
}
.CompetitionsListzhezhao{
position: absolute;
top: 0px;
left: 0px;
width: 1206px;
height: 100%;
z-index: 10000;
display: none;
background: rgba(0,0,0,0.33);
text-align: center;
color: #fff;
font-size: 22px;
}
.relative{position: relative;}
.relative:hover .CompetitionsListzhezhao{
display: block;
display: flex;
align-items: center;
justify-content: space-around;
flex-direction: column;
}
.image_urlbox{
width: 790px;
height: 340px;
}
.CompetitionContents{
background: #fff !important;
padding: 40px;
box-shadow: 3px 2px 12px 2px rgba(0,0,0,0.05);
border: 1px solid rgba(239,239,239,1);
}
.rankbeicenter{
text-align: center;
}
.rankbei{
font-size: 16px;
color: rgba(119,119,119,1);
}
.youranklist{
background: rgba(226,241,255,1);
line-height: 50px;
text-align: center;
padding-left: 20px;
}
.ranknames{
font-size: 16px;
color: rgba(62,62,62,1);
}
.ranknameslast{
font-size:16px;
color:rgba(12,158,254,1);
}
.textleft{
text-align: left;
}
.textright{
text-align: right;
}
.userranksclass{
text-align: left;
width: 18%;
padding-left: 12px;
margin-right: 28px;
}
.Commonimgbox{
width: 800px !important;
}
.CompetitionCommonbannerfont{
width: 350px !important;
margin-left:10px;
}
.color000{
color: #000;
}
.cursorpointer{
cursor: pointer;
}
.rankfonttop{
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap
}
.usernamebox{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
max-width: 100px;
display: inherit;
}
.competimgabsolute{
position: absolute;
left: 72px;
bottom: -10px;
}
.competimgabsolute .ant-badge-count{
box-shadow: none !important;
}
.competimgabsoluteijmg{
position: absolute;
left: -11px;
bottom: 0px;
}
@@ -0,0 +1,548 @@
import React, { Component } from 'react';
import { Breadcrumb,Layout,Table, Divider, Tag,Badge,Row, Col,Button, Menu, Icon} from 'antd';
import { Link } from 'react-router-dom';
import axios from 'axios';
import {markdownToHTML,getImageUrl} from 'educoder';
import CompetitionContents from './CompetitionCommonChild/CompetitionContents';
import CompetitionContentsChart from './CompetitionCommonChild/CompetitionContentsChart';
import CompetitionContentsMd from './CompetitionCommonChild/CompetitionContentsMd';
import CompetitionContentspdf from './CompetitionCommonChild/CompetitionContentspdf';
import './CompetitionCommon.css';
const {Sider } = Layout;
class CompetitionCommon extends Component{
constructor(props) {
super(props)
this.state={
data: undefined,
bannerdata: undefined,
module_type: undefined,
mdContentdata: undefined,
chart_rules: undefined,
Competitionedittype: false,
chartdata: undefined,
has_url: false,
signupdata: undefined
}
}
componentDidMount(){
window.document.title = '竞赛';
if(this.props.match.params.identifier!=null){
this.getbannerdata();
// this.setState({
// thiskeys:this.props.location.search.replace('?menu=', '')
// })
// let url=`/competitions/${this.props.match.params.identifier}.json`;
// axios.get(url).then((response) => {
// if(response.status===200){
// this.setState({
// bannerdata:response.data
// })
// }
// }).catch((error) => {
// //console.log(error)
// })
}
}
componentDidUpdate = (prevProps) => {
if (prevProps.user != this.props.user) {
//console.log("componentDidUpdatess");
//console.log(this.props.user);
if (this.props.user && this.props.user.login != "") {
const zul = `/competitions/${this.props.match.params.identifier}/competition_staff.json`;
axios.get((zul)).then((result) => {
if (result) {
if (result.data) {
this.setState({
signupdata: result.data
})
}
}
}).catch((error) => {
////console.log(error);
})
}
}
}
//获取头部信息
getbannerdata=()=>{
// let menuid=this.props.location.search.replace('?menu=', '');
let query=this.props.location&&this.props.location.search;
const types = query.split('&')
const menuid = types[0].split('?menu=')
let url=`/competitions/${this.props.match.params.identifier}/common_header.json`;
axios.get(url).then((response) => {
if(response.status===200){
this.setState({
data: response.data,
thiskeys: menuid[1] === undefined || menuid[1] === "" ? response.data.competition_modules[0].id : menuid[1],
mode: response.data.mode
})
if(menuid[1]===undefined||menuid[1]===""){
this.getrightdata(
response.data.competition_modules[0].id,
response.data.competition_modules[0].module_type,
response.data.competition_modules[0].module_url,
response.data.competition_modules[0].has_url
)
}else{
let newlist=response.data.competition_modules;
newlist.map((item,key)=>{
if(`${item.id}`===`${menuid[1]}`){
this.getrightdata(
item.id,
item.module_type,
item.module_url,
item.has_url
)
}
})
}
}
}).catch((error) => {
//console.log(error)
})
//this.props.user 有可能为空
if (this.props.user && this.props.user.login != "") {
const zul = `/competitions/${this.props.match.params.identifier}/competition_staff.json`;
axios.get((zul)).then((result) => {
if (result) {
if (result.data) {
this.setState({
signupdata: result.data
})
}
}
}).catch((error) => {
////console.log(error);
})
}
}
getrightdatas=(e)=>{
let keys = parseInt(e.key);
this.getlistdata(keys)
this.props.history.replace(`?menu=${keys}`);
}
getlistdata=(keys,listkey)=>{
let{data}=this.state;
this.setState({
thiskeys:keys
})
data&&data.competition_modules.map((item,key)=>{
if(item.module_type!="enroll") {
if (keys === item.id) {
this.getrightdata(item.id, item.module_type, item.module_url, item.has_url, listkey)
return
}
}
})
}
getnewchartdata=(typeid,tabkey)=>{
if(typeid==="chart"){
let url=`/competitions/${this.props.match.params.identifier}/chart_rules.json`;
axios.get(url)
.then((response) => {
if(response.status===200){
this.setState({
chart_rules:response.data,
tabkey: tabkey === undefined ? response.data.stages[0].id === null ? "0" : `${response.data.stages[0].id}` : tabkey
})
}
}).catch((error) => {
//console.log(error)
})
}
}
getrightdata=(id,typeid,module_url,has_url,listkey)=>{
// if(typeid==="enroll"){
// this.props.history.replace(`/competitions/${this.props.match.params.identifier}/enroll`);
// return
// }
this.getnewchartdata(typeid, listkey)
if(has_url===false){
let url=`${module_url}`;
axios.get(url).then((response) => {
if(response.status===200){
if(typeid==="chart"){
this.setState({
chartdata:response.data
})
}else{
this.setState({
mdContentdata:response.data
})
}
}
}).catch((error) => {
//console.log(error)
})
}else{
if (module_url.substring(0, 7) == 'http://' || module_url.substring(0, 8) == 'https://') {
// window.location.href= module_url
window.open(module_url)
}else{
window.open(`https://${module_url}`)
// window.location.href=;
}
return
}
this.setState({
module_id:id,
module_type:typeid,
has_url:has_url
})
}
Competitionedit=()=>{
this.setState({
Competitionedittype: true
})
}
hideCompetitionedit=()=>{
this.setState({
Competitionedittype:false
})
}
newgotocourse=(url)=>{
if(this.props.checkIfLogin()===false){
this.props.showLoginDialog()
return
}
if(this.props.checkIfProfileCompleted()===false){
this.props.showProfileCompleteDialog()
return
}
window.open(url);
}
gotocourse=(url)=>{
if(this.props.checkIfLogin()===false){
this.props.showLoginDialog()
return
}
if(this.props.checkIfProfileCompleted()===false){
this.props.showProfileCompleteDialog()
return
}
if(url===undefined){
let {data,signupdata}=this.state;
// if(signupdata.enrolled===true){
// this.props.history.replace(`/courses/${data.course_id}`);
// }else{ }
if (data.member_of_course === true) {
// this.props.history.replace(`/courses/${data.course_id}`);
window.open(`/courses/${data.course_id}`)
} else {
// 以学生身份调用加入课堂 进入课堂首页
let url = "/courses/apply_to_join_course.json"
axios.post(url, {
invite_code: data.invite_code,
student: 1
}
).then((response) => {
if (response.data.status === 0) {
// this.props.history.replace();
this.getbannerdata()
window.open(`/courses/${data.course_id}`);
}
})
}
}else{
if (url === "personal") {
let urls = `/competitions/${this.props.match.params.identifier}/enroll`;
this.Personalregistration(urls)
} else {
window.open(url);
}
}
}
Personalregistration = (urls) => {
let {signupdata} = this.state;
if (signupdata.enroll_ended === true) {
//已截止
this.props.showNotification(`报名已截止`);
return;
}
if (signupdata.enrolled === true) {
this.props.showNotification(`你已经报名,不能重复报名!`);
return;
}
const url = `/competitions/${this.props.match.params.identifier}/competition_teams.json`;
axios.post(url).then((response) => {
if (response) {
if (response.data) {
this.props.showNotification(`报名成功,预祝您夺得桂冠!`);
// this.props.history.replace(urls);
this.getbannerdata()
window.open(urls)
}
}
}).catch((error) => {
});
}
Competitioncallback=(key)=>{
this.setState({
tabkey:key
})
let url=`/competitions/${this.props.match.params.identifier}/charts.json`;
axios.get(url,{params:{
stage_id:key===0||key===null?undefined:key
}}).then((response) => {
if(response.status===200){
this.setState({
chartdata:response.data
})
}
}).catch((error) => {
//console.log(error)
})
}
render() {
let {data, module_type, Competitionedittype, signupdata} = this.state;
return (
data===undefined?"":<div className={"educontent clearfix mt20 "}>
<Breadcrumb separator=">">
<Breadcrumb.Item><Link to={"/competitions"}>在线竞赛</Link></Breadcrumb.Item>
<Breadcrumb.Item>{data && data.name}</Breadcrumb.Item>
</Breadcrumb>
<div className={"mt10 relative"}>
<Row className={"CompetitionCommonbanner"}>
{data.competition_status === "nearly_published" ?
data && data.permission.editable === true ? "" :
<div className={"CompetitionsListzhezhao"}>即将发布 敬请期待</div> : ""}
<img className={"Commonimg"}
src={data.competition_status === "ended" ? getImageUrl(`images/educoder/competitions/groups1.png`) : data.competition_status === "nearly_published" ? getImageUrl(`images/educoder/competitions/groups2.png`) : data.competition_status === "progressing" ? getImageUrl(`images/educoder/competitions/groups3.png`) : ""}/>
<Col span={15} className={"Commonimgbox"}>
<img className={"image_urlbox"}
src={data === undefined ? getImageUrl(`images/educoder/competitions/mainbanner.jpg`) : data.avatar_url === null ? getImageUrl(`images/educoder/competitions/mainbanner.jpg`) : getImageUrl(data.avatar_url)}/>
</Col>
<Col className={"CompetitionCommonbannerfont"} span={9}>
<Col
className={data && data.name.length > 15 ? "competitionbannerdiv mt30" : "competitionbannerdiv mt30"}>{data && data.name}</Col>
<Col className={"competitionbannerdiv mt10"}>
<Col className={"Competitioncolor9b"}>竞赛时间<span
className={"color000"}>{data && data.start_time}{data && data.end_time}</span></Col>
{/*<Col></Col>*/}
</Col>
<style>
{
`
.ant-col-6{
width: 30%;
}
`
}
</style>
<Col className={"competitionbannerdiv mt10"}>
<Row gutter={16}>
<Col className="gutter-row" span={6}>
<div className="gutter-box CompetitionsIndexdadels Competitioncolor77">奖金</div>
</Col>
<Col className="gutter-row ml20 rankbeicenter" span={6}>
<div className="gutter-box CompetitionsIndexdadels Competitioncolor77">浏览数</div>
</Col>
<Col className="gutter-row rankbeicenter" span={6}>
<div className="gutter-box CompetitionsIndexdadels Competitioncolor77">报名数</div>
</Col>
</Row>
<Row gutter={16}>
<Col className="gutter-row" span={6}>
<div
className="gutter-box CompetitionsIndexbottomvalue Competitioncolor516">¥{data && data.bonus}</div>
</Col>
<Col className="gutter-row ml20 rankbeicenter" span={6}>
<div
className="gutter-box CompetitionsIndexbottomvalue Competitioncolor516">{data.competition_status === "nearly_published" ? "--" : data && data.visits_count}</div>
</Col>
<Col className="gutter-row rankbeicenter" span={6}>
{data.competition_status === "ended" ?
<div className={data.mode === 2 ?data.member_of_course==true?"gutter-box CompetitionsIndexbottomvalue Competitioncolor516 cursorpointer":"gutter-box CompetitionsIndexbottomvalue Competitioncolor516":"gutter-box CompetitionsIndexbottomvalue Competitioncolor516 cursorpointer"}
// 已结束onClick={data.competition_status === "nearly_published" ? "" : () => this.gotocourse(`/competitions/${this.props.match.params.identifier}/enroll`)}
onClick={ data.mode === 2 ?data.member_of_course==true?() => this.newgotocourse(`/courses/${data.course_id}`):"":() => this.newgotocourse(`/competitions/${this.props.match.params.identifier}/enroll`)}
>{data && data.member_count}</div>
:data.competition_status === "nearly_published" ?
<div className="gutter-box CompetitionsIndexbottomvalue Competitioncolor516"
// onClick={data.competition_status === "nearly_published" ? "" : () => this.gotocourse(`/competitions/${this.props.match.params.identifier}/enroll`)}
>{"--"}</div> :
data.competition_status === "progressing" ?
data.mode === 2 ?
<div className="gutter-box CompetitionsIndexbottomvalue Competitioncolor516 cursorpointer" onClick={() => this.gotocourse()}>{data && data.member_count}</div> : signupdata && signupdata.personal === true ?
<div className="gutter-box CompetitionsIndexbottomvalue Competitioncolor516 cursorpointer" onClick={() => this.gotocourse("personal")}>{data && data.member_count}</div> : <div
className="gutter-box CompetitionsIndexbottomvalue Competitioncolor516 cursorpointer"
onClick={() => this.gotocourse(`/competitions/${this.props.match.params.identifier}/enroll`)}>{data && data.member_count}</div>
:""}
</Col>
</Row>
</Col>
{data.competition_status === "ended" ? <style>
{
`
.Competitionfontsize22{
height: 50px;
border-radius: 4px;
}
`
}
</style> : <style>
{
`
.Competitionfontsize22{
height: 50px;
background: rgba(76,172,255,1);
border-radius: 4px;
}
`
}
</style>}
<Col className={"competitionbannerdiv mt20"}>
{data.competition_status === "ended" ?
<Button type="primary" block className={"Competitionfontsize22"} disabled={true}>
已结束
</Button> : data.enroll_end === true ?
<Button type="primary" block className={"Competitionfontsize22"} disabled={true}>
{data.competition_status === "nearly_published" ? "未发布" : "报名截止"}
</Button> :
data.competition_status === "progressing" ?
<Button type="primary" block className={"Competitionfontsize22"}>
{data.mode === 2 ?
<a onClick={() => this.gotocourse()}>立即报名</a> : signupdata && signupdata.personal === true ?
<a onClick={() => this.gotocourse("personal")}>立即报名</a> : <a
onClick={() => this.gotocourse(`/competitions/${this.props.match.params.identifier}/enroll`)}>立即报名</a>}
</Button>:""}
</Col>
<Col
className={"mt10 Competitionfontsize16"}>{data && data.enroll_end_time === null ? "" : `报名截止时间:${data && data.enroll_end_time}`}</Col>
</Col>
</Row>
</div>
<style>
{
`
.CompetitionMenu .ant-menu-item a{
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap
}
`
}
</style>
<Layout className={'teamsLayout mt40'}>
<Sider>
<Menu mode="inline" className="CompetitionMenu" selectedKeys={[`${this.state.thiskeys}`]}
onClick={(e) => this.getrightdatas(e)}>
{data && data.competition_modules.map((item, key) => {
if (item.module_type != "enroll") {
return (
<Menu.Item key={item.id}>
{/*{item.has_url===false?<span*/}
{/*// onClick={()=>this.getrightdata(item.id,item.module_type,item.module_url,item.has_url)}*/}
{/*>{item.name}</span>:<a*/}
{/*// target="_blank"*/}
{/*href={item.module_url}*/}
{/*// onClick={()=>this.getrightdata(item.id,item.module_type)}*/}
{/*>{item.name}</a>}*/}
<a title={item.name}>{item.name}</a>
</Menu.Item>
)
}
})}
</Menu>
{/*<Menu mode="inline" className="CompetitionMenu" onClick={(e)=>this.isdownloadpdf(e)}>*/}
{/*<Menu.Item>*/}
{/*<a>证书下载</a>*/}
{/*</Menu.Item>*/}
{/*</Menu>*/}
</Sider>
{module_type === "certificate" ? "" : <Layout className={"teamsLayoutleft CompetitionContents mb30"}>
{this.state.module_type === "chart" ? Competitionedittype === false ? <CompetitionContentsChart
{...this.props}
{...this.state}
Competitionedit={() => this.Competitionedit()}
Competitioncallback={(e) => this.Competitioncallback(e)}
/> : "" : Competitionedittype === false ? <CompetitionContents
Competitionedit={() => this.Competitionedit()}
{...this.props}
{...this.state}
/> : ""}
{/*<CompetitionContentsChart*/}
{/*{...this.props}*/}
{/*{...this.state}*/}
{/*/>*/}
{Competitionedittype === true ? <CompetitionContentsMd
hideCompetitionedit={() => this.hideCompetitionedit()}
getlistdata={(keys, listkey) => this.getlistdata(keys, listkey)}
Competitioncallback={(e) => this.Competitioncallback(e)}
{...this.props}
{...this.state}
/> : ""}
</Layout>}
{module_type === "certificate" ? <Layout className={"teamsLayoutleft CompetitionContents mb30"}>
<CompetitionContentspdf
{...this.props}
{...this.state}
/>
</Layout> : ""}
</Layout>
</div>
)
}
}
export default CompetitionCommon;
@@ -0,0 +1,45 @@
import React, { Component } from 'react';
import {Button,Layout} from 'antd';
import axios from 'axios';
import {markdownToHTML,getImageUrl,AttachmentList} from 'educoder';
const { Header, Footer, Sider, Content } = Layout;
class CompetitionContents extends Component{
constructor(props) {
super(props)
this.state={
hash:undefined
}
}
componentDidMount(){
window.document.title = '竞赛';
this.props.MdifHasAnchorJustScorll();
}
render() {
let {mdContentdata, data} = this.props;
//mdhash滚动
this.props.MdifHasAnchorJustScorll();
return (
<div className={"fr"}>
{data && data.permission.editable === true ? this.props.Competitionedittype === false ? this.props.has_url === false ?
<Button className={"fr"} type="primary" ghost onClick={() => this.props.Competitionedit()}>
编辑
</Button>:"":"":""}
<div className={this.props.current_user&&this.props.current_user.admin===true||this.props.current_user&&this.props.current_user.business===true?"mt50 mb100 ":"mb100 "}>
<Content className={"markdown-body"} dangerouslySetInnerHTML={{__html: markdownToHTML(mdContentdata===undefined?"":mdContentdata.md_content===undefined||mdContentdata.md_content===null?"":mdContentdata.md_content).replace(/▁/g, "▁▁▁")}}>
</Content>
<div className={"mt30"}>
<AttachmentList {...this.props} {...this.state} attachments={mdContentdata===undefined?[]:mdContentdata.attachments===undefined?[]:mdContentdata.attachments}></AttachmentList>
</div>
</div>
</div>
)
}
}
export default CompetitionContents;
@@ -0,0 +1,423 @@
import React, { Component } from 'react';
import {Button,Layout,Tabs,Icon, Card, Avatar, Row, Col ,Table,Badge} from 'antd';
import {markdownToHTML,getImageUrl,getRandomNumber} from 'educoder';
import axios from 'axios';
const { Content } = Layout;
const { TabPane } = Tabs;
const { Meta } = Card;
class CompetitionContents extends Component{
constructor(props) {
super(props)
this.state={
personal:undefined
}
}
componentDidMount(){
window.document.title = '竞赛';
let url=`/competitions/${this.props.match.params.identifier}/competition_staff.json`;
axios.get(url)
.then((response) => {
if(response.status===200){
console.log(response)
this.setState({
personal:response.data.personal
})
}
}).catch((error) => {
console.log(error)
})
this.props.MdifHasAnchorJustScorll();
}
derivefun=(url)=>{
axios.get(url).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)
});
}
render() {
this.props.MdifHasAnchorJustScorll();
const operations = <div>
<Button className={"fr"} type="primary" ghost onClick={()=>this.props.Competitionedit()}>编辑</Button>
<Button className={"fr mr20"} type="primary" ghost>
<a onClick={()=>this.derivefun(`/competitions/${this.props.match.params.identifier}/charts.xlsx?${getRandomNumber(true)}`)}>导出</a>
</Button>
</div>
const columns = [
{
title: 'usersum',
dataIndex: 'usersum',
key: 'name',
render: text => <span className={"color-blue"}>{text}</span>,
},
{
title: 'userimg',
dataIndex: 'userimg',
key: 'userimg',
render: (text, record) =>(
<a href={`/users/${record.user_login}`} target={"_blank"} className="color-dark">
<div className={"relativef"}>
<img className={"Competitionuserimg mb10"} src={getImageUrl(`images/${record.userimg===null?`avatars/User/0?1442652658`:record.userimg}`)}/>
<div className={"competimgabsoluteijmg"}><Badge count={record.competition_prize} style={{ backgroundColor: '#459BE5' }} title={record.competition_prize}/></div>
</div>
</a>),
},
{
title: 'username',
dataIndex: 'username',
key: 'username',
render: text => <span title={text} title={text}>{text}</span>,
},
{
title: 'school',
dataIndex: 'school',
key: 'school',
render: text => <span title={text} title={text}>{text}</span>,
},
{
title: 'spendtime',
dataIndex: 'spendtime',
key: 'spendtime',
render: text => <span>{text}</span>,
},
{
title: 'score',
dataIndex: 'score',
key: 'score',
render: text => <span className={"color-blue"}>{text}</span>,
},
];
const datas = [];
let {chart_rules, chartdata, data} = this.props;
let {personal}=this.state;
if(this.props&&this.props.mode!=1){
columns.some((item,key)=> {
if (item.title === "spendtime") {
columns.splice(key, 1)
return true
}
}
)
}
{chartdata===undefined?"":chartdata.teams.length===0?"":chartdata.teams.map((item,key)=>{
let list={
usersum:key+1,
userimg:item.user_image,
username:personal===undefined||personal===null?item.record_user_name:personal===true?item.record_user_name:item.team_name,
school:item.school_name,
spendtime:item.spend_time,
score:item.score<50?"< 50 分":item.score,
user_login:item.user_login,
competition_prize:item.competition_prize
}
datas.push(list)
})}
// console.log(this.props&&this.props.mode)
// console.log(columns)
return (
<div>
<style>
{
`
.ant-tabs-nav .ant-tabs-tab{
font-size: 20px;
margin: 0 10px 0 0;
}
.ant-badge{
width:90px;
}
.ant-badge sup{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
`
}
</style>
{chart_rules === undefined ? "" :
<Tabs onChange={(e) => this.props.Competitioncallback(e)} activeKey={this.props.tabkey}
tabBarExtraContent={data && data.permission.editable === true ? this.props.Competitionedittype === false ? this.props.has_url === false ? operations : "" : "" : ""}>
{chart_rules.stages.map((item,key)=>{
return(
<TabPane tab={item.name} key={item.id===null?0:item.id}>
{chart_rules.rule_contents.map((items,keys)=>{
if(item.id===items.competition_stage_id){
return(
<Content key={keys} className={"markdown-body "} dangerouslySetInnerHTML={{__html: markdownToHTML(items.content===undefined||items.content===null?"":items.content).replace(/▁/g, "▁▁▁")}}></Content>
)
}else if(item.id===null&&items.competition_stage_id===0){
return(
<Content key={keys} className={"markdown-body "} dangerouslySetInnerHTML={{__html: markdownToHTML(items.content===undefined||items.content===null?"":items.content).replace(/▁/g, "▁▁▁")}}></Content>
)
}
})}
</TabPane>
)
})}
</Tabs>}
<Col className="gutter-row Competitioncharts mt30 mb30">总排名</Col>
{chartdata===undefined?"":chartdata.teams.length===0?<Col className="gutter-row Competitioncharts mt30 mb30 rankbeicenter">
<img src={getImageUrl(`images/educoder/competitions/trophy.png`)}/>
<div className={"rankbei mt10"}>要抓住一切机会向所有人证明你自己证明你能够迎接荣耀</div>
</Col>:""}
<Row calssName={"Competition399"}>
{chartdata===undefined?"":chartdata.teams.length===0?"":chartdata.teams.map((item,key)=>{
if(key===1){
return(
<Col className="mt40" xs={{ span: 5, offset: 1 }} lg={{ span: 6, offset: 2 }}>
<Card
className={"Competitionthird"}
cover={
<div className={"Competitionthirdbox center"}>
<li className="pr Competitioncenter">
<img src={getImageUrl("images/educoder/huangguan-two.png")}/>
<div className={"mt10"}>
<a href={`/users/${item.user_login}`} target={"_blank"} className="color-dark ">
<div className={"relativef"}>
<img className={"rankingimg"} src={getImageUrl(`images/${item.user_image===null?`avatars/User/0?1442652658`:item.user_image}`)} />
<div className={"competimgabsolute"}><Badge count={item.competition_prize} style={{ backgroundColor: '#459BE5' }} title={item.competition_prize}/></div>
</div>
<p className="task-hide rankName mt5 jinshaifont">{personal===undefined||personal===null?item.record_user_name:personal===true?item.record_user_name:item.team_name}</p>
</a>
</div>
</li>
</div>
}
>
<Meta
title={<div className={"center"}>
<div className={"rankfonttop"} title={item.school_name}>{item.school_name}</div>
{this.props&&this.props.mode===1?<div className={"rankfontmid"}>{item.spend_time}</div>:""}
</div>}
description={
<div className={"rankfontbottom"}>{item.score<50?"< 50 分":item.score}</div>
}
/>
</Card>
</Col>
)
}
})}
{chartdata===undefined?"":chartdata.teams.length===0?"":chartdata.teams.map((item,key)=>{
if(key===0){
return(
<Col xs={{ span: 11, offset: 1 }} lg={{ span: 6, offset: 1 }}>
<Card
className={"Competitionfirst"}
cover={
<div className={"Competitionfirstbox center"}>
<li className="pr Competitioncenter">
<img src={getImageUrl("images/educoder/huangguan.png")}/>
<div className={"mt10"}>
<a href={`/users/${item.user_login}`} target={"_blank"} className="color-dark">
<div className={"relativef"}>
<img className={"rankingimg"} src={getImageUrl(`images/${item.user_image===null?`avatars/User/0?1442652658`:item.user_image}`)} />
<div className={"competimgabsolute"}><Badge count={item.competition_prize} style={{ backgroundColor: '#459BE5' }} title={item.competition_prize}/></div>
</div>
<p className="task-hide rankName mt5 jinshaifont">{personal===undefined||personal===null?item.record_user_name:personal===true?item.record_user_name:item.team_name}</p>
</a>
</div>
</li>
</div>
}
>
<Meta
title={<div className={"center"}>
<div className={"rankfonttop"} title={item.school_name}>{item.school_name}</div>
{this.props&&this.props.mode===1?<div className={"rankfontmid"}>{item.spend_time}</div>:""}
</div>}
description={
<div className={"rankfontbottoms"}>{item.score<50?"< 50 分":item.score}</div>
}
/>
</Card>
</Col>
)
}
})}
{chartdata===undefined?"":chartdata.teams.length===0?"":chartdata.teams.map((item,key)=>{
if(key===2){
return(
<Col className="mt30" xs={{ span: 5, offset: 1 }} lg={{ span: 6, offset: 1 }}>
<Card
className={"Competitionsecondary "}
cover={
<div className={"Competitionsecondarybox center"}>
<li className=" pr Competitioncenter ">
<img src={getImageUrl("images/educoder/huangguan-three.png")}/>
<div className={"mt10"}>
<a href={`/users/${item.user_login}`} target={"_blank"} className="color-dark">
<div className={"relativef"}>
<img className={"rankingimg"} src={getImageUrl(`images/${item.user_image===null?`avatars/User/0?1442652658`:item.user_image}`)} />
<div className={"competimgabsolute"}><Badge count={item.competition_prize} style={{ backgroundColor: '#459BE5' }} title={item.competition_prize}/></div>
</div>
<p className="task-hide rankName mt5 jinshaifont">{personal===undefined||personal===null?item.record_user_name:personal===true?item.record_user_name:item.team_name}</p>
</a>
</div>
</li>
</div>
}
>
<Meta
title={<div className={"center"}>
<div className={"rankfonttop"} title={item.school_name}>{item.school_name}</div>
{this.props&&this.props.mode===1?<div className={"rankfontmid"}>{item.spend_time}</div>:""}
</div>}
description={
<div className={"rankfontbottom"}>{item.score<50?"< 50 分":item.score}</div>
}
/>
</Card>
</Col>
)
}
})}
</Row>
{chartdata===undefined?"":chartdata.user_ranks.length===0?"":<div className={"youranklist mt50"}>
<style>
{
`
.ant-col-9 {
width: 35.5%;
}
.col13{
width:13%;
text-align:left;
}
.col24{
width: 24%;
text-align: left;
padding-left: 33px;
}
.col15{
width: 14%;
text-align: right;
}
.col12{
width: 12%;
text-align: right;
}
.col10{
width: 10%;
}
.col6{
width: 6%;
}
`
}
</style>
{chartdata.user_ranks.map((item,key)=>{
return(
<Row type="flex" key={key}>
<Col span={4} order={1} className={"col13"}>
<span className={"ranknames"}>您当前排名:{item.rank}</span>
</Col>
<Col className="userranksclass" span={3} order={2} className={"col24"}>
{personal===undefined||personal===null?item.record_user_name:personal===true?item.user_name:item.team_name}
</Col>
<Col className="textleft" span={9} order={3}>
{/*{item.team_name}*/}
</Col>
{item.cost_time=== "--"?<Col span={3} order={4} className={"col6"}>
{this.props&&this.props.mode===1?item.cost_time:""}
</Col>:<Col span={3} order={4} className={"col10"}>
{this.props&&this.props.mode===1?item.cost_time:""}
</Col>}
{item.cost_time=== "--"?<Col className="textright" span={3} order={5} className={"col15"}>
<span className={"ranknameslast"}>{item.score<50?"< 50 分":item.score}</span>
</Col>:<Col className="textright" span={3} order={5} className={"col12"}>
<span className={"ranknameslast"}>{item.score<50?"< 50 分":item.score}</span>
</Col>}
</Row>
)
})}
</div>}
<Row className={"mt20 mb80"}>
<style>
{
`
.ant-table-tbody > tr > td:nth-last-child(4){
overflow: hidden;
max-width: 100px;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.ant-table-tbody > tr > td:nth-last-child(3){
overflow: hidden;
max-width: 200px;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.ant-table-tbody>tr>td, .ant-table-thead>tr>th {
padding: 5px;
}
`
}
</style>
{chartdata === undefined ? "" : chartdata.teams.length === 0 ? "" :
<Table className="Competitiontransparent" columns={columns} dataSource={datas} showHeader={false}
pagination={false}/>}
</Row>
</div>
)
}
}
export default CompetitionContents;
@@ -0,0 +1,232 @@
import React, { Component } from 'react';
import {Button, Card, Row, Col ,Upload,Icon,message,Tabs} from 'antd';
import axios from 'axios';
import {getUploadActionUrl,getUrl,appendFileSizeToUploadFileAll,appendFileSizeToUploadFile} from 'educoder';
import TPMMDEditor from '../../../tpm/challengesnew/TPMMDEditor';
const { TabPane } = Tabs;
class CompetitionContentsMd extends Component{
constructor(props) {
super(props)
this.contentMdRef = React.createRef();
this.state={
contentFileList:[],
chartmodule_id:undefined
}
}
componentDidUpdate =(prevState)=>{
if(prevState!=this.props){
this.getchartdata();
}
}
componentDidMount(){
window.document.title = '竞赛';
this.getchartdata()
}
getchartdata=()=>{
let {mdContentdata,chart_rules}=this.props;
// is_pdf: false
if(this.props.module_type==="chart"){
let type=true;
if(chart_rules===undefined){
}else{
chart_rules.rule_contents.map((items,keys)=>{
debugger
if(parseInt(this.props.tabkey)===items.competition_stage_id){
console.log(items)
this.contentMdRef.current.setValue(items.content);
this.setState({
contentFileList:undefined,
chartmodule_id:items.id
})
type=false;
}
})
if(type===true){
this.contentMdRef.current.setValue("");
this.setState({
contentFileList:undefined,
chartmodule_id:undefined
})
}
}
}else{
let contentFileList = mdContentdata===undefined?[]:mdContentdata.attachments===undefined?[]:mdContentdata.attachments.map((item) => {
return {
id: item.id,
uid: item.id,
name: appendFileSizeToUploadFile(item),
url: item.url,
filesize: item.filesize,
status: 'done',
response:{id: item.id}
}
})
this.setState({
contentFileList:contentFileList
})
this.contentMdRef.current.setValue(mdContentdata===undefined?"":mdContentdata.md_content===undefined?"":mdContentdata.md_content || '')
}
}
handleContentUploadChange = (info) => {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let contentFileList = info.fileList;
this.setState({ contentFileList: appendFileSizeToUploadFileAll(contentFileList) });
}
}
onAttachmentRemove = (file, stateName) => {
if(file.response!=undefined){
this.props.confirm({
content: '是否确认删除?',
onOk: () => {
this.deleteAttachment(file, stateName)
},
onCancel() {
console.log('Cancel');
},
});
return false;
}
}
deleteAttachment = (file, stateName) => {
// 初次上传不能直接取uid
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
axios.delete(url, {
})
.then((response) => {
if (response.data) {
const { status } = response.data;
if (status == 0) {
console.log('--- success')
this.props.showNotification(response.data.message);
this.setState((state) => {
const index = state[stateName].indexOf(file);
const newFileList = state[stateName].slice();
newFileList.splice(index, 1);
return {
[stateName]: newFileList,
};
});
}
}
})
.catch(function (error) {
console.log(error);
});
}
handleSubmit = () => {
let {contentFileList}=this.state;
const mdContnet = this.contentMdRef.current.getValue().trim();
// if(mdContnet.length>10000){
// this.props.showNotification("内容超过10000个字");
// return
// }
let attachment_ids=undefined
if(contentFileList!=undefined){
attachment_ids= contentFileList.map(item => {
return item.response ? item.response.id : item.id
})
}
let newstage_id=parseInt(this.props.tabkey)===0||null?undefined:parseInt(this.props.tabkey)
let data={}
if(this.props.module_type==="chart"){
data={
md_content_id:this.state.chartmodule_id,
competition_module_id:this.props.module_id,
stage_id:newstage_id,
content:mdContnet,
}
}else{
data={
md_content_id:this.props.mdContentdata.md_id,
competition_module_id:this.props.mdContentdata.id,
content:mdContnet,
attachment_ids:attachment_ids
}
}
let url=`/competitions/${this.props.match.params.identifier}/update_md_content.json`;
axios.post(url,data
).then((response) => {
if(response.data.status===0){
this.props.showNotification(response.data.message);
this.props.getlistdata(this.props.thiskeys,this.props.tabkey);
this.props.hideCompetitionedit();
}else{
this.props.showNotification(response.data.message);
}
}).catch((error) => {
console.log(error)
})
}
render() {
let {contentFileList}=this.state;
let {chart_rules}=this.props;
const uploadProps = {
width: 600,
fileList: contentFileList,
multiple: true,
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUploadActionUrl()}`,
onChange: this.handleContentUploadChange,
onRemove: (file) => this.onAttachmentRemove(file, 'contentFileList'),
beforeUpload: (file) => {
console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
this.props.showNotification("文件大小必须小于150MB");
}
return isLt150M;
},
};
// console.log(this.props.tabkey)
// console.log(chart_rules)
console.log(this.props.mdContentdata)
return (
<div>
{chart_rules===undefined?"":this.props.module_type==="chart"?<Tabs activeKey={this.props.tabkey} onChange={(e)=>this.props.Competitioncallback(e)}>
{chart_rules.stages.map((item,key)=>{
return(
<TabPane tab={item.name} key={item.id===null?0:item.id}></TabPane>
)
})}
</Tabs>:""}
<TPMMDEditor ref={this.contentMdRef} placeholder="请输入内容说明" mdID={'courseContentMD'} refreshTimeout={1500}
className="courseMessageMD" initValue={this.state.description}></TPMMDEditor>
{this.props.module_type==="chart"?"":<Upload {...uploadProps} className="upload_1 newWorkUpload">
<Button className="uploadBtn">
<Icon type="upload" /> 上传附件
</Button>
(单个文件150M以内)
</Upload>}
<div className="clearfix mt30 mb30">
{/* htmlType="submit" */}
<Button type="primary" onClick={this.handleSubmit} className="defalutSubmitbtn fl mr20">提交</Button>
<a className="defalutCancelbtn fl" onClick={() => this.props.hideCompetitionedit()}>取消</ a>
</div>
</div>
)
}
}
export default CompetitionContentsMd;
@@ -0,0 +1,64 @@
import React, { Component } from 'react';
import {Tabs} from 'antd';
import axios from 'axios';
import {markdownToHTML,getImageUrl,AttachmentList} from 'educoder';
import CompetitionContentspdfdownload from './CompetitionContentspdfChild/CompetitionContentspdfdownload';
import CompetitionContentspdfpeopledata from './CompetitionContentspdfChild/CompetitionContentspdfpeopledata';
// import NoneData from "../../../courses/shixunHomework/shixunHomework";
const { TabPane } = Tabs;
class CompetitionContentspdf extends Component{
constructor(props) {
super(props)
this.state={
Tabskey:"1"
}
}
componentDidMount(){
window.document.title = '竞赛';
let query=this.props.location&&this.props.location.search;
const types = query.split('user_id=')
if(types[1]===undefined){
}else{
this.setState({
Tabskey:"2"
})
}
}
Competitioncallback=(key)=>{
this.setState({
Tabskey:key
})
}
render() {
return (
<div className={"fr"}>
<div className={"mb100 "}>
<Tabs defaultActiveKey="1" onChange={(e) => this.Competitioncallback(e)} activeKey={this.state.Tabskey}>
<TabPane tab="获奖证书下载" key="1" >
{this.state.Tabskey==="1"?<CompetitionContentspdfdownload
{...this.props}
{...this.state}
Competitioncallback={(e)=>this.Competitioncallback(e)}
/>:""}
</TabPane>
<TabPane tab="完善个人信息" key="2">
{this.state.Tabskey==="2"?<CompetitionContentspdfpeopledata
{...this.props}
{...this.state}
/>:""}
</TabPane>
</Tabs>
</div>
</div>
)
}
}
export default CompetitionContentspdf;
@@ -0,0 +1,292 @@
import React, {Component} from 'react';
import {Button, Layout, Input, Form} from 'antd';
import axios from 'axios';
import {getImageUrl} from 'educoder';
import mycompetotionchild from './mycompetotionchild.css';
import {getHiddenName} from "../../../../user/account/AccountBasicEdit";
import '../../../../courses/css/Courses.css'
export const identityMap = {"teacher": "教师", "student": "学生", "professional": "专业人士"}
class Bankcardnumberverification extends Component {
constructor(props) {
super(props)
this.state = {
basicInfo: {},
updating: '',
secondsFlag: false,
seconds: 60,
phonebool: false,
emailbool: false,
formationdata: [],
bank_account_editable: false,
leader: false,
bank_account: undefined,
certification: 1
}
}
componentDidMount() {
window.document.title = '竞赛';
// console.log("3获取用户信息");
// console.log(this.props)
try {
this.props.triggerRef(this);
}catch (e) {
}
console.log(this.props.bank_account);
//初始化值
if (this.props.bank_account) {
this.props.form.setFieldsValue({
openingbank: this.props.bank_account.bank,
subbranch: this.props.bank_account.second_bank,
subbranchs: this.props.bank_account.card_no,
})
this.setState({
openingbank: this.props.bank_account.bank,
subbranch: this.props.bank_account.second_bank,
subbranchs: this.props.bank_account.card_no,
})
}
}
setdata(bank,second_bank,card_no){
this.props.form.setFieldsValue({
openingbank: bank,
subbranch: second_bank,
subbranchs:card_no,
})
}
componentDidUpdate = (prevProps) => {
if (prevProps.bank_account != this.props.bank_account) {
console.log("componentDidUpdate");
console.log(this.props);//是新数据
console.log(prevProps);//是老数据
////console.log("Registration");
////console.log("componentDidUpdate");
////console.log(this.props.user.admin);
try {
if(this.props.bank_account){
this.props.form.setFieldsValue({
openingbank: this.props.bank_account.bank,
subbranch: this.props.bank_account.second_bank,
subbranchs: this.props.bank_account.card_no,
})
}
this.setState({
openingbank: this.props.bank_account.bank,
subbranch: this.props.bank_account.second_bank,
subbranchs: this.props.bank_account.card_no,
})
}catch (e) {
}
}
}
yhBankstrue = () => {
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
let url = `/competitions/${this.props.match.params.identifier}/prize_leader_account.json`;
axios.patch(url, {
user_id:this.props.userdata.id,
bank: values.openingbank,
second_bank: values.subbranch ,
card_no: values.subbranchs
})
.then((result) => {
try {
if (result.data.status === 0) {
try {
// console.log(values.openingbank);
// console.log(values.subbranch);
// console.log(values.subbranchs);
this.props.form.setFieldsValue({
openingbank: values.openingbank,
subbranch: values.subbranch,
subbranchs: values.subbranchs,
});
this.setState({
openingbank: values.openingbank,
subbranch: values.subbranch,
subbranchs: values.subbranchs,
})
}catch (e) {
}
try {
this.props.showNotification(`提交成功,等待审核!`);
}catch (e) {
}
try {
this.props.getdata(this.props.userdata.id);
}catch (e) {
}
try {
this.props.GetawardinformationAPI();
}catch (e) {
}
}
} catch (e) {
}
}).catch((error) => {
console.log(error);
})
}
})
}
render() {
const {getFieldDecorator} = this.props.form;
const {updating, seconds, secondsFlag, basicInfo, phonebool, emailbool, certification, formationdata, bank_account_editable, leader, bank_account} = this.state
return (
<div>
<div className="flexdirections mt36">
<p className="fontcolorsyslhei font-16">//开户行及银行卡号</p>
<p className="fontcolorsyslhui font-14 ml11">为保障奖金的及时发放请队长如实填写你名下的银行卡信息</p>
</div>
<div className="ml38">
<style>{`
.flexRow {
padding: 20px 0;
}
.flexRow .name {
margin-left: 12px;
color: #666666;
text-align: center;
flex: 0 0 100px;
}
.flexRow .description {
margin-left: 10px;
flex: 1;
color: #CDCDCD;
}
.description span {
margin-right: 20px;
color: #05101A;
}
.flexRow .status {
width: 100px;
color: #28AC7F;
text-align: right;
}
.flexTable .flexTable {
border-bottom: 1px solid #EBEBEB;
}
.settingFormsy label{
color: #666666;
font-size: 14px !important ;
}
.settingFormsy input {
width: 340px;
height: 40px;
}
.settingFormsy input.validateInput {
width: 220px;
}
.settingFormsy .formItemInline button {
width: 110px;
margin-left: 10px;
}
.settingFormsy .ant-form-item-label {
width: 60px;
text-align: left;
}
.formItemInline .ant-form-explain{
position:absolute;
bottom:-22px;
left:0px;
width:100%;
}
.yslzxueshi .ant-input{
height: 40px !important;
width: 276px !important
}
// class="ant-col ant-form-item-label"
`}</style>
<div className="settingFormsy">
<React.Fragment>
<Form>
<Form.Item
label="开户行:"
className="formItemInline hideRequireTag mb20 mt20"
>
{getFieldDecorator('openingbank', {
rules: [{
initialValue:this.state.openingbank,
required: true,
message: '请输入开户行',
}],
})(
<Input placeholder={`例如:中国工商银行`}></Input>
)}
</Form.Item>
<Form.Item
label="支行:"
className="formItemInline hideRequireTag mb20 mt20"
>
{getFieldDecorator('subbranch', {
rules: [{
initialValue: this.state.subbranch,
required: true,
message: '请输入支行',
}],
})(
<Input placeholder={`例如:长沙天河支行`}></Input>
)}
</Form.Item>
<Form.Item
label="账号:"
className="formItemInline hideRequireTag mb20 mt20"
>
{getFieldDecorator('subbranchs', {
rules: [{
initialValue: this.state.subbranchs,
required: true,
message: '请输入账号',
}],
})(
<Input placeholder={`请填写银行卡账号`}></Input>
)}
</Form.Item>
<div className="flexdirections yslzxueshi ml38 mt34">
<p className="fontcolorsyslhui1 font-14 w300 myysllineheight myyslminwidth"></p>
<div className="myyslminwidth276 flexdirections">
{/*<div className="buttongo mycompitcursor" onClick={()=>this.yhBanksfalse()}><p className="fontwenzi mycompitcursor" >取消</p></div>*/}
<Button type="primary" onClick={() => this.yhBankstrue()}>确定</Button>
</div>
</div>
</Form>
</React.Fragment>
</div>
</div>
</div>
)
}
}
const Bankcardnumberverifications = Form.create({name: 'Bankcardnumberverification'})(Bankcardnumberverification);
export default Bankcardnumberverifications;
@@ -0,0 +1,24 @@
.pdfdownload {
max-width: 791px;
height: 40px;
background: rgba(249, 249, 249, 1);
line-height: 40px;
padding-left: 15px;
}
.pdfpicture {
font-size: 16px;
color: rgba(0, 0, 0, 1);
}
.pdfdownloadfont4CACFF {
color: #4CACFF !important;
}
.pdfdownloadfont00CC5F {
color: #00CC5F;
}
.pdfdownloadfontFF6602 {
color: #FF6602;
}
@@ -0,0 +1,215 @@
import React, { Component } from 'react';
import {Button,Layout,Row, Col,Divider,Table} from 'antd';
import axios from 'axios';
import {getImageUrl,getRandomcode} from 'educoder';
import './CompetitionContentspdfdownload.css';
// import NoneData from "../../../courses/shixunHomework/shixunHomework";
class CompetitionContentspdfdownload extends Component{
constructor(props) {
super(props)
this.state={
data:undefined,
teams:undefined
}
}
componentDidMount(){
window.document.title = '竞赛';
let url=`/competitions/${this.props.match.params.identifier}/prize.json`;
let query=this.props.location&&this.props.location.search;
const types = query.split('user_id=')
let userid;
if(types[1]===undefined){
userid=this.props.user&&this.props.user.user_id;
}else{
userid=types[1];
}
axios.get(url,{params:{
user_id:userid,
}
}).then((response) => {
if(response.status===200){
let datas=response.data.teams;
if(datas.length>0){
datas.map((item,key)=>{
let lista=item.team_members;
if(lista.length>0){
console.log(lista)
lista.map((i,k)=>{
i["bank_account"]=item.bank_account;
})
}
})
}
this.setState({
data:response.data,
teams:datas,
})
}
}).catch((error) => {
console.log(error)
})
}
render() {
let {data,teams}=this.state;
const columns = [
{
title: '角色',
dataIndex: 'type',
key: 'type',
render: (text, record) => (
<span>
{record.role}
</span>
),
},
{
title: '姓名',
dataIndex: 'name',
key: 'name',
render: (text, record) => (
<span>
{record.name}
</span>
),
},
{
title: '实名认证',
dataIndex: 'namecertify',
key: 'namecertify',
render: (text, record) => (
<span>
{record.real_name_auth==="authed"?<span className={"pdfdownloadfont00CC5F"}>已认证</span>:record.real_name_auth==="authing"?<span className={"pdfdownloadfontFF6602"}></span>:record.real_name_auth==="not_authed"?<span className={"color-red"}></span>:""}
</span>
),
},
{
title: '职业认证',
key: 'certify',
dataIndex: 'certify',
render: (text, record) => (
<span>
{record.professional_auth==="authed"?<span className={"pdfdownloadfont00CC5F"}>已认证</span>:record.professional_auth==="authing"?<span className={"pdfdownloadfontFF6602"}></span>:record.professional_auth==="not_authed"?<span className={"color-red"}></span>:""}
</span>
),
},
{
title: '手机绑定',
key: 'mobile',
dataIndex: 'mobile',
render: (text, record) => (
<span>
{record.phone_binded===true?<span className={"pdfdownloadfont00CC5F"}>已绑定</span>:<span className={"color-red"}></span>}
</span>
),
},
{
title: '邮箱绑定',
key: 'mail',
dataIndex: 'mail',
render: (text, record) => (
<span>
{record.email_binded===true?<span className={"pdfdownloadfont00CC5F"}>已绑定</span>:<span className={"color-red"}></span>}
</span>
),
},
{
title: '开户行及银行卡号信息(队长填写)',
key: 'idcard',
dataIndex: 'idcard',
render: (value, record, index) => {
if (index === 0&&record.bank_account!=null) {
return {
children: <span>{record.bank_account.bank + record.bank_account.second_bank + record.bank_account.card_no}</span>,
};
}
},
},
];
let people=[ { url: '/api/competitions/xxxxx/certificates/1/personal' },
{ url: '/api/competitions/xxxxx/certificates/2/personal' },]
return (
<React.Fragment>
<Row>
<Col className={"pdfdownload"}>温馨提示填写的个人信息经审批后将提供个人获奖证书下载团队队员信息全部审批后将提供团队获奖证书下载</Col>
</Row>
<Row className={"mt30"}>
<Col className={"pdfpicture"}>证书情况</Col>
</Row>
<Row className={"mt30"}>
<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}>
<img src={ getImageUrl(`images/educoder/pdfs.png`)} />
<a className={"pdfdownloadfont4CACFF ml10"} href={getRandomcode(item.url)} download=""
title="下载" mce_href="#">立即下载</a>
</span>
)
})}</Col>
</Row>
<Row className={"mt30"}>
<Col>团队证书
{data&&data.team_certifications.length===0?<span className={"pdfpicture font-14"}>暂未生成</span>
:data&&data.team_certifications.map((item,key)=>{
return(
<span className={"mr10"} key={key}>
<img src={ getImageUrl(`images/educoder/pdfs.png`)} />
<a className={"pdfdownloadfont4CACFF ml10"} href={getRandomcode(item.url)} download=""
title="下载" mce_href="#">立即下载</a>
</span>
)
})}
</Col>
</Row>
<Divider />
<style>
{
`
.pdfdownloadTable .ant-table-column-title{
color:#000;
}
.pdfdownloadTable .ant-table-tbody > tr > td{
color:#666666;
border-bottom: 1px solid transparent;
}
.pdfdownloadTable .ant-table-thead > tr > th, .ant-table-tbody > tr > td{
text-align: center;
}
`
}
</style>
{teams&&teams.map((item,key)=>{
return(
<Row className={"mt30"} key={key}>
<Col className={"pdfpicture mb20"}>{item.name}战队信息填报概况</Col>
<Table columns={columns} dataSource={item.team_members} className={"pdfdownloadTable"} pagination={false}/>
</Row>
)
})
}
</React.Fragment>
)
}
}
export default CompetitionContentspdfdownload;
@@ -0,0 +1,597 @@
import React, {Component} from 'react';
import {Button, Layout, Input, Form} from 'antd';
import axios from 'axios';
import {setmiyah} from 'educoder';
import mycompetotionchild from './mycompetotionchild.css';
import {getHiddenName} from "../../../../user/account/AccountBasicEdit";
import '../../../../courses/css/Courses.css'
import RealNameCertificationModal from "../../../../user/modal/RealNameCertificationModal";
import Phonenumberverifications from './Phonenumberverification';
import Mailboxvalidations from './Mailboxvalidation'
import Bankcardnumberverifications from './Bankcardnumberverification'
export const identityMap = {"teacher": "教师", "student": "学生", "professional": "专业人士"}
class CompetitionContentspdfpeopledata extends Component {
constructor(props) {
super(props)
this.state = {
basicInfo: {},
updating: '',
secondsFlag: false,
seconds: 60,
phonebool: false,
emailbool: false,
formationdata: [],
bank_account_editable: false,
leader: false,
bank_account: undefined,
certification: 1,
userdata:undefined
}
}
componentDidMount() {
window.document.title = '竞赛';
console.log("获取用户信息");
console.log(this.props);
this.GetawardinformationAPI();
let query=this.props.location&&this.props.location.search;
const types = query.split('user_id=')
let userid;
if(types[1]===undefined){
userid=this.props.user&&this.props.user.user_id;
}else{
userid=types[1];
}
this.getdata(userid);
this.GetuseridApi(userid);
}
GetuseridApi=(id)=>{
//个人信息API 获取个人信息
let url = `/users/accounts/${id}.json`;
axios.get(url).then((result) => {
if (result.data) {
console.log("GetuseridApi");
console.log(result.data);
this.setState({
userdata:result.data
})
}
}).catch((error) => {
console.log(error);
})
}
GetawardinformationAPI = () => {
let url = `/competitions/${this.props.match.params.identifier}/prize.json`;
let query=this.props.location&&this.props.location.search;
const types = query.split('user_id=')
let userid;
if(types[1]===undefined){
userid=this.props.user&&this.props.user.user_id;
}else{
userid=types[1];
}
axios.get(url,{params:{
user_id:userid,
}
}).then((result) => {
if (result.data) {
this.setState({
formationdata: result.data.formationdata,
bank_account_editable: result.data.bank_account_editable, //队长是否可以编辑
leader: result.data.leader, //是否是队长
bank_account: result.data.bank_account, //队长银行卡号信息
})
}
}).catch((error) => {
console.log(error);
})
}
getdata = (id) => {
this.setState({
certification: 3
})
this.GetuseridApi(id);
};
// 绑定手机
onPhoneSubmit = () => {
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
let {id} =this.state.userdata;
let reg = /^1\d{10}$/;
if (reg.test(values.phone)) {
let url = `/users/accounts/${id}/phone_bind.json`
axios.post((url), {
phone: values.phone,
code: values.phoneValidateCode
}).then((result) => {
if (result) {
this.props.showNotification("手机号码绑定成功!");
this.setState({
phonebool: false
})
this.getdata(this.state.userdata.id);
}
}).catch((error) => {
console.log(error);
})
} else {
this.props.showNotification("请输入有效的11位手机号码");
}
}
})
}
// 绑定邮箱
onEmailSubmit = () => {
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
let {id} =this.state.userdata;
let reg = /^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/;
if (reg.test(values.email)) {
let url = `/users/accounts/${id}/email_bind.json`
axios.post((url), {
email: values.email,
code: values.emailValidateCode
}).then((result) => {
if (result) {
this.props.showNotification("邮箱地址绑定成功!");
this.setState({
emailbool: false
})
this.getdata(this.state.userdata.id);
}
}).catch((error) => {
console.log(error);
})
} else {
this.props.showNotification("请输入正确的邮箱地址");
}
}
})
}
//取消编辑
hideUpdating = (i) => {
if (i === 1) {
this.setState({
phonebool: false
})
} else if (i === 2) {
this.setState({
emailbool: false
})
} else if (i === 3) {
}
}
// 获取验证码
getCode = (index) => {
let url = `/accounts/get_verification_code.json`
let login = '';
let values = this.props.form.getFieldsValue();
if (index == 3) {
//绑定手机号码
login = values.phone;
let reg = /^1\d{10}$/;
if (reg.test(login) == false) {
this.props.showNotification(`请先输入正确的手机号码`);
return;
}
} else if (index == 4) {
// 绑定邮箱
login = values.email;
let reg = /^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/;
if (reg.test(login) == false) {
this.props.showNotification(`请先输入正确的邮箱地址`);
return;
}
}
let type = index;
if (!login) {
this.props.showNotification(`请先输入${index == 3 ? "手机号码" : "邮箱地址"}`);
return;
}
let smscode=setmiyah(login)
axios.get((url), {
params: {
login, type,smscode
}
}).then((result) => {
if (result) {
// 倒计时
this.setState({
secondsFlag: true
})
this.remainTime();
}
}).catch((error) => {
console.log(error);
})
}
// 获取验证码倒计时
remainTime = () => {
this.setState({
seconds: 60
})
this.timer = setInterval(() => {
let {seconds} = this.state;
let s = parseInt(seconds) - 1;
if (s > -1) {
this.setState({
seconds: s
})
} else {
this.setState({
secondsFlag: false
})
clearInterval(this.timer);
}
}, 1000)
}
phonebools = () => {
this.setState({
phonebool: true
})
}
emailbools = () => {
console.log("点击了邮箱");
this.setState({
emailbool: true
})
}
//立即认证
checkBasicInfo = (index) => {
if (this.state.userdata.base_info_completed == true) {
this.showRealNameCertificationModal(index)
} else {
try {
this.props.confirm({
okText: `立即完善`,
content: `请先完善基本信息`,
onOk: () => {
this.props.history.push('/account/profile/edit')
}
})
} catch (e) {
this.props.history.push(`/account/profile/edit`);
}
}
}
showRealNameCertificationModal = (index) => {
this.setState({
certification: index,
}, () => {
if (index == 1) {
this.realNameCertificationModal1.setVisible(true)
} else if (index == 2) {
this.realNameCertificationModal2.setVisible(true)
}
})
}
//绑定银行确认
yhBankstrue = () => {
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
let url = `/competitions/${this.props.match.params.identifier}/prize_leader_account.json`;
axios.patch(url, {
user_id:this.state.userdata.id,
bank: values.openingbank,
second_bank: values.subbranchs,
card_no: values.subbranch
})
.then((result) => {
try {
if (result.data.status == 0) {
// console.log(JSON.stringify(result));
this.props.showNotification(`提交成功`);
this.getdata(this.state.userdata.id);
this.GetawardinformationAPI();
}
} catch (e) {
}
}).catch((error) => {
console.log(error);
})
}
})
}
//取消认证弹框
onCancel = () => {
this.getdata(this.state.userdata.id);
}
bindRef = ref => { this.child = ref }
//撤销认证
Cancellationofapplication = (index) => {
let userid;
let query=this.props.location&&this.props.location.search;
const types = query.split('user_id=')
if(types[1]===undefined){
userid=this.props.user&&this.props.user.user_id;
}else{
userid=types[1];
}
let url = ""
if (index === 1) {
url = `/users/accounts/${userid}/authentication_apply.json`;
} else if (index === 2) {
url = `/users/accounts/${userid}/professional_auth_apply.json`;
}
axios.delete(url)
.then((response) => {
try {
if (response.data.status == 0) {
if (index === 1) {
this.props.showNotification('撤销实名认证成功')
} else if (index === 2) {
this.props.showNotification('撤销职业认证成功')
}
try {
this.getdata(this.state.userdata.id);
}catch (e) {
}
}
} catch (e) {
}
})
.catch(function (error) {
console.log(error);
});
}
render() {
const admins=this.props.user.admin;
if(admins===undefined||admins===null) {
admins === false;
}
const {updating, seconds, secondsFlag,userdata, basicInfo, phonebool, emailbool, certification, formationdata, bank_account_editable, leader, bank_account} = this.state
return (
<div className="flexdidirectionss mt17">
{this.state.certification === 1 &&userdata?
<RealNameCertificationModal {...this.props} {...this.state} basicInfo={userdata}
wrappedComponentRef={(form) => this.realNameCertificationModal1 = form}
certification={certification}
Getdata={(id) => this.getdata(id)}
onCancel={() => this.onCancel()}
></RealNameCertificationModal>
: ""}
{this.state.certification === 2 &&userdata ?
<RealNameCertificationModal {...this.props} {...this.state} basicInfo={userdata}
wrappedComponentRef={(form) => this.realNameCertificationModal2 = form}
certification={certification}
Getdata={(id) => this.getdata(id)}
onCancel={() => this.onCancel()}
></RealNameCertificationModal> : ""}
<div className="flexdirections ">
<p className="fontcolorsyslhei font-16 w98"><span className="fontcolorsysl font-16">*</span><span></span>
</p>
{userdata && userdata.authentication == "uncertified" ?
<p className="fontcolorsyslhui font-14 w200">通过实名认证后才能获得证书</p> : ""
}
{userdata && userdata.authentication == "uncertified" ?
<p className="fontcolorsysllan font-14 ml3 mycompitcursor"
onClick={() => this.checkBasicInfo(1)}>立即认证</p> : ""
}
</div>
<div className="flexdirections ml38 mt23">
<p className="fontcolorsyslhui1">姓名</p>
<p className="fontcolorsyslhei flexdirections">{userdata && userdata.name}
{
userdata && userdata.authentication == "uncertified" ? "" :
userdata && userdata.authentication == "applying" ?
<p className="flexdirections"><p className="iconfont icon-tishi colorgreenorg font-14 ml20"></p><p
className="colorgreenorg font-14 ml6">待审核</p><p
className="fontcolorsysllan borcolors font-12 w64 mycompitcursor"
onClick={() => this.Cancellationofapplication(1)}>撤销认证</p></p>
:
<p className="flexdirections">
<p className="iconfont icon-wancheng colorgreenlight font-14 ml20"></p><p
className="colorgreenlight font-14 ml6">已认证</p>
</p>
}
</p>
</div>
<div className="flexdirections ml38 mt19">
<p className="fontcolorsyslhui1">性别</p>
<p className="fontcolorsyslhei"> {userdata && userdata.gender == 0 ? "男" : "女"}</p>
</div>
<div className="flexdirections mt36 ">
<p className="fontcolorsyslhei font-16 w98"><span className="fontcolorsysl font-16">*</span><span></span>
</p>
{
userdata && userdata.professional_certification == "uncertified" ?
<p className="fontcolorsyslhui font-14 w200">通过职业认证后才能获得证书</p>
: ""}
{
userdata && userdata.professional_certification == "uncertified" ?
<p className="fontcolorsysllan font-14 ml3 mycompitcursor" onClick={() => this.checkBasicInfo(2)}>立即认证</p>
: ""}
</div>
<div className="flexdirections ml38 mt23">
<p className="fontcolorsyslhui1">职业</p>
<p
className="fontcolorsyslhei flexdirections">{userdata && userdata.identity && identityMap[userdata.identity]}
{
userdata && userdata.professional_certification == "uncertified" ?
"" :
userdata && userdata.professional_certification == "applying" ?
<p className="flexdirections">
<p className="iconfont icon-tishi colorgreenorg font-14 ml20"></p><p
className="colorgreenorg font-14 ml6">待审核</p><p
className="fontcolorsysllan borcolors font-12 w64 mycompitcursor ml6"
onClick={() => this.Cancellationofapplication(2)}>撤销认证</p>
</p>
:
<p className="flexdirections">
<p className="iconfont icon-wancheng colorgreenlight font-14 ml20"></p><p
className="colorgreenlight font-14 ml6">已认证</p>
<p className="fontcolorsysllan borcolors font-12 w64 mycompitcursor ml6"
onClick={() => this.checkBasicInfo(2)}>重新认证</p>
</p>
}
</p>
</div>
<div className="flexdirections ml38 mt19">
<p
className="fontcolorsyslhui1">{userdata && userdata.technical_title ? "职称:" : ""}{userdata && userdata.student_id ? "学号:" : ""}</p>
<p className="fontcolorsyslhei">{userdata && (userdata.technical_title || userdata.student_id)}</p>
</div>
<div className="flexdirections ml38 mt19">
<p className="fontcolorsyslhui1">学校</p>
<p className="fontcolorsyslhei">{userdata && userdata.school_name}</p>
</div>
<div className="flexdirections ml38 mt19">
<p className="fontcolorsyslhui1">院系</p>
<p className="fontcolorsyslhei"> {userdata && userdata.department_name}</p>
</div>
<div className="flexdirections mt36 ">
<p className="fontcolorsyslhei font-16 w98"><span className="fontcolorsysl font-16">*</span><span></span>
</p>
</div>
<div className="flexdirections ml38 mt23">
<p className="fontcolorsyslhui1 w60 ">手机号</p>
{
userdata && userdata.phone ?
<p className="fontcolorsyslhei w200 ">{userdata && userdata.phone}</p>
:
<p className="fontcolorsysljin w200 ">未绑定</p>
}
<p className="fontcolorsysllan mycompitcursor"
onClick={() => this.phonebools()}>{userdata && userdata.phone ? (phonebool === false ? "更换" : "") : (phonebool === false ? "立即绑定" : "")}</p>
</div>
{/*手机号绑定*/}
{
phonebool === true &&userdata ?
<Phonenumberverifications {...this.props} {...this.state} basicInfo={userdata}
hideUpdating={(i) => this.hideUpdating(i)}
getdata={(id) => this.getdata(id)}></Phonenumberverifications>
: ""
}
<div className="flexdirections ml38 mt19">
<p className="fontcolorsyslhui1 w60">Email</p>
<p className="fontcolorsyslhei w200">{userdata && userdata.mail}</p>
<p className="fontcolorsysllan mycompitcursor"
onClick={() => this.emailbools()}>{userdata && userdata.mail ? (emailbool === false ? "更换" : "") : (emailbool === false ? "立即绑定" : "")}</p>
</div>
{
emailbool === false ? "" :
(
userdata?
<Mailboxvalidations {...this.props} {...this.state} basicInfo={userdata}
hideUpdating={(i) => this.hideUpdating(i)}
getdata={(id) => this.getdata(id)}></Mailboxvalidations>
:""
)
}
{
leader === true ?
<div>
{
bank_account_editable === true ?
<Bankcardnumberverifications triggerRef={this.bindRef} {...this.props} {...this.state} basicInfo={userdata}
hideUpdating={(i) => this.hideUpdating(i)}
getdata={(id) => this.getdata(id)}
GetawardinformationAPI={() => this.GetawardinformationAPI()}
bank_account={this.state.bank_account}
></Bankcardnumberverifications>
:
admins===true?
<Bankcardnumberverifications triggerRef={this.bindRef} {...this.props} {...this.state} basicInfo={userdata}
hideUpdating={(i) => this.hideUpdating(i)}
getdata={(id) => this.getdata(id)}
GetawardinformationAPI={() => this.GetawardinformationAPI()}
bank_account={this.state.bank_account}
></Bankcardnumberverifications>
:
<div>
<div className="flexdirections mt36">
<p className="fontcolorsyslhei font-16">//开户行及银行卡号</p>
<p className="fontcolorsyslhui font-14 ml11">为保障奖金的及时发放请队长如实填写你名下的银行卡信息</p>
</div>
{
bank_account && bank_account ?
<div>
<div className="flexdirections ml38 mt19">
<p className="fontcolorsyslhui1 w56">开户行</p>
<p className="fontcolorsyslhei">{bank_account && bank_account.bank}</p>
</div>
<div className="flexdirections ml38 mt19">
<p className="fontcolorsyslhui1 w56" style={{textAlign: "right"}}>支行</p>
<p className="fontcolorsyslhei">{bank_account && bank_account.second_bank}</p>
</div>
<div className="flexdirections ml38 mt19">
<p className="fontcolorsyslhui1 w56" style={{textAlign: "right"}}>账号</p>
<p className="fontcolorsyslhei"> {bank_account && bank_account.card_no}</p>
</div>
</div>
:
""
}
</div>
}
</div>
:
<div>
{
admins===true?
<Bankcardnumberverifications triggerRef={this.bindRef} {...this.props} {...this.state} basicInfo={userdata}
hideUpdating={(i) => this.hideUpdating(i)}
getdata={(id) => this.getdata(id)}
GetawardinformationAPI={() => this.GetawardinformationAPI()}
bank_account={this.state.bank_account}
></Bankcardnumberverifications>
:""
}
</div>
}
</div>
)
}
}
export default CompetitionContentspdfpeopledata;
@@ -0,0 +1,278 @@
import React, {Component} from 'react';
import {Button, Layout, Input, Form} from 'antd';
import axios from 'axios';
import {setmiyah} from 'educoder';
import mycompetotionchild from './mycompetotionchild.css';
import {getHiddenName} from "../../../../user/account/AccountBasicEdit";
import '../../../../courses/css/Courses.css'
export const identityMap = {"teacher": "教师", "student": "学生", "professional": "专业人士"}
class Mailboxvalidation extends Component {
constructor(props) {
super(props)
this.state = {
basicInfo: {},
updating: '',
secondsFlag: false,
seconds: 60,
phonebool: false,
emailbool: false,
formationdata: [],
bank_account_editable: false,
leader: false,
bank_account: undefined,
certification: 1
}
}
componentDidMount() {
window.document.title = '竞赛';
// console.log("3获取用户信息");
// console.log(this.props);
}
// 绑定邮箱
onEmailSubmit = () => {
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
let {id} = this.props.userdata;
let reg = /^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/;
if (reg.test(values.email)) {
let url = `/users/accounts/${id}/email_bind.json`
axios.post((url), {
email: values.email,
code: values.emailValidateCode
}).then((result) => {
if (result) {
this.props.showNotification("邮箱地址绑定成功!");
this.hideUpdating(2);
this.props.getdata(id);
}
}).catch((error) => {
console.log(error);
})
} else {
this.props.showNotification("请输入正确的邮箱地址");
}
}
})
}
//取消编辑
hideUpdating = (i) => {
if (i === 1) {
this.props.hideUpdating(1);
} else if (i === 2) {
this.props.hideUpdating(2);
} else if (i === 3) {
}
}
// 获取验证码
getCode = (index) => {
let url = `/accounts/get_verification_code.json`
let login = '';
let values = this.props.form.getFieldsValue();
if (index == 3) {
//绑定手机号码
login = values.phone;
let reg = /^1\d{10}$/;
if (reg.test(login) == false) {
this.props.showNotification(`请先输入正确的手机号码`);
return;
}
} else if (index == 4) {
// 绑定邮箱
login = values.email;
let reg = /^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/;
if (reg.test(login) == false) {
this.props.showNotification(`请先输入正确的邮箱地址`);
return;
}
}
let type = index;
if (!login) {
this.props.showNotification(`请先输入${index == 3 ? "手机号码" : "邮箱地址"}`);
return;
}
let smscode=setmiyah(login)
axios.get((url), {
params: {
login, type,smscode
}
}).then((result) => {
if (result) {
// 倒计时
if(result.data.status===1){
this.props.showNotification("验证码已发送,请注意查收");
}else if(result.data.status===-2){
this.props.showNotification(result.data.message);
}
this.setState({
secondsFlag: true
})
this.remainTime();
}
}).catch((error) => {
console.log(error);
})
}
// 获取验证码倒计时
remainTime = () => {
this.setState({
seconds: 60
})
this.timer = setInterval(() => {
let {seconds} = this.state;
let s = parseInt(seconds) - 1;
if (s > -1) {
this.setState({
seconds: s
})
} else {
this.setState({
secondsFlag: false
})
clearInterval(this.timer);
}
}, 1000)
}
phonebools = () => {
this.setState({
phonebool: true
})
}
emailbools = () => {
console.log("点击了邮箱");
this.setState({
emailbool: true
})
}
render() {
const {getFieldDecorator} = this.props.form;
const {updating, seconds, secondsFlag, basicInfo, phonebool, emailbool, certification, formationdata, bank_account_editable, leader, bank_account} = this.state
console.log(emailbool);
return (
<div>
<style>{`
.flexRow {
padding: 20px 0;
}
.flexRow .name {
margin-left: 12px;
color: #666666;
text-align: center;
flex: 0 0 100px;
}
.flexRow .description {
margin-left: 10px;
flex: 1;
color: #CDCDCD;
}
.description span {
margin-right: 20px;
color: #05101A;
}
.flexRow .status {
width: 100px;
color: #28AC7F;
text-align: right;
}
.flexTable .flexTable {
border-bottom: 1px solid #EBEBEB;
}
.settingForm label{
color: #666666;
font-size: 14px !important ;
}
.settingForm input {
width: 340px;
height: 40px;
}
.settingForm input.validateInput {
width: 220px;
}
.settingForm .formItemInline button {
width: 110px;
margin-left: 10px;
}
.settingForm .ant-form-item-label {
text-align: left;
width: 84px;
}
.formItemInline .ant-form-explain{
position:absolute;
bottom:-22px;
left:0px;
width:100%;
}
`}</style>
<div className="settingForm ml38">
<React.Fragment>
<Form>
<Form.Item
label="邮箱地址"
className="formItemInline hideRequireTag mb20 mt20"
>
{getFieldDecorator('email', {
rules: [{
// initialValue: this.state.cityDefaultValue,
required: true,
message: basicInfo && basicInfo.mail ? '请输入要更换的新邮箱地址' : '请输入邮箱地址',
}],
})(
<Input placeholder={`${basicInfo && basicInfo.mail ? '请输入要更换的新邮箱地址' : '请输入邮箱地址'}`}></Input>
)}
</Form.Item>
<Form.Item
label="邮箱验证码"
className="mb20 formItemInline hideRequireTag"
>
{getFieldDecorator('emailValidateCode', {
rules: [{
// initialValue: this.state.cityDefaultValue,
required: true,
message: '请输入邮箱收到的验证码',
}],
})(
<Input placeholder="请输入邮箱收到的验证码" className="validateInput"></Input>
)}
<Button type="primary" disabled={secondsFlag} onClick={() => this.getCode(4)}>
{!secondsFlag ? "获取验证码" : `重新发送${seconds}s`}</Button>
</Form.Item>
<div className="mb20" style={{marginLeft: '204px'}}>
<Button type="primary" onClick={() => this.onEmailSubmit()}>确定</Button>
<Button type="primary grayBtn" style={{marginLeft: '20px'}}
onClick={() => this.hideUpdating(2)}>取消</Button>
</div>
</Form>
</React.Fragment>
</div>
</div>
)
}
}
const Mailboxvalidations = Form.create({name: 'Mailboxvalidation'})(Mailboxvalidation);
export default Mailboxvalidations;
@@ -0,0 +1,269 @@
import React, {Component} from 'react';
import {Button, Layout, Input, Form} from 'antd';
import axios from 'axios';
import {setmiyah} from 'educoder';
import mycompetotionchild from './mycompetotionchild.css';
import {getHiddenName} from "../../../../user/account/AccountBasicEdit";
import '../../../../courses/css/Courses.css'
import RealNameCertificationModal from "../../../../user/modal/RealNameCertificationModal";
export const identityMap = {"teacher": "教师", "student": "学生", "professional": "专业人士"}
class Phonenumberverification extends Component {
constructor(props) {
super(props)
this.state = {
updating: '',
secondsFlag: false,
seconds: 60,
phonebool: false,
emailbool: false,
formationdata: [],
bank_account_editable: false,
leader: false,
bank_account: undefined,
certification: 1
}
}
componentDidMount() {
window.document.title = '竞赛';
// console.log("获取用户信息");
// console.log(this.props);
}
// 绑定手机
onPhoneSubmit = () => {
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
let {id} = this.props.userdata;
let reg = /^1\d{10}$/;
if (reg.test(values.phone)) {
let url = `/users/accounts/${id}/phone_bind.json`
axios.post((url), {
phone: values.phone,
code: values.phoneValidateCode
}).then((result) => {
if (result) {
this.props.showNotification("手机号码绑定成功!");
this.props.hideUpdating(1)
this.props.getdata(id);
}
}).catch((error) => {
console.log(error);
})
} else {
this.props.showNotification("请输入有效的11位手机号码");
}
}
})
}
//取消编辑
hideUpdating = (i) => {
if (i === 1) {
this.props.hideUpdating(1);
} else if (i === 2) {
this.props.hideUpdating(2);
} else if (i === 3) {
}
}
// 获取验证码
getCode = (index) => {
let url = `/accounts/get_verification_code.json`
let login = '';
let values = this.props.form.getFieldsValue();
if (index == 3) {
//绑定手机号码
login = values.phone;
let reg = /^1\d{10}$/;
if (reg.test(login) == false) {
this.props.showNotification(`请先输入正确的手机号码`);
return;
}
} else if (index == 4) {
// 绑定邮箱
login = values.email;
let reg = /^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/;
if (reg.test(login) == false) {
this.props.showNotification(`请先输入正确的邮箱地址`);
return;
}
}
let type = index;
if (!login) {
this.props.showNotification(`请先输入${index == 3 ? "手机号码" : "邮箱地址"}`);
return;
}
let smscode=setmiyah(login)
axios.get((url), {
params: {
login, type,smscode
}
}).then((result) => {
if (result) {
// 倒计时
if(result.data.status===1){
this.props.showNotification("验证码已发送,请注意查收");
}else if(result.data.status===-2){
this.props.showNotification(result.data.message);
}
this.setState({
secondsFlag: true
})
this.remainTime();
}
}).catch((error) => {
console.log(error);
})
}
// 获取验证码倒计时
remainTime = () => {
this.setState({
seconds: 60
})
this.timer = setInterval(() => {
let {seconds} = this.state;
let s = parseInt(seconds) - 1;
if (s > -1) {
this.setState({
seconds: s
})
} else {
this.setState({
secondsFlag: false
})
clearInterval(this.timer);
}
}, 1000)
}
phonebools = () => {
this.setState({
phonebool: true
})
}
render() {
const {getFieldDecorator} = this.props.form;
const {updating, seconds, secondsFlag, phonebool, emailbool, certification, formationdata, bank_account_editable, leader, bank_account} = this.state
const {basicInfo} = this.props
console.log(emailbool);
return (
<div>
<style>{`
.flexRow {
padding: 20px 0;
}
.flexRow .name {
margin-left: 12px;
color: #666666;
text-align: center;
flex: 0 0 100px;
}
.flexRow .description {
margin-left: 10px;
flex: 1;
color: #CDCDCD;
}
.description span {
margin-right: 20px;
color: #05101A;
}
.flexRow .status {
width: 100px;
color: #28AC7F;
text-align: right;
}
.flexTable .flexTable {
border-bottom: 1px solid #EBEBEB;
}
.settingForm label{
color: #666666;
font-size: 14px !important ;
}
.settingForm input {
width: 340px;
height: 40px;
}
.settingForm input.validateInput {
width: 220px;
}
.settingForm .formItemInline button {
width: 110px;
margin-left: 10px;
}
.settingForm .ant-form-item-label {
text-align: left;
width: 84px;
}
.formItemInline .ant-form-explain{
position:absolute;
bottom:-22px;
left:0px;
width:100%;
}
`}</style>
<div className="settingForm ml38">
<React.Fragment>
<Form>
<Form.Item
label="你的手机号"
className="formItemInline hideRequireTag mb20 mt20"
>
{getFieldDecorator('phone', {
rules: [{
// initialValue: this.state.cityDefaultValue,
required: true,
message: `请输入要${basicInfo.phone ? '更换' : '绑定'}的手机号码`,
}],
})(
<Input placeholder={`请输入要${basicInfo.phone ? '更换' : '绑定'}的手机号码`}></Input>
)}
</Form.Item>
<Form.Item
label="手机验证码"
className="mb20 formItemInline hideRequireTag"
>
{getFieldDecorator('phoneValidateCode', {
rules: [{
// initialValue: this.state.cityDefaultValue,
required: true,
message: '请输入手机获取的验证码',
}],
})(
<Input placeholder="请输入手机获取的验证码" className="validateInput"></Input>
)}
<Button type="primary" disabled={secondsFlag} onClick={() => this.getCode(3)}>
{!secondsFlag ? "获取验证码" : `重新发送${seconds}s`}
</Button>
</Form.Item>
<div className="mb20" style={{marginLeft: '204px'}}>
<Button type="primary" onClick={() => this.onPhoneSubmit()}>确定</Button>
<Button type="primary grayBtn" style={{marginLeft: '20px'}}
onClick={() => this.hideUpdating(1)}>取消</Button>
</div>
</Form>
</React.Fragment>
</div>
</div>
)
}
}
const Phonenumberverifications = Form.create({name: 'Phonenumberverification'})(Phonenumberverification);
export default Phonenumberverifications;
@@ -0,0 +1,314 @@
/*垂直布局
*/
.flexdirectionjust {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.directstwebkitflex {
display: flex;
display: -webkit-flex;
flex-direction: column;
align-items: center;
}
.diredisplayitflex {
display: flex;
display: -webkit-flex;
align-items: center;
}
/*垂直布局*/
/*靠左侧
一 二 三 四 五 六 七 八
*/
.flexdirection {
display: flex;
flex-direction: row;
}
.flexdirections {
display: flex;
flex-direction: initial;
}
/*靠左侧
*/
/*靠右侧八 七 六 五 四 三 二 一*/
.flexdirectionss {
display: flex;
flex-direction: row-reverse;
}
/*垂直布局
*/
.flexdidirectionss {
display: flex;
flex-direction: column;
}
/*垂直布局
*/
.flexdidireverses {
display: flex;
flex-direction: column-reverse;
}
.fontcolorsysl {
color: #FF0000
}
.fontcolorsyslhei {
color: #000000
}
.fontcolorsyslhui {
color: #888888
}
.fontcolorsyslhui1 {
color: #666666;
}
.fontcolorsysllan {
color: #4CACFF
}
.fontcolorsysljin {
color: #DD7600
}
.w200 {
width: 200px;
}
.w64 {
width: 64px;
}
.w60 {
width: 60px;
}
.w98 {
width: 98px;
}
.myysllineheight {
line-height: 40px;
}
.myyslminwidth {
min-width: 60px;
}
.myyslminwidth276 {
width: 276px;
}
.buttongo {
background: #E7E7E7;
border: 1px solid #E7E7E7;
width: 60px;
height: 30px;
border-radius: 4px;
color: #999999;
font-size: 16px;
}
.buttongo2 {
background: #4CACFF;
border: 1px solid #4CACFF;
width: 64px;
height: 32px;
border-radius: 4px;
color: #FFFFFF;
font-size: 16px;
}
.fontwenzi {
text-align: center;
line-height: 30px;
}
.mt17 {
margin-top: 17px;
}
.mt36 {
margin-top: 36px;
}
.mt23 {
margin-top: 23px;
}
.mt19 {
margin-top: 19px;
}
.mt23 {
margin-top: 23px;
}
.mt34 {
margin-top: 34px;
}
.ml11 {
margin-left: 11px;
}
.ml38 {
margin-left: 38px;
}
.ml7 {
margin-left: 7px;
}
.colorgreenlight {
color: #6EC76E
}
.colorgreenorg {
color: #FF7300;
}
.borcolors {
border: 1px solid #4CACFF;
text-align: center;
}
.mycompitcursor {
cursor: pointer;
}
.basicForm {
background: #fff;
padding: 30px;
margin-bottom: 10px;
box-sizing: border-box;
width: 100%;
min-height: 390px;
}
.basicForm .title {
font-size: 16px;
padding-left: 30px;
margin-bottom: 10px;
}
.flexTable {
display: flex;
flex-direction: column;
}
.flexRow {
display: flex;
}
.mb15 {
margin-bottom: 15px !important;
}
/* BUTTOn */
.ant-btn {
border-radius: 2px;
}
button.ant-btn.ant-btn-primary.grayBtn {
background: #CBCBCB;
border-color: #CBCBCB;
}
.borderBottom {
border-bottom: 1px solid #4CACFF;
}
/* form ---------------- START */
.formItemInline {
display: flex;
}
.formItemInline .ant-form-item-control-wrapper {
display: inline-block;
}
.hideRequireTag .ant-form-item-required:before {
display: none;
}
/* .basicForm .ant-form-item-label {
width: 100px;
padding-right: 10px;
}
.basicForm .ant-form-item-label label {
color: #979797
} */
.courseNormalForm .ant-select-show-search {
height: 40px;
}
.courseNormalForm .ant-select-auto-complete.ant-select .ant-input {
height: 40px;
}
.courseNormalForm .ant-select-search__field__mirror {
height: 40px;
}
.courseNormalForm .ant-input-lg {
height: 40px;
}
.courseNormalForm .ant-select-selection--single {
height: 40px;
}
.courseNormalForm .ant-select-auto-complete.ant-select .ant-select-selection--single {
height: 40px
}
.courseNormalForm .ant-input-affix-wrapper {
height: 40px;
}
/* 职业 */
.courseNormalForm .ant-select-selection-selected-value {
line-height: 38px
}
.courseNormalForm input {
height: 40px;
}
.w300 {
width: 300px;
}
.w56 {
width: 56px;
}
@@ -0,0 +1,92 @@
import React, { Component } from 'react';
import { Redirect } from 'react-router';
import { Route, Link, Switch } from "react-router-dom";
import Loading from '../../Loading';
import Loadable from 'react-loadable';
import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
import { CNotificationHOC } from '../courses/common/CNotificationHOC';
//新版竞赛首页
const CompetitionsIndex = Loadable({
loader: () => import('./Competitimain/CompetitionsIndex'),
loading: Loading,
})
//竞赛详情页
const CompetitionCommon=Loadable({
loader: () => import('./Competitioncommon/CompetitionCommon'),
loading: Loading,
})
//战队详情
const CompetitionTeams = Loadable({
loader: () => import('./Competition_teams/Competitionteams'),
loading: Loading,
})
//团队竞赛报名
const Registration = Loadable({
loader: () => import('../competition/Registration'),
loading: Loading,
});
class Competitions extends Component {
constructor(props) {
super(props)
}
componentDidMount(){
window.document.title = '竞赛';
}
render() {
return (
<div className="newMain clearfix">
<Switch>
{/*新版竞赛战队详情*/}
<Route path="/competitions/:identifier/competition_teams/:competition_team_id"
render={
(props) => (<CompetitionTeams {...this.props} {...props} {...this.state} />)
}
></Route>
{/*新版竞赛报名*/}
<Route
path="/competitions/:identifier/enroll"
render={
(props) => (<Registration {...this.props} {...props} {...this.state}/>)
}
/>
{/*新版竞赛详情页面*/}
<Route path="/competitions/:identifier"
render={
(props) => (<CompetitionCommon {...this.props} {...props} {...this.state} />)
}
></Route>
{/*新版竞赛首页*/}
<Route path="/competitions"
render={
(props) => (<CompetitionsIndex {...this.props} {...props} {...this.state} />)
}
></Route>
</Switch>
</div>
);
}
}
export default CNotificationHOC() (TPMIndexHOC (Competitions)) ;
@@ -0,0 +1,100 @@
import React, { Component } from 'react';
import { SnackbarHOC } from 'educoder';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import Loadable from 'react-loadable';
import Loading from '../../Loading';
import axios from 'axios';
// import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
import { CNotificationHOC } from './common/CNotificationHOC'
import "./css/Courses.css"
//引入对应跳转的组件
const ListPageIndex = Loadable({
loader: () => import('./ListPageIndex'),
loading:Loading,
})
// 讨论
const BoardsNew= Loadable({
loader: () => import('./boards/BoardsNew'),
loading: Loading,
})
const TopicDetail= Loadable({
loader: () => import('./boards/TopicDetail'),
loading: Loading,
})
// 讨论
const Boards= Loadable({
loader: () => import('./boards'),
loading: Loading,
})
class CoursesIndex extends Component{
constructor(props) {
super(props)
this.state={
// coursedata:undefined
}
}
componentDidMount(){
}
render() {
const common = {
}
return (
<Switch {...this.props}>
{/*课堂讨论*/}
{/* /board/5464/messages/new */}
{/* <Route exact path="/courses/:coursesId/board/:boardId/messages/:topicId/edit"
render={
(props) => (<BoardsNew {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route exact path="/courses/:coursesId/board/:boardId/messages/new" component={BoardsNew}
render={
(props) => (<BoardsNew {...this.props} {...props} {...this.state} {...common} />)
}
></Route> */}
{/*课堂讨论*/}
<Route exact path="/courses/:coursesId/boards/:boardId/messages/:topicId/edit"
render={
(props) => (<BoardsNew {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route exact path="/courses/:coursesId/boards/:boardId/messages/new"
render={
(props) => (<BoardsNew {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route exact path="/courses/:coursesId/boards/:boardId/messages/:topicId"
render={
(props) => (<TopicDetail {...this.props} {...this.state} {...props} {...common} />)
}
></Route>
<Route exact path="/courses/:coursesId/boards/:boardId/messages/:topicId"
render={
(props) => (<TopicDetail {...this.props} {...this.state} {...props} {...common} />)
}
></Route>
<Route exact path="/courses/:coursesId/boards/:boardId"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
</Switch>
)
}
}
export default CNotificationHOC() ( SnackbarHOC() ( (CoursesIndex) ));
+963
View File
@@ -0,0 +1,963 @@
import React, { Component } from 'react';
import { SnackbarHOC } from 'educoder';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import { withRouter } from 'react-router'
import Loadable from 'react-loadable';
import Loading from '../../Loading';
import axios from 'axios';
import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
import { CNotificationHOC } from './common/CNotificationHOC'
import {ImageLayerOfCommentHOC} from '../page/layers/ImageLayerOfCommentHOC'
import "./css/Courses.css"
//引入对应跳转的组件
const ListPageIndex = Loadable({
loader: () => import('./ListPageIndex'),
loading:Loading,
})
//课堂首页
const CoursesHome = Loadable({
loader: () => import('./coursesHome/CoursesHome'),
loading:Loading,
})
//课堂详情页
const CoursesDetail= Loadable({
loader: () => import('./coursesDetail/CoursesDetail'),
loading:Loading,
})
// 课堂讨论
const BoardIndex = Loadable({
loader: () => import('./BoardIndex'),
loading:Loading,
})
const GraduationTopics= Loadable({
loader: () => import('./graduation/topics'),
loading: Loading,
})
const GraduationTopicsDetail= Loadable({
loader: () => import('./graduation/topics/GraduateTopicDetail'),
loading: Loading,
})
const GraduateTopicPostWorksNew= Loadable({
loader: () => import('./graduation/topics/GraduateTopicPostWorksNew'),
loading: Loading,
})
const GraduateTopicNew= Loadable({
loader: () => import('./graduation/topics/GraduateTopicNew'),
loading: Loading,
})
const GraduationTasks= Loadable({
loader: () => import('./graduation/tasks'),
loading: Loading,
})
// 资源
const Resourcelist= Loadable({
loader: () => import('./Resource/index'),
loading: Loading,
})
//新建实训
const WrappedCoursesNewApp= Loadable({
loader: () => import('./new/CoursesNew'),
loading: Loading,
})
//新建精品课堂
const WrappedCoursesNewAppGoldclass= Loadable({
loader: () => import('./new/Goldsubject'),
loading: Loading,
})
const ShixunHomeworkPage =Loadable({
loader: () => import('./shixunHomework/ShixunHomeworkPage'),
loading: Loading,
})
//实训作业
const ShixunHomework= Loadable({
loader: () => import('./shixunHomework/shixunHomework'),
loading: Loading,
})
//实训作业设置
const Trainingjobsetting= Loadable({
loader: () => import('./shixunHomework/Trainingjobsetting'),
loading: Loading,
})
//实训作业列表教师
// //实训作业列表学生未完成的
// const Studentnotcompleted= Loadable({
// loader: () => import('./shixunHomework/Listofworksstudentone'),
// loading: Loading,
// })
//实训作业列表完成的
const Studenthascompleted= Loadable({
loader: () => import('./shixunHomework/Listofworksstudentone'),
loading: Loading,
})
//实训作业提交总结
const CommitSummary = Loadable({
loader: () => import('./shixunHomework/CommitSummary'),
loading: Loading,
})
//实训作业设置
const Workquestionandanswer= Loadable({
loader: () => import('./shixunHomework/Workquestionandanswer'),
loading: Loading,
})
//实训报告
const ShixunWorkReport= Loadable({
loader: () => import('./shixunHomework/ShixunWorkReport'),
loading: Loading,
})
//实训查重
const ShixunStudentWork= Loadable({
loader: () => import('./shixunHomework/ShixunStudentWork'),
loading: Loading,
})
//实训查重详情
const ShixunWorkDetails= Loadable({
loader: () => import('./shixunHomework/ShixunWorkDetails'),
loading: Loading,
})
//问卷
const Poll= Loadable({
loader: () => import('./poll/Poll'),
loading: Loading,
})
//问卷新建
const PollNew= Loadable({
loader: () => import('./poll/PollNew'),
loading: Loading,
})
//问卷答题
const PollInfo= Loadable({
loader: () => import('./poll/PollInfo'),
loading: Loading,
})
//问卷详情
const PollDetail= Loadable({
loader: () => import('./poll/PollDetailIndex'),
loading: Loading,
})
//试卷
const Exercise= Loadable({
loader: () => import('./exercise/Exercise'),
loading: Loading,
})
const Testpapersettinghomepage =Loadable({
loader: () => import('./exercise/Testpapersettinghomepage'),
loading: Loading,
})
const ExerciseNew =Loadable({
loader: () => import('./exercise/ExerciseNew'),
loading: Loading,
})
const ExerciseReviewAndAnswer=Loadable({
loader:() => import('./exercise/ExerciseReviewAndAnswer'),
loading: Loading,
})
//学生列表
const Studentshavecompletedthelist = Loadable({
loader: () => import('./exercise/Studentshavecompletedthelist'),
loading: Loading,
})
//教师列表
const TeacherList= Loadable({
loader: () => import('./members/teacherList'),
loading: Loading,
})
//学生列表
const StudentsList= Loadable({
loader: () => import('./members/studentsList'),
loading: Loading,
})
//普通作业
const CommonWork= Loadable({
loader: () => import('./busyWork/Index'),
loading: Loading,
})
const GroupWork= Loadable({
loader: () => import('./busyWork/IndexGroup'),
loading: Loading,
})
//
// //分组作业
// const GroupWork= Loadable({
// loader: () => import('./busyWork/GroupWork'),
// loading: Loading,
// })
//毕设任务列表新建
const GraduationTaskseditApp= Loadable({
loader: () => import('./graduation/tasks/GraduationTasksedit'),
loading: Loading,
})
//毕设任务列表编辑
const GraduationTasksnewApp= Loadable({
loader: () => import('./graduation/tasks/GraduationTasksnew'),
loading: Loading,
})
//毕设任务列表设置
const GraduationTasksquestions= Loadable({
loader: () => import('./graduation/tasks/GraduationTaskssettingquestions'),
loading: Loading,
})
//毕设任务列表
const GraduationTaskssettinglist= Loadable({
loader: () => import('./graduation/tasks/GraduationTaskDetail'),
loading: Loading,
})
//毕业任务设置
const GraduationTaskssetting=Loadable({
loader: () => import('./graduation/tasks/GraduationTaskssetting'),
loading: Loading,
})
// 评阅列表
const GraduationTasksappraise=Loadable({
loader: () => import('./graduation/tasks/GraduationTasksappraise'),
loading: Loading,
})
//创建作品
const GraduationTasksSubmitnewApp=Loadable({
loader: () => import('./graduation/tasks/GraduationTasksSubmitnew'),
loading: Loading,
})
//修改作品
const GraduationTasksSubmiteditApp=Loadable({
loader: () => import('./graduation/tasks/GraduationTasksSubmitedit'),
loading: Loading,
})
//排序
const Ordering=Loadable({
loader: () => import('../../modules/courses/ordering/Ordering'),
loading: Loading,
});
class CoursesIndex extends Component{
constructor(props) {
super(props)
this.state={
coursesids: undefined,
coursesidsfun:this.coursesidsfun,
updataleftNavfun:this.updataleftNav,
course_modules:undefined,
hidden_modules:[],
is_teacher:false,
isaloadtype:true,
coursesidtype:undefined,
locationNavfun:this.locationNav,
unlocationNavfun:this.unlocationNavfun
}
}
coursesidsfun=(id,type)=>{
this.setState({
coursesids:id,
coursesidtype:type
})
}
componentDidMount(){
// this.updataleftNav()
this.historyArray = [window.location.pathname];
this.props.history.listen( location => {
this.historyArray.unshift(window.location.pathname);
this.historyArray.length = 2;
//Do your stuff here
});
}
//更新左边课堂导航
updataleftNav=()=>{
let query=this.props.location.pathname
let {isaloadtype}=this.state;
let types=isaloadtype===false?true:false
const type = query.split('/');
let id=parseInt(type[2])
if(isNaN(id)){
return
}
const querys = this.props.location.search;
var dataqueryss={}
try {
var foqus=this.foo(querys);
if(JSON.stringify(foqus) ==="{}"){
this.setState({
dataquerys:{},
});
}else{
this.setState({
dataquerys:foqus,
});
dataqueryss=foqus;
}
}catch (e) {
this.setState({
dataquerys:{},
})
}
// let id=this.props.match.params.coursesId;
let url ="/courses/"+id+"/left_banner.json"
axios.get(url,
{params:
dataqueryss
}
).then((response) => {
if(response!=undefined){
if(response.data&&response.data){
this.setState({
course_modules:response.data.course_modules,
hidden_modules:response.data.hidden_modules,
is_teacher:response.data.is_teacher,
isaloadtype:types
})
this.locationNav(response.data.course_modules)
}
}
})
};
foo=(url)=> {
var json = {};
var regExp = /[\?\&](\w+)(=?)(\w*)/g;
var arr;
do {
arr = regExp.exec(url);
// console.log(arr); // arr = [完整的字符串, key, 等号或'', value或'']
if (arr) {
var key = arr[1];
var value = arr[3];
// arr[2] === ''时, value = undefined
if (!arr[2])
value = undefined;
json[key] = value;
}
} while (arr);
return json;
};
locationNav=(list)=>{
if(list){
let url=this.props.location.pathname;
const type = url.split('/');
let filesId=parseInt(type[4]);
// let urls=this.props.match.url
var lists=list;
lists.forEach((item,index)=>{
if(url===item.category_url){
this.setState({
indexs:index,
mainurl:item.category_url
})
this.coursesidsfun(item.id,"node")
}else{
if(item.second_category!=undefined&&item.second_category.length!=0){
item.second_category.forEach((iem,key)=>{
if(url===iem.second_category_url){
if( parseInt(filesId)===iem.category_id||parseInt(filesId)===item.id){
this.setState({
indexs:index,
url:url,
mainurl:item.category_url
})
}
this.coursesidsfun(item.id,"child")
}
})
}
}
})
}
}
unlocationNavfun=(val)=>{
this.setState({
indexs:val,
})
}
// // 老师、管理员等
// isAdmin = () => {
// return this.state.coursedata && this.state.coursedata.course_identity < 4
// }
// // 学生
// isStudent = () => {
// return this.state.coursedata && this.state.coursedata.course_identity === 4
// }
// isAdminOrStudent = () => {
// return this.state.coursedata && this.state.coursedata.course_identity <= 4
// }
// // 非课堂成员
// isNotMember = () => {
// return this.state.coursedata && this.state.coursedata.course_identity === 5
// }
// isCourseidentity = () => {
//
// if(this.state.coursedata&&this.state.coursedata.course_identity < 4){
// return "isAdmin"
// // 老师、管理员等
// }else if(this.state.coursedata&&this.state.coursedata.course_identity === 4){
// return "isStudent"
// // 学生
// }else if(this.state.coursedata&&this.state.coursedata.course_identity === 5){
// return "isNotMember"
// // 非课堂成员
// }
//
// }
// componentDidMount(){
// // console.log(this.props.location.pathname)
// let query=this.props.location.pathname;
// const type = query.split('/');
// if (!type[2]) {
// return;
// }
// let id=parseInt(type[2])
//
// let url="/courses/"+id+"/top_banner.json"
// axios.get(url).then((result)=>{
// if(result.status===200){
// let data=result.data;
// this.setState({
// coursedata:data
// })
//
// // console.log(data)
// }
// }).catch((error)=>{
// console.log(error);
// })
// }
render() {
const common = {
previousPathName: this.historyArray && this.historyArray[1]
// isAdmin: this.isAdmin,
// isStudent: this.isStudent,
// isAdminOrStudent: this.isAdminOrStudent,
// isNotMember: this.isNotMember,
// isCourseidentity:this.isCourseidentity
}
// const commons = {
// admind: this.props.user,
// // isAdminOrStudent: this.isAdminOrStudent,
//
// // isNotMember: this.isNotMember,
// // isCourseidentity:this.isCourseidentity
// }
// console.log("commonscommons")
// console.log(commons)
return (
<Switch {...this.props}>
{/*排序*/}
<Route path="/courses/:coursesId/ordering/:ordering_type/:main_id"
render={
(props) => (<Ordering {...this.props} {...props} {...this.state} />)
}
></Route>
{/*视频列表*/}
<Route path="/courses/:coursesId/course_videos"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 资源列表页 */}
<Route path="/courses/:coursesId/file/:Id" exact
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
<Route path="/courses/:coursesId/files/:main_id"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
<Route exact path="/courses/:coursesId/boards/:boardId"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*课堂讨论*/}
<Route path="/courses/:coursesId/boards/:boardId"
render={
(props) => (<BoardIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 毕业设计 */}
{/* 毕设问答 */}
<Route path="/courses/:coursesId/graduation_topics/postwork/new"
render={
(props) => (<GraduateTopicPostWorksNew {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 毕设选题新建页 */}
<Route path="/courses/:coursesId/graduation_topics/new"
render={
(props) => (<GraduateTopicNew {...this.props} {...props} {...this.state} {...common}/>)
}></Route>
{/* 毕设选题编辑页*/}
<Route path="/courses/:coursesId/graduation_topics/:topicId/edit"
render={
(props) => (<GraduateTopicNew {...this.props} {...props} {...this.state} {...common}/>)
}></Route>
{/* 毕设选题详情页 */}
<Route path="/courses/:course_id/graduation_topics/:graduation_topic_id/detail"
render={
(props)=>(<GraduationTopicsDetail {...this.props} {...props} {...this.state}></GraduationTopicsDetail>)
}
></Route>
{/* 毕设选题列表 */}
<Route path="/courses/:coursesId/graduation_topics/:Id" exact
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common}/>)
}></Route>
{/*————————————————————————————————————————————————————————————————————————————————————*/}
{/* 作品评阅 https://www.trustie.net/issues/19981 */}
<Route path="/courses/:coursesId/graduation_tasks/:category_id/appraise"
render={
(props) => (<GraduationTasksappraise {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 新建作品 */}
<Route path="/courses/:coursesId/graduation_tasks/:category_id/works/:task_Id/new"
render={
(props) => (<GraduationTasksSubmitnewApp {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 修改作品 */}
<Route path="/courses/:coursesId/graduation_tasks/:category_id/works/:work_Id/edit"
render={
(props) => (<GraduationTasksSubmiteditApp {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 设置毕设任务 https://www.trustie.net/issues/19981 */}
{/* <Route path="/courses/:coursesId/graduation_tasks/:category_id/:task_Id/setting"
render={
(props) => (<GraduationTaskssetting {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
<Route path="/courses/:coursesId/graduation_tasks/:category_id/:task_Id/questions"
render={
(props) => (<GraduationTasksquestions {...this.props} {...props} {...this.state} />)
}></Route> */}
<Route path="/courses/:coursesId/graduation_tasks/:category_id/detail/:task_Id"
render={
(props) => (<GraduationTaskssettinglist {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 修改毕设任务 https://www.trustie.net/issues/19981 */}
<Route path="/courses/:coursesId/graduation_tasks/:category_id/edit"
render={
(props) => (<GraduationTaskseditApp {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 新建毕设任务 https://www.trustie.net/issues/19981 */}
<Route path="/courses/:coursesId/graduation_tasks/:category_id/new"
render={
(props) => (<GraduationTasksnewApp {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 毕设任务列表 https://www.trustie.net/issues/19981 */}
<Route path="/courses/:coursesId/graduation_tasks/:Id" exact
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*/!* 毕业设计主 https://www.trustie.net/issues/19981 *!/*/}
{/*<Route path="/courses/:coursesId/graduation"*/}
{/*render={*/}
{/*(props) => (<GraduationTopics {...this.props} {...props} {...this.state} {...common}/>)*/}
{/*}*/}
{/*></Route>*/}
{/*/!* 资源子目录 https://www.trustie.net/issues/19917 *!/*/}
{/*<Route path="/courses/:coursesId/attachment/attachment/:attachmentId"*/}
{/*render={*/}
{/*(props) => (<Files {...this.props} {...props} {...this.state} {...common}/>)*/}
{/*}*/}
{/*></Route>*/}
{/* 教师列表*/}
<Route path="/courses/:coursesId/teachers"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 学生列表*/}
<Route path="/courses/:coursesId/students"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 分班列表 */}
<Route path="/courses/:coursesId/course_groups/:course_group_id"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
<Route path="/courses/:coursesId/course_groups"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 普通作业 */}
<Route path="/courses/:coursesId/common_homeworks/:category_id" exact
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 分组作业 */}
<Route path="/courses/:coursesId/group_homeworks/:category_id" exact
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 普通作业 */}
<Route path="/courses/:coursesId/common_homeworks/" strict
render={
(props) => (<CommonWork {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 分组作业 */}
<Route path="/courses/:coursesId/group_homeworks/" strict
render={
(props) => (<GroupWork {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 问卷答题 */}
<Route path="/courses/:coursesId/polls/:pollId/users/:login"
render={
(props) => (<PollInfo {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 问卷详情 */}
<Route path="/courses/:coursesId/polls/:pollId/detail"
render={
(props) => (<PollDetail {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 问卷新建 */}
<Route path="/courses/:coursesId/polls/:pollid/:news"
render={
(props) => (<PollNew {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*/!* 问卷编辑 *!/*/}
{/*<Route path="/courses/:coursesId/polls/:pollid/:news"*/}
{/*render={*/}
{/*(props) => (<PollNew {...this.props} {...props} {...this.state} {...common} />)*/}
{/*}*/}
{/*></Route>*/}
{/* 问卷 */}
<Route path="/courses/:coursesId/polls/:Id"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 试卷查看/评阅 */}
<Route exact path="/courses/:coursesId/exercises/:Id/users/:userId"
render={
(props)=>(<ExerciseReviewAndAnswer {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*试卷新建 */}
<Route exact path="/courses/:coursesId/exercises/new"
render={
(props) => (<ExerciseNew {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*试卷新建 */}
<Route exact path="/courses/:coursesId/exercises/:Id/edit"
render={
(props) => (<ExerciseNew {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*试卷设置主页*/}
<Route path="/courses/:coursesId/exercises/:Id/student_exercise_list"
render={
(props) => (<Testpapersettinghomepage {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*学生完成列表*/}
<Route
path="/courses/:coursesId/exercises/:Id/Studentshavecompletedthelist"
render={
(props) => (<Studentshavecompletedthelist {...this.props} {...props} {...this.state} {...common}/>)
}
>
</Route>
{/* 试卷 */}
<Route path="/courses/:coursesId/exercises/:Id"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*实训查重详情*/}
<Route path="/courses/:coursesId/shixun_homeworks/:homeworkid/review_detail/:userid"
render={
(props) => (<ShixunWorkDetails {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
<Route path="/courses/:coursesId/shixun_homework/:homeworkid/review_detail/:userid"
render={
(props) => (<ShixunWorkDetails {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*实训查重列表*/}
<Route path="/courses/:coursesId/shixun_homeworks/:homeworkid/student_work"
render={
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
<Route path="/courses/:coursesId/shixun_homework/:homeworkid/student_work"
render={
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*实训报告*/}
<Route path="/courses/:coursesId/shixun_homeworks/:homeworkid/shixun_work_report"
render={
(props) => (<ShixunWorkReport {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
<Route path="/courses/:coursesId/shixun_homework/:homeworkid/shixun_work_report"
render={
(props) => (<ShixunWorkReport {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*教师列表*/}
<Route path="/courses/:coursesId/shixun_homeworks/:homeworkid/list"
render={
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
<Route path="/courses/:coursesId/shixun_homework/:homeworkid/list"
render={
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*实训作业page*/}
<Route path="/courses/:coursesId/shixun_homeworks/:homeworkid/Page"
render={
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*实训作业设置*/}
<Route path="/courses/:coursesId/shixun_homeworks/:homeworkid/settings"
render={
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
<Route path="/courses/:coursesId/shixun_homework/:homeworkid/settings"
render={
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*/!*实训作品列表教师*!/*/}
{/*提交总结*/}
<Route path="/courses/:coursesId/shixun_homeworks/:id/commitsummary/:homeworkid"
render={
(props) => (<CommitSummary {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/courses/:coursesId/shixun_homework/:id/commitsummary/:homeworkid"
render={
(props) => (<CommitSummary {...this.props} {...props} {...this.state} />)
}
></Route>
{/*实训作品列表学生未公布*/}
{/*<Route path="/courses/:coursesId/shixun_homeworks/:homeworkid/noopenlist"*/}
{/* render={*/}
{/* (props) => (<Studentnotcompleted {...this.props} {...props} {...this.state} />)*/}
{/* }*/}
{/*></Route>*/}
{/*实训学生作品列表已公布*/}
{/* <Route path="/courses/:coursesId/shixun_homeworks/:homeworkid/list"*/}
{/* render={*/}
{/* (props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} />)*/}
{/* }*/}
{/* ></Route>*/}
{/*/!*实训学生作品列表已公布*!/*/}
{/*<Route path="/courses/:coursesId/shixun_homework/:homeworkid/list"*/}
{/* render={*/}
{/* (props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} />)*/}
{/* }*/}
{/*></Route>*/}
{/*实训作业问答主目录*/}
<Route path="/courses/:coursesId/shixun_homeworks/:homeworkid/questions"
render={
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*实训作业问答子目录*/}
<Route path="/courses/:coursesId/shixun_homework/:homeworkid/questions"
render={
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*新建课堂*/}
<Route path="/courses/new"
render={
(props) => (<WrappedCoursesNewApp {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*新建精品课堂*/}
{/*coursesId 课堂id */}
{/*id 是否是私有或者公有*/}
<Route path="/courses/news/:subjectid/newgold/:id"
render={
(props) => (<WrappedCoursesNewAppGoldclass {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*修改精品课堂*/}
<Route path="/courses/:coursesId/newgolds/settings"
render={
(props) => (<WrappedCoursesNewAppGoldclass {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*修改课堂*/}
<Route path="/courses/:coursesId/settings"
render={
(props) => (<WrappedCoursesNewApp {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 实训作业子页面*/}
<Route path="/courses/:coursesId/shixun_homework/:category_id"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 实训作业页面*/}
<Route path="/courses/:coursesId/shixun_homeworks/:main_id"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*课堂统计列表2019.10.29 */}
<Route path="/courses/:coursesId/statistics"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} />)
}
></Route>
{/*/!*实训作业and课堂详情页*!/*/}
<Route path="/courses/:coursesId"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*课堂首页*/}
<Route path="/courses"
render={
(props) => (<CoursesHome {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*<Route exact path="/courses" component={CoursesHome} {...this.props} {...props} {...this.state} ></Route>*/}
</Switch>
)
}
}
export default withRouter(ImageLayerOfCommentHOC({imgSelector: '.imageLayerParent img, .imageLayerParent .imageTarget', parentSelector: '.newMain'}) (CNotificationHOC() ( SnackbarHOC() ( TPMIndexHOC(CoursesIndex) ))));
@@ -0,0 +1,371 @@
import React, { Component } from 'react';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import {Input,Pagination,Tooltip} from 'antd';
import Loadable from 'react-loadable';
import Loading from '../../Loading';
import Guide from "../../modules/courses/shixunHomework/Guide";
//业务组件
import CoursesBanner from "./coursesDetail/CoursesBanner";
import Coursesleftnav from "./coursesDetail/CoursesLeftNav";
//普通作业
const CommonWork = Loadable({
loader: () => import('./busyWork/commonWork'),
loading:Loading,
})
const GroupWork= Loadable({
loader: () => import('./busyWork/commonWork'),
loading: Loading,
})
// 讨论
const Boards= Loadable({
loader: () => import('./boards'),
loading: Loading,
})
//教师列表
const TeacherList= Loadable({
loader: () => import('./members/teacherList'),
loading: Loading,
})
//学生列表
const StudentsList= Loadable({
loader: () => import('./members/studentsList'),
loading: Loading,
});
//分班列表
const CourseGroupList= Loadable({
loader: () => import('./members/CourseGroupList'),
loading: Loading,
});
const Eduinforms= Loadable({
loader: () => import('./gradinforms/Eduinforms'),
loading: Loading,
});
//2019.10.29 统计
const Statistics=Loadable({
loader: () => import('./statistics/Statistics'),
loading: Loading,
});
const Elearning= Loadable({
loader: () => import('./elearning/Elearning'),
loading: Loading,
});
//
const Exercise= Loadable({
loader: () => import('./exercise/Exercise'),
loading: Loading,
})
//
const Poll= Loadable({
loader: () => import('./poll/Poll'),
loading: Loading,
})
// 资源
const Resourcelist= Loadable({
loader: () => import('./Resource/index'),
loading: Loading,
})
// 视频
const CourseVideo = Loadable({
loader: () => import('./Video/VideoIndex'),
loading: Loading,
})
//实训作业
const ShixunHomework= Loadable({
loader: () => import('./shixunHomework/shixunHomework'),
loading: Loading,
})
const GraduationTopics= Loadable({
loader: () => import('./graduation/topics'),
loading: Loading,
})
const GraduationTasks= Loadable({
loader: () => import('./graduation/tasks'),
loading: Loading,
})
class ListPageIndex extends Component{
constructor(props) {
super(props);
this.state={
yslGuideone:undefined,
yslElearning:false,
isexcellent:false
}
}
comyslElearning(bool){
if(bool===true){
this.setState({
yslElearning:true,
})
}else {
this.setState({
yslElearning:false,
})
}
}
componentDidMount(){
// console.log("77");
var yslGuideone = window.localStorage.getItem('yslGuideone');
// console.log("78");
// console.log(yslGuideone);
try {
if (yslGuideone === "true") {
// console.log("true 字符串");
this.setState({
yslGuideone:true,
})
} else {
this.setState({
yslGuideone:false,
});
// console.log("false 字符串");
}
}catch (e) {
console.log(e);
this.setState({
yslGuideone:false,
});
}
}
//
// getleftNavid=(navid,newselectnavid)=>{
// console.log(navid,newselectnavid)
// this.setState({
// navkey:navid,
// navttype:newselectnavid
// })
// }
componentWillUnmount(){
// window.localStorage.setItem('yslGuideone', "false");
}
setwindowlocal=(bool)=>{
window.localStorage.setItem('yslGuideone', bool);
try {
if (bool === "true") {
this.setState({
yslGuideone:true,
})
} else {
this.setState({
yslGuideone:false,
});
}
}catch (e) {
// console.log(e);
this.setState({
yslGuideone:false,
});
}
}
ispostexcellenttype=(excellent)=>{
this.setState({
isexcellent:excellent
})
}
render() {
let {yslGuideone} =this.state;
// console.log("98");
// console.log(yslGuideone);
// console.log(this.props.isAdmin());
// // var yslGuideones = window.sessionStorage.getItem('yslGuideone');
// console.log(this.props);
// console.log(this.props.location.search);
return (
<div>
<div className="newMain clearfix">
{/*头部banner*/}
<CoursesBanner {...this.props} ispostexcellenttype={(excellent)=>this.ispostexcellenttype(excellent)}></CoursesBanner>
{/*下面是指引哦*/}
{/*{yslGuideone!==undefined?*/}
{/*(*/}
{/* yslGuideone===true?*/}
{/* <Guide*/}
{/* setwindowlocal={(b)=>this.setwindowlocal(b)}*/}
{/* >*/}
{/* </Guide>*/}
{/* :""*/}
{/* )*/}
{/* :""*/}
{/*}*/}
<div className="educontent clearfix" style={{flex: "1 0 auto"}}>
<div className="stud-class-set">
<div className="news">
<div className="edu-class-inner container clearfix">
<div className="member for-content-0 for-content">
<div className="people clearfix mb60">
{/*left_nav*/}
<div className={"with22 fl setleft"} style={{width:'264px',minHeight:'500px'}}>
<Coursesleftnav {...this.props} {...this.state} comyslElearning={(i)=>this.comyslElearning(i)}/>
</div>
{/*right_concent*/}
<div className="with78 fl">
<div className={"ml20 clearfix"}>
<Switch {...this.props}>
{/* --------------------------------------------------------------------- */}
{/* 作业设置 */}
{/* http://localhost:3007/courses/1309/homework/9300/setting */}
{/* 普通作业 */}
<Route path="/courses/:coursesId/common_homeworks/:category_id"
render={
(props) => (<CommonWork {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 作品列表 */}
<Route path="/courses/:coursesId/group_homeworks/:category_id"
render={
(props) => (<CommonWork {...this.props} {...props} {...this.state} />)
}
></Route>
<Route exact path="/courses/:coursesId/boards/:boardId"
render={
(props) => (<Boards {...this.props} {...props} {...this.state} />)
}
></Route>
{/*视频列表*/}
<Route path="/courses/:coursesId/course_videos"
render={
(props) => (<CourseVideo {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/courses/:coursesId/teachers"
render={
(props) => (<TeacherList {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 学生列表*/}
<Route path="/courses/:coursesId/students"
render={
(props) => (<StudentsList {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/courses/:coursesId/course_groups/:course_group_id"
render={
(props) => (<StudentsList {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/courses/:coursesId/course_groups"
render={
(props) => (<CourseGroupList {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/courses/:coursesId/exercises/:Id"
render={
(props) => (<Exercise {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/courses/:coursesId/polls/:Id"
render={
(props) => (<Poll {...this.props} {...props} {...this.state} />)
}
></Route>
{/* <Route path="/courses/:coursesId/file/:Id"
render={
(props) => (<Resourcelist {...this.props} {...props} {...this.state} />)
}
></Route> */}
{/* 实训作业子页面*/}
<Route path="/courses/:coursesId/shixun_homework/:category_id"
render={
(props) => (<ShixunHomework {...this.props} {...props} {...this.state} />)
}
></Route>
{/*课堂统计列表2019.10.29 */}
<Route path="/courses/:coursesId/statistics"
render={
(props) => (<Statistics {...this.props} {...props} {...this.state} />)
}
></Route>
{/*公告栏列表*/}
<Route path="/courses/:coursesId/informs"
render={
(props) => (<Eduinforms {...this.props} {...props} {...this.state} />)
}
></Route>
{/*在线学习*/}
<Route
path="/courses/:coursesId/online_learning"
render={
(props) => (<Elearning {...this.props} {...props} {...this.state} comyslElearning={(i)=>this.comyslElearning(i)} />)
}
>
</Route>
{/* 实训作业页面*/}
<Route path="/courses/:coursesId/shixun_homeworks/:main_id"
render={
(props) => (<ShixunHomework {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/courses/:coursesId/files/:main_id"
render={
(props) => (<Resourcelist {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/courses/:coursesId/file/:Id"
render={
(props) => (<Resourcelist {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/courses/:coursesId/graduation_topics/:Id"
render={
(props) => (<GraduationTopics {...this.props} {...props} {...this.state} />)
}></Route>
<Route path="/courses/:coursesId/graduation_tasks/:Id"
render={
(props) => (<GraduationTasks {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 默认 */}
<Route path="/courses/:coursesId"
render={
(props) => (<StudentsList {...this.props} {...props} {...this.state} />)
}
></Route>
</Switch>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default ListPageIndex;
@@ -0,0 +1,368 @@
import React,{ Component } from "react";
import { WordsBtn } from 'educoder';
import {Tooltip,message} from 'antd';
import {Link} from 'react-router-dom';
import {getImageUrl} from 'educoder';
import axios from 'axios'
import {getUrl} from 'educoder';
import moment from 'moment'
import CoursesListType from '../coursesPublic/CoursesListType';
import Showoldfiles from "../coursesPublic/Showoldfiles";
import Modals from '../../modals/Modals';
class Fileslistitem extends Component{
constructor(props){
super(props);
this.state = {
}
}
settingList=()=>{
let {discussMessage}=this.props
this.setState({
discussMessageid:discussMessage.id
})
this.props.Settingtypes(discussMessage.id)
}
showfiles=(list)=>{
if(this.props.checkIfLogin()===false){
this.props.showLoginDialog()
return
}
// if(this.props.checkIfProfileCompleted()===false){
// this.setState({
// AccountProfiletype:true
// })
// return
// }
// if(this.props.checkIfProfessionalCertification()===false){
// this.props.showProfileCompleteDialog()
// return
// }
if(list.is_history_file===false){
// this.props.DownloadFileA(list.title,list.url)
//window.location.href=list.url;
window.open(list.url, '_blank');
}else{
let {discussMessage,coursesId}=this.props
let file_id=discussMessage.id
let url="/files/"+file_id+"/histories.json"
axios.get(url,{
params:{
course_id:coursesId
},
}).then((result)=>{
if(result.data.attachment_histories.length===0){
// if(result.data.is_pdf===true){
// this.props.ShowOnlinePdf(result.data.url)
// //预览pdf
// }else{
//
// }
// this.props.DownloadFileA(result.data.title,result.data.url)
window.open(list.url, '_blank');
}else{
this.setState({
Showoldfiles:true,
allfiles:result.data
})
}
}).catch((error)=>{
console.log(error)
})
}
}
closaoldfilesprops=()=>{
this.setState({
Showoldfiles:false,
})
}
onDelete = (id) => {
this.setState({
Modalstype:true,
Modalstopval:"是否确认删除?",
ModalCancel:this.cancelmodel,
ModalSave:()=>this.savedelete(id),
})
}
cancelmodel=()=>{
this.setState({
Modalstype:false,
Loadtype:false,
Modalstopval:"",
ModalCancel:"",
ModalSave:"",
checkBoxValues:[],
})
}
savedelete=(id)=>{
this.setState({
Modalstype:false,
})
const cid = this.props.match.params.coursesId
const url = `/files/bulk_delete.json`;
axios.delete(url, { data: {
course_id:cid,
ids: [id],
}})
.then((response) => {
if (response.data.status == 0) {
//Modalstopval:response.data.message,
this.props.updatafiledfun()
this.setState({
// Modalstype:true,
// Modalstopval:"删除成功",
ModalsBottomval:"",
// ModalSave:this.cancelmodel,
// Loadtype:true,
checkBoxValues:[],
checkAllValue:false
})
this.props.showNotification("删除成功");
}
})
.catch(function (error) {
console.log(error);
});
}
eventStop = (event) =>{
event.stopPropagation()
}
render(){
const { checkBox,
discussMessage,index
} = this.props;
return(
<div className="graduateTopicList boardsList">
{/*提示*/}
{this.state.Modalstype&&this.state.Modalstype===true?<Modals
modalsType={this.state.Modalstype}
modalsTopval={this.state.Modalstopval}
modalCancel={this.state.ModalCancel}
modalSave={this.state.ModalSave}
modalsBottomval={this.state.ModalsBottomval}
loadtype={this.state.Loadtype}
/>:""}
<Showoldfiles
{...this.props}
visible={this.state.Showoldfiles}
allfiles={this.state.allfiles}
closaoldfilesprops={this.closaoldfilesprops}
/>
<style>{`
.graduateTopicList .ant-checkbox-input {
margin-right: 15px;
}
.graduateTopicList .ant-checkbox-wrapper {
margin-top: 0px;
}
`}</style>
<style>{`
.boardsList .panel-list-img {
width: 50px;
height: 50px;
}
`}</style>
{/*<a href={"/users/"+this.props.user.login} alt="用户"*/}
{/*style={{"width": "50px", "height": "50px", "display": "block", margin: "0 10px"}}>*/}
{/*<img*/}
{/*alt="1?1529221779" className="panel-list-img mr15" height="50"*/}
{/*src={getImageUrl("images/"+discussMessage.author.image_url)} width="50"*/}
{/*></img>*/}
{/*</a>*/}
<style>{`
.boardsList .contentSection{
margin-left:0px;
}
.maxwidth580{
max-width: 580px;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap
}
.mt2{
margin-top:2px;
}
`}</style>
<div className="clearfix ds pr contentSection" style={{cursor : this.props.isAdmin ? "pointer" : "default"}} onClick={() => window.$(`.sourceitem${index} input`).click() }>
<h6 onClick={(event)=>this.eventStop(event)}>
<span className={`sourceitem${index} fl mr12 mt3`}>
{checkBox}
</span>
{
this.props.isAdmin ? <a
// href={"/courses/" + coursesId + "/graduation/graduation_tasks/" + categoryid + "/" + taskid + "/list"}
onClick={()=>this.showfiles(discussMessage)}
title={discussMessage.title}
className="fl mt3 font-16 font-bd color-dark maxwidth580">{discussMessage.title}</a> : ""
}
{
this.props.isStudent? <a
onClick={()=>this.showfiles(discussMessage)}
title={discussMessage.title}
className="fl mt3 font-16 font-bd color-dark maxwidth580">{discussMessage.title}</a> :""
}
{
this.props.isNotMember===true?
discussMessage.is_lock === true ?
<span className="fl mt3 font-16 font-bd color-dark maxwidth580 pointer" title={"私有属性,非课堂成员不能访问"}>{discussMessage.title}</span>
:<a
onClick={()=>this.showfiles(discussMessage)}
title={discussMessage.title}
className="fl mt3 font-16 font-bd color-dark maxwidth580">{discussMessage.title}</a>:""
}
{
discussMessage.is_lock === true ?
<Tooltip title={"私有属性,非课堂成员不能访问"} placement="bottom">
<i className="iconfont icon-guansuo color-grey-c ml10 font-16 fl mt4"></i>
</Tooltip>
:""
}
{discussMessage.is_publish===false?<CoursesListType typelist={["未发布"]} typesylename={""}/>:""}
{this.props.isAdmin?
<span className={"fr mt2"} onClick={(event)=>this.eventStop(event)}>
<WordsBtn style="blue" className="colorblue font-16 ml20 fr">
<a className="btn colorblue fontweight400"
onClick={()=>this.settingList()}>设置</a>
</WordsBtn>
</span>:""}
{this.props.isStudent===true&&this.props.current_user.login===discussMessage.author.login?
<span className={"fr mt2"} onClick={(event)=>this.eventStop(event)}>
<WordsBtn style="blue" className="colorblue font-16 ml20 fr">
<a className="btn colorblue fontweight400"
onClick={()=>this.settingList()}>设置</a>
</WordsBtn>
<WordsBtn style="blue" className="colorblue font-16 ml20 fr">
<a className="btn colorblue fontweight400"
onClick={()=>this.onDelete(discussMessage.id)}>删除</a>
</WordsBtn>
</span>:""}
</h6>
<style>
{
`
.sttingbox{
position: absolute;
right: 0px;
top: 20px;
}
`
}
</style>
<style>
{
`
.lightgreybox{
min-width: 260px;
}
.mrf2{
margin-top: -2px;
}
`
}
</style>
{/*资源分班*/}
{/*{discussMessage.course_groups.length===0?"":*/}
{/*<p className="color-grey panel-lightgrey mt8 fl lightgreybox ml30" style={{width:'100%'}}>*/}
{/*{discussMessage.course_groups.map((item,key)=>{*/}
{/*return(*/}
{/*<div className="mr50">*/}
{/*<span className="mr15 color-dark">{item.course_group_name}</span>*/}
{/*<span className="mr15 color-grey9 ">将发布于 { moment(item.course_group_publish_time).format('YYYY-MM-DD HH:mm')}</span>*/}
{/*</div>*/}
{/*)*/}
{/*})}*/}
{/*</p>}*/}
<p className={this.props.isAdmin===true?"color-grey panel-lightgrey mt8 fl ml30":"color-grey panel-lightgrey mt8 fl ml13"} style={{width:'100%'}}>
<span className="mr50">
<span className="mr15 color-dark">{discussMessage.author.name}</span>
<span className="mr15 color-grey9">大小 {discussMessage.filesize}</span>
<span className="mr15 color-grey9">下载 {discussMessage.downloads_count}</span>
{/*<span className="mr15 color-grey9">引用 {discussMessage.quotes}</span>*/}
<span className="mr15 color-grey-c">
{/*{moment(discussMessage.publish_time).format('YYYY-MM-DD HH:mm:ss')}*/}
{/*{moment(discussMessage.publish_time).fromNow()}*/}
{ discussMessage.publish_time===null?"":
discussMessage.is_publish===true?"":"发布于"}
{ discussMessage.publish_time===null?"":discussMessage.is_publish===true?moment(discussMessage.publish_time).fromNow():moment(discussMessage.publish_time).format('YYYY-MM-DD HH:mm')}
</span>
</span>
{discussMessage&&discussMessage.category_name===null?"":this.props.child===false?<div className="color-grey9 task-hide fr mr30" title={discussMessage&&discussMessage.category_name}
style={{"max-width":"268px"}}>所属目录{discussMessage&&discussMessage.category_name}
</div>:""}
</p>
<p className={this.props.isAdmin===true?"color-grey panel-lightgrey mt8 fl ml30":"color-grey panel-lightgrey mt8 fl ml13"} style={{width:'94%'}}>
<style>
{
`
.isspans{
text-align: left;
white-space: pre-wrap;
word-break: break-all;
overflow-wrap: break-word;
}
`
}
</style>
<span className="color-dark isspans">资源描述 :{discussMessage.description===null?"暂无描述":discussMessage.description}</span>
{/*<span className="mr50">*/}
{/*/!*<span className="mr15 color-dark"></span>*!/*/}
{/*<span className="mr15 color-dark">*/}
{/*</span>*/}
{/*/!*{this.props.isAdmin ?<span><i className="iconfont icon-bianjidaibeijing font-22 color-green" onClick={()=>this.settingList()}></i></span>:""}*!/*/}
{/*</span>*/}
</p>
</div>
</div>
)
}
}
export default Fileslistitem;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,82 @@
import React,{ Component } from "react";
import { Pagination } from 'antd';
// import { NoneData } from 'educoder';
import LiveItem from './LiveItem';
import './video.css';
import bilibili from './images/bilibili.png';
import tencent from './images/tencent.png';
import WeiBaiTong from './images/WeiBaiTong.png';
import douyu from './images/douyu.jpg';
class Live extends Component{
render(){
const { liveData , lives , successFunc , pageSize , changePage , page } = this.props;
return(
<div className="livePanel">
<p className="mt30 mb10 color-grey-9 pl10">EduCoder支持所有第三方直播平台特别推荐</p>
<ul className="platform">
<li>
<a href="https://ke.qq.com/act/speed_pc/index.html?from=800021837" target="_blank"><img alt="" src={tencent} className="mr8" width="28px"/>
<label className="color-grey-3 pointer">腾讯课堂</label>
</a>
<a href="https://pub.idqqimg.com/pc/misc/files/20200204/2e4cb765bef54f0c919c0ab8ab79d969.pdf" target="_blank" className="color-blue ml20">(简明手册)</a>
</li>
<li>
<a href="https://live.bilibili.com/liveHime?visit_id=a81rbm7v2kk0" target="_blank"><img alt="" src={bilibili} className="mr8" width="52px"/>
<label className="color-grey-3 pointer">哔哩哔哩</label>
</a>
<a href="https://www.bilibili.com/blackboard/live/broadcast-intro.html#/" target="_blank" className="color-blue ml20">(简明手册)</a>
</li>
<li>
<a href="https://www.douyu.com/" target="_blank"><img alt="" src={douyu} className="mr8" width="28px"/>
<label className="color-grey-3 pointer">斗鱼</label>
</a>
<a href="https://www.douyu.com/special/guide/anchor" target="_blank" className="color-blue ml20">(简明手册)</a>
</li>
<li>
<div><img alt="" src={WeiBaiTong} className="mr8" width="28px"/>
<label className="color-grey-3">威佰通</label>
</div>
<a href="https://www.educoder.net/forums/3734" target="_blank" className="color-blue ml20">(简明手册)</a>
</li>
</ul>
{
lives && lives.length > 0 ?
<React.Fragment>
<p className="font-grey-9 mt20 mb20 pl10"> <span className="color-orange">{liveData && liveData.total_count}</span> </p>
<div className="liveContent">
{
lives.map((item,key)=>{
return(
<LiveItem
key={key}
item={item}
{...this.props}
{...this.state}
successFunc={()=>successFunc()}
></LiveItem>
)
})
}
</div>
{
liveData && liveData.total_count > pageSize &&
<div className="mt30 mb50 edu-txt-center">
<Pagination showQuickJumper total={liveData.total_count} pageSize={pageSize} current={page} onChange={(page)=>changePage(page,'live')}></Pagination>
</div>
}
</React.Fragment>
:
""
// <div className="edu-back-white">
// <NoneData style={{width: '100%'}}></NoneData>
// </div>
}
</div>
)
}
}
export default Live;
@@ -0,0 +1,177 @@
import React,{ Component } from "react";
import { getImageUrl } from 'educoder';
import { Modal } from 'antd';
import { WordsBtn } from 'educoder';
import axios from 'axios';
// 点击按钮复制功能
function jsCopy(props){
var e = document.getElementById("copy_meet_content");
e.select();
document.execCommand("Copy");
props.showNotification("复制成功!");
}
const $ = window.$;
function getRight(){
var right = parseInt($(".-task-sidebar").css("right"));
return right===0?0:right;
}
class LiveItem extends Component{
constructor(props) {
super(props);
this.state = {
visible:false
}
}
deleteLive=(id)=>{
this.props.confirm({
content: '是否确认删除?',
onOk: () => {
const url = `/live_links/${id}.json`;
axios.delete(url).then(result=>{
if(result){
this.props.showNotification(`删除成功!`);
const { successFunc } = this.props;
successFunc && successFunc(1);
}
}).catch(error=>{
console.log(error);
})
},
onCancel() {
console.log('Cancel');
},
});
}
alertInfo=()=>{
this.setState({
visible:true
})
getRight();
}
onDialogOkBtnClick=()=>{
this.setState({
visible:false,
})
}
render(){
const { key, item , setLiveId } = this.props;
const { visible } = this.state;
const wei_flag = item.platform && (item.platform === "威佰通");
console.log(wei_flag && item.url);
return(
<div className="liveItem" key={key}>
<Modal
title="提示"
visible={visible}
closable={false}
footer={null}
keyboard={false}
centered={true}
>
{
wei_flag && item.url ?
<React.Fragment>
<div className="task-popup-content edu-txt-center">
<p className="font-16">打开威佰通客户端输入会议号即可进入直播</p>
<div className="wei_meet">
<p className="wei_meet_info">
<span>会议号</span>
<input value={item.url} className="showNumber" readOnly id="copy_meet_content"/>
<a className="color-blue ml50" onClick={()=>jsCopy(this.props)}>复制会议号</a>
</p>
</div>
</div>
<div className="task-popup-submit clearfix edu-txt-center">
<a className="task-btn fl" onClick={this.onDialogOkBtnClick}>取消</a >
<a className="task-btn task-btn-orange fr" onClick={this.onDialogOkBtnClick}>完成</a >
</div>
</React.Fragment>
:
<React.Fragment>
<div className="task-popup-content">
<p className="task-popup-text-center font-16 pb20">{wei_flag ? "当前直播无会议号":"直播链接失效"}</p >
</div>
<div className="task-popup-submit clearfix edu-txt-center">
<a className="task-btn task-btn-orange mr51" onClick={this.onDialogOkBtnClick}>知道了</a >
</div>
</React.Fragment>
}
</Modal>
{
visible ?
<style>{
`
body{
width: calc(100% - 7px)!important;
overflow: hidden!important;
}
.-task-sidebar{
right:${getRight()+7}px!important;
}
`}
</style>
:
""
}
<div className="lineMiddle livesMain">
<span className="lineMiddle">
<span className="font-18 task-hide" style={{maxWidth:"759px"}}>{item.course_name}</span>
<span className={item.on_status?"labels living":"labels lived"}>{item.on_status?'已开播':'未开播'}</span>
</span>
</div>
<div className="lineMiddle mt15">
<div className="liveDesc">
<p><span className="task-hide-2 break_word">{item.description}</span></p>
</div>
{
item.on_status ?
<React.Fragment>
{
item.url ?
<React.Fragment>
{
wei_flag ?
<a className="btns going" onClick={this.alertInfo}>进入</a>
:
<a className="btns going" target="_blank" href={`${item.url}`}>进入</a>
}
</React.Fragment>
:
<a className="btns going" onClick={this.alertInfo}>进入</a>
}
</React.Fragment>
:
<span className="btns ect">进入</span>
}
</div>
<p className="lineMiddle livesMain mt15 font-12">
<span className="lineMiddle color-grey-9">
<img alt={`${item.author_name}`} className="liveAuthor" src={getImageUrl(`images/${item.author_img}`)}/>
<label className="mr50">{item.author_name}</label>
{ item.platform && <span className="mr50">直播平台{item.platform}</span> }
{ item.live_time && <span className="mr50">开播时间{item.live_time}</span>}
{ item.duration && <span className="mr50">直播预计时长{item.duration}分钟</span> }
</span>
<span>
{
item.op_auth ?
<WordsBtn style="grey" onClick={()=>setLiveId(item.id)}>编辑</WordsBtn>:""
}
{
item.delete_auth ?
<WordsBtn style="grey" className="ml30" onClick={()=>this.deleteLive(item.id)}>删除</WordsBtn>
:""
}
</span>
</p>
</div>
)
}
}
export default LiveItem;
@@ -0,0 +1,292 @@
import React,{ Component } from "react";
import { Modal , Form , Input , Spin , Select , AutoComplete , DatePicker , InputNumber } from 'antd';
import locale from 'antd/lib/date-picker/locale/zh_CN';
import moment from 'moment';
import { handleDateString } from 'educoder';
import './video.css';
import axios from 'axios';
const { TextArea } = Input;
const { Option } = Select;
const array=['腾讯课堂','B站','斗鱼','威佰通'];
function range(start, end) {
const result = [];
for (let i = start; i < end; i++) {
result.push(i);
}
return result;
}
function disabledDateTime() {
return {
disabledMinutes: () => range(1, 30).concat(range(31, 60)),
};
}
function disabledDate(current) {
return current && current < moment().endOf('day').subtract(1, 'days');
}
class LiveNew extends Component{
constructor(props){
super(props);
this.state={
isSpining:false,
beginTime:undefined,
wei_flag:undefined
}
}
componentDidMount=()=>{
this.checkType();
}
componentDidUpdate=(prevState)=>{
if(prevState && prevState.liveId !== this.props.liveId){
this.setState({
isSpining:true
})
this.checkType();
}
}
checkType=()=>{
const { liveId } = this.props;
this.clearAll();
if(liveId){
const url =`/live_links/${liveId}/edit.json`;
axios.get(url).then(result=>{
if(result){
this.props.form.setFieldsValue({
url:result.data.url,
description:result.data.description,
platform:result.data.platform || "腾讯课堂",
duration:result.data.duration,
course_name:result.data.course_name
})
this.setState({
beginTime:result.data.live_time && moment(result.data.live_time,"YYYY-MM-DD HH:mm"),
wei_flag:result.data.platform && result.data.platform === "威佰通"
})
}
})
}
this.setState({
isSpining:false
})
}
handleSubmit=()=>{
this.props.form.validateFields((err, values) => {
if(!err){
this.setState({
isSpining:true
})
const { liveId } = this.props;
if(liveId){
// 修改
this.updateFunc(liveId,values);
}else{
this.creatFunc(values);
}
}
})
}
// 修改
updateFunc=(id,values)=>{
const url = `/live_links/${id}.json`;
const { beginTime } = this.state;
axios.put(url,{
...values,
live_time:beginTime
}).then(result=>{
if(result){
this.props.showNotification("修改成功!");
const { setliveVisibel } = this.props;
setliveVisibel && setliveVisibel(false,true);
}
this.setState({
isSpining:false
})
}).catch(error=>{
this.setState({
isSpining:false
})
console.log(error);
})
}
// 新增
creatFunc=(values)=>{
const CourseId=this.props.match.params.coursesId;
const url = `/courses/${CourseId}/live_links.json`;
const { beginTime } = this.state;
axios.post(url,{
...values,
live_time:beginTime
}).then(result=>{
if(result){
this.props.showNotification("添加成功!");
const { setliveVisibel } = this.props;
setliveVisibel && setliveVisibel(false,true);
}
this.setState({
isSpining:false
})
}).catch(error=>{
this.setState({
isSpining:false
})
console.log(error);
})
}
validateDesc= (rule, value, callback) => {
if(!value){
callback();
}
if (value.length > 100) {
callback("直播说明不能超过100个字!");
}else{
callback();
}
}
cancelNew=()=>{
const { setliveVisibel } = this.props;
this.clearAll();
setliveVisibel && setliveVisibel(false);
}
clearAll=()=>{
this.props.form.setFieldsValue({
course_name:undefined,
platform:"腾讯课堂",
url:undefined,
description:undefined,
duration:undefined
})
this.setState({
beginTime:undefined,
wei_flag:false
})
}
onChangeTime=(data,dateString)=>{
this.setState({
beginTime:handleDateString(dateString)
})
}
ChangePlatform=(e)=>{
if(e === "威佰通"){
this.setState({
wei_flag:true
})
}else{
this.setState({
wei_flag:false
})
}
}
render(){
const { isSpining , beginTime , wei_flag } = this.state;
const {getFieldDecorator} = this.props.form;
const { visible } = this.props;
const dataSource = array.map((item,key)=>{
return(
<Option value={item}>{item}</Option>
)
})
return(
<Modal
visible={visible}
width="560px"
title={'添加直播'}
footer={null}
closable={false}
className="liveModal"
>
<Spin spinning={isSpining}>
<div className="task-popup-content">
<Form onSubmit={this.handleSubmit}>
<Form.Item label={`直播课程`}>
{getFieldDecorator('course_name', {
rules: [{required: true, message: "请输入课程名称"}],
})(
<Input placeholder="请输入课程名称" />
)}
</Form.Item>
<Form.Item label={`直播平台`}>
{getFieldDecorator('platform', {
rules: [{required: true, message: "请选择直播平台"}],
})(
<AutoComplete
placeholder="请选择或输入直播平台名称"
onChange={this.ChangePlatform}
dataSource={dataSource}
>
</AutoComplete>
)}
</Form.Item>
<Form.Item label={wei_flag?`会议号`:`直播链接`}>
{getFieldDecorator('url', {
rules: [],
})(
<Input placeholder={wei_flag?`请输入会议号`:"请输入第三方直播链接,如:腾讯课堂播放链接等。"} />
)}
</Form.Item>
<div className="flex-bottom">
<div className="flex1">
<p className="ant-col ant-form-item-label color-grey-3 font-16 setStyle">开播时间</p>
<DatePicker
dropdownClassName="hideDisable"
className="timeStyle mb20"
placeholder="如:2020/02/02 12:00"
style={{width:"220px"}}
showTime={{ format: 'HH:mm' }}
locale={locale}
format="YYYY-MM-DD HH:mm"
showToday={false}
disabledTime={disabledDateTime}
disabledDate={disabledDate}
value={beginTime && moment(beginTime,"YYYY-MM-DD HH:mm")}
onChange={this.onChangeTime}
></DatePicker>
</div>
<Form.Item label={`直播预计时长`}>
{getFieldDecorator('duration', {
rules: [],
})(
<InputNumber placeholder="请输入直播时长" style={{width:"220px"}}/>
)}
</Form.Item>
<span className="mb20 ml5">分钟</span>
</div>
<Form.Item label={`直播说明`}>
{getFieldDecorator('description', {
rules: [{
validator: this.validateDesc,
}],
})(
<TextArea rows={4} placeholder="可在此介绍开播具体事项,如开播时间安排等。" />
)}
</Form.Item>
</Form>
<div className="clearfix mt30 edu-txt-center">
<a onClick={this.cancelNew} className="task-btn mr30">取消</a>
<a type="submit" onClick={this.handleSubmit} className="task-btn task-btn-orange">确定</a>
</div>
</div>
</Spin>
</Modal>
)
}
}
const WrappedLiveNew = Form.create({name: 'LiveNew'})(LiveNew);
export default WrappedLiveNew;
@@ -0,0 +1,254 @@
import React,{ Component } from "react";
import { Input , Pagination } from 'antd';
import { NoneData ,ActionBtn } from 'educoder';
import VideoUploadList from '../../user/usersInfo/video/VideoUploadList';
import VideoInReviewItem from '../../user/usersInfo/video/VideoInReviewItem';
import HeadlessModal from '../../user/usersInfo/common/HeadlessModal';
import EditVideoModal from '../../user/usersInfo/video/EditVideoModal'
import ClipboardJS from 'clipboard'
import './video.css';
import '../../user/usersInfo/video/InfosVideo.css'
import axios from 'axios';
const DEFAULT_VIDEO_WIDTH_IN_MD = "90%" // 400
const DEFAULT_VIDEO_HEIGHT_IN_MD = "55%" // 400
const videoEl = null;
let _clipboard = null;
class Video extends Component{
constructor(props){
super(props);
this.state={
videos:undefined,
count:0,
page:1,
videoId:undefined,
videoVisible:false,
visible:false
}
}
// 编辑的弹框visible
setVisible=(flag)=>{
this.setState({
visible:flag
})
}
setVideoVisible=(flag)=>{
this.setState({
videoVisible:flag
})
if (flag === false) {
if (_clipboard) {
this.setState({
videoId:undefined
})
_clipboard.listener.destroy();
_clipboard = null;
}
} else {
// videoEl.current && videoEl.current.play()
setTimeout(() => {
if (!_clipboard) {
_clipboard = new ClipboardJS('.copybtn');
_clipboard.on('success', (e) => {
this.props.showNotification('复制成功');
});
}
}, 200)
}
}
// 编辑成功后回调的方法
editSuccess=()=>{
this.props.showNotification("视频名称修改成功!");
const { listFunc , page } = this.props;
listFunc && listFunc(page);
}
onEditVideo=(item)=>{
let videoId = {
videoId: item.id,
title: item.title
}
this.setState({
videoId,
})
this.setVisible(true);
}
onMaskClick=(item)=> {
let videoId = {
videoId: item.id,
title: item.title,
file_url: item.file_url,
cover_url: item.cover_url
}
this.setState({
videoId
})
this.setVideoVisible(true);
}
setVideoVisible=(flag)=>{
this.setState({
videoVisible:flag
})
if (flag === false) {
if (_clipboard) {
this.setState({
videoId:undefined
})
_clipboard.listener.destroy();
_clipboard = null;
}
} else {
// videoEl.current && videoEl.current.play()
setTimeout(() => {
if (!_clipboard) {
_clipboard = new ClipboardJS('.copybtn');
_clipboard.on('success', (e) => {
this.props.showNotification('复制成功');
});
}
}, 200)
}
}
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>`
}
// 删除事件
deleteVideo=(item)=>{
this.props.confirm({
content: '该视频将被删除,不可恢复',
subContent:'是否确认删除?',
onOk: () => {
const CourseId=this.props.match.params.coursesId;
const url = `/courses/${CourseId}/delete_course_video.json`;
axios.delete(url,{
params:{
video_id:item.id
}
}).then(result=>{
if(result){
this.props.showNotification(`删除成功!`);
this.props.updataleftNavfun();
const { listFunc , page } = this.props;
listFunc && listFunc(page);
}
}).catch(error=>{
console.log(error);
})
},
onCancel() {
console.log('Cancel');
},
});
}
render(){
const { visible , videoVisible , videoId } = this.state;
const CourseId=this.props.match.params.coursesId;
const login=this.props.user&&this.props.user.login;
const _inputValue = videoId && this.getCopyText(videoId.file_url, videoId.cover_url);
const { admin , is_teacher ,business , user_id} = this.props.user;
const { videos , upload , uploadVideo , videoData , changePage ,pageSize ,page } = this.props;
const operation = admin || business || (is_teacher && this.props.checkIfProfessionalCertification())
return(
<div>
<EditVideoModal {...this.props} visible={visible} setVisible={this.setVisible}
editSuccess={this.editSuccess}
{...videoId} CourseUser={login}
></EditVideoModal>
<HeadlessModal
visible={videoVisible}
setVisible={this.setVideoVisible}
className="showVideoModal"
width={800 - 1}
>
{
videoId &&
<video
autoplay="true"
ref={videoEl}
src={videoId.file_url} controls="true" controlslist="nodownload">
您的浏览器不支持 video 标签
</video>
}
<div className="df copyLine">
<Input value={_inputValue}
className="dark"
></Input>
<ActionBtn className="copybtn" data-clipboard-text={_inputValue}>复制视频地址</ActionBtn>
</div>
</HeadlessModal>
<div className="videoPanel">
{
upload ?
<VideoUploadList {...this.props} flag={true} CourseId={CourseId} CourseUser={login} successFunc={()=>uploadVideo()}></VideoUploadList>
:
<React.Fragment>
{
videos && videos.length > 0 ?
<React.Fragment>
<p className="font-grey-9 mt20 mb20 pl5"> <span className="color-orange">{videoData && videoData.count}</span> </p>
<div className="videoContent">
{
videos.map((item,key)=>{
return(
<VideoInReviewItem
{...this.props}
{...item}
key={item.id}
onEditVideo={this.onEditVideo}
onMaskClick={this.onMaskClick}
getCopyText={this.getCopyText}
operation={operation}
deleteVideo={(admin || item.user_id === user_id) ? this.deleteVideo : undefined}
>
</VideoInReviewItem>
)
})
}
</div>
</React.Fragment>
:
<div className="edu-back-white">
<NoneData style={{width: '100%'}}></NoneData>
</div>
}
{
videoData && videoData.count > pageSize &&
<div className="mt30 mb50 edu-txt-center">
<Pagination showQuickJumper total={videoData.count} current={page} pageSize={pageSize} onChange={(page)=>changePage(page,'video')}></Pagination>
</div>
}
</React.Fragment>
}
</div>
</div>
)
}
}
export default Video;
@@ -0,0 +1,297 @@
import React,{ Component } from "react";
import { Menu, Spin } from 'antd';
import { WordsBtn } from 'educoder';
import axios from 'axios';
import Videos from './Video';
import Lives from './Live';
import LivesNew from './LiveNew';
import './video.css';
import '../css/Courses.css';
import '../publicNav/nav.css';
const PAGE_SIZE = 15;
const LIVE_PAGE_SIZE = 10;
const $ = window.$;
function getRight(){
var right = parseInt($(".-task-sidebar").css("right"));
return right===0?0:right;
}
class VideoIndex extends Component{
constructor(props){
super(props);
this.state={
page:1,
upload:false,
videos:undefined,
videoData:undefined,
type:"video",
isSpining:false,
lives:undefined,
liveData:undefined,
liveId:undefined,
liveVisible:false
}
}
checkType=(type,page)=>{
this.setState({
type,
isSpining:true
})
if(type === "video"){
this.getList(page);
}else{
this.getLiveList(page);
}
}
componentDidMount=()=>{
const { search } = this.props.location;
const { page } = this.state;
if(search && search === "?open=live"){
this.setState({
type:"live"
})
this.checkType("live",page);
}else{
if(search === "?open=new"){
this.setState({
upload:true
})
}
this.checkType("video",page);
}
}
// 获取直播列表
getLiveList=(page)=>{
const CourseId=this.props.match.params.coursesId;
const url = `/courses/${CourseId}/live_links.json`;
axios.get(url,{
params:{
page,
limit:LIVE_PAGE_SIZE
}
}).then(result=>{
if(result){
this.setState({
liveData:result.data,
lives:result.data.lives,
isSpining:false,
})
}
}).catch(error=>{
console.log(error);
})
}
// 获取视频列表
getList=(page)=>{
const CourseId=this.props.match.params.coursesId;
const fetchUrl = `/courses/${CourseId}/course_videos.json`;
axios.get(fetchUrl, {
params: {
page,
limit: PAGE_SIZE,
}
})
.then((response) => {
if(response){
this.setState({
videos:response.data.videos,
videoData:response.data,
isSpining:false
})
}
}).catch((error) => {
console.log(error);
})
}
changeType=(e)=>{
this.setState({
type:e.key,
upload:false,
page:1
})
this.checkType(e.key,1);
}
changePage=(page,type)=>{
this.setState({
page
})
this.checkType(type,page);
}
onEditVideo=(item)=>{
let videoId = {
videoId: item.id,
title: item.title
}
this.setState({
videoId,
})
this.setVisible(true);
}
uploadVideo=(upload)=>{
this.setState({
upload,
isSpining:true
})
const { page } = this.state;
setTimeout(()=>{
this.getList(page);
},500)
}
toUpload =()=> {
const { admin , is_teacher,business} = this.props.user;
if (admin || business || (is_teacher && this.props.checkIfProfessionalCertification())) {
this.setState({
type:"video",
upload:true,
page:1
})
} else {
this.props.showProfessionalCertificationDialog();
}
}
// 直播设置后回调的方法
// successFunc=()=>{
// this.setState({
// type:"live",
// page:1
// })
// this.checkType("live",1);
// }
// 直播设置
liveSetting=()=>{
this.setState({
liveId:undefined
})
this.setliveVisibel(true);
}
//直播设置弹框
setliveVisibel=(flag,changetypeFlag)=>{
this.setState({
liveVisible:flag
})
if(flag === false){
this.setState({
liveId:undefined
})
}
if(changetypeFlag){
this.checkType("live",1);
}
}
// 列表-编辑(修改传到编辑的id)
setLiveId=(id)=>{
this.setState({
liveId:id
})
this.setliveVisibel(true);
}
render(){
const { videos , upload , videoData , type , liveData , lives , page , liveVisible , isSpining , liveId } = this.state;
const { admin , is_teacher , business } = this.props.user;
// console.log("p",this.props);
return(
<React.Fragment>
<LivesNew
visible={liveVisible}
liveId={liveId}
setliveVisibel={this.setliveVisibel}
{...this.props}
{...this.state}
></LivesNew>
{
liveVisible ?
<style>{
`
body{
width: calc(100% - 7px)!important;
overflow: hidden!important;
}
.-task-sidebar{
right:${getRight()+7}px!important;
}
`}</style>:
<style>{
`
body{
width: 100%!important;
}
`}</style>
}
<div className="edu-back-white" style={{marginBottom:"1px"}}>
<div className="clearfix pl30 pr30 menuDiv">
<div className="task_menu_ul fl">
<Menu mode="horizontal" selectedKeys={[type]} onClick={this.changeType}>
<Menu.Item key="video">视频</Menu.Item>
<Menu.Item key="live">直播</Menu.Item>
</Menu>
</div>
{
(admin || is_teacher || business) &&
<li className="fr mt18">
{
type === "video" ?
<React.Fragment>
{
upload ?
<WordsBtn style="grey" className="font-16" onClick={()=>this.uploadVideo(false)}>取消</WordsBtn>
:
<WordsBtn style="blue" className="font-16" onClick={this.toUpload}>上传视频</WordsBtn>
}
</React.Fragment>
:
<WordsBtn style="blue" className="font-16 ml30" onClick={this.liveSetting}>添加直播</WordsBtn>
}
</li>
}
</div>
</div>
<Spin spinning={isSpining}>
{
type === "video" ?
<Videos
upload={upload}
videos={videos}
page={page}
data={videoData}
pageSize={PAGE_SIZE}
uploadVideo={this.uploadVideo}
listFunc={this.getList}
changePage={this.changePage}
{...this.props}
{...this.state}
></Videos>
:
<Lives
lives={lives}
liveData={liveData}
page={page}
pageSize={LIVE_PAGE_SIZE}
successFunc={this.getLiveList}
changePage={this.changePage}
setLiveId={this.setLiveId}
{...this.props}
{...this.state}
></Lives>
}
</Spin>
</React.Fragment>
)
}
}
export default VideoIndex;
@@ -0,0 +1,11 @@
import React,{ Component } from "react";
class VideoNew extends Component{
render(){
return(
<div></div>
)
}
}
export default VideoNew;
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

@@ -0,0 +1,179 @@
.videoContent{
display: flex;
flex-wrap: wrap;
align-items: flex-start;
}
.videoContent > div{
width: 285px;
margin-bottom: 20px;
background: #fff;
border-radius: 12px;
margin-right: 30px;
}
.videoContent > div:nth-child(3n+0){
margin-right: 0px!important;
}
.videoContent > div.videoItem:nth-child(4n+0){
margin-right: 30px;
}
.videoItem:nth-child(4n+0) {
margin-right: 30px;
}
.videoContent > div .cover{
border-radius: 12px 12px 0px 0px;
height: 158px;
width: 100%;
}
.videoInfo{
padding:15px;
}
.videoInfo .title{
line-height: 24px;
}
.videoInfo .time{
line-height: 15px;
margin-bottom: 10px;
color: #C0C4CC;
font-size: 14px;
}
.flex-middle{
display: flex;
justify-content: space-between;
align-items: center;
height: 25px;
}
.flex-bottom{
display: flex;
justify-content: space-between;
align-items: flex-end;
}
.square-main .buttonRow i {
vertical-align: top;
font-size: 16px;
color: #4CACFF !important;
margin-left: 6px;
}
.leftMenu{
float: left;
border:none;
font-size: 18px;
}
.liveItem{
padding: 30px 30px 15px 30px;
background: #fff;
margin-bottom: 10px;
}
.livesMain{
justify-content: space-between;
}
.lineMiddle{
display: flex;
align-items: center;
}
.liveAuthor{
width: 20px;
height: 20px;
border-radius: 50%;
margin-right: 20px;
}
.labels{
display: block;
padding: 0px 16px;
height: 24px;
line-height: 24px;
font-size: 12px;
margin-left: 20px;
border-radius: 16px;
}
.labels.living{
border:1px solid #25C03B;
color: #25C03B;
}
.labels.lived{
border:1px solid #999999;
color: #999999;
}
.btns
{
display: block;
height: 34px;
line-height: 34px;
padding: 0px 26px;
border-radius: 4px;
}
.btns.going{
background: #5091FF;
color: #fff;
}
.btns.ect{
background: #CCCCCC;
color: #fff;
}
.liveDesc{
flex:1;
margin-right: 30px;
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
}
.timeStyle{
transition: 0.5s;
}
.borderRed{
border:1px solid #f5222d;
border-radius: 4px;
}
.setStyle{
margin-bottom: 5px!important;
}
.switchStyle.ant-switch-checked{
background-color: #25C03B;
}
.liveModal .ant-modal-body{
padding:20px;
}
.liveModal .ant-modal-body .task-popup-content{
padding:0px;
}
.liveModal .ant-row.ant-form-item{
position: relative;
margin-bottom: 16px;
}
.liveModal .ant-form-explain{
position: absolute;
bottom: -18px;
left: 0px;
}
.liveModal .ant-col.ant-form-item-label{
height: 22px;
line-height: 22px;
}
.platform{
background: #fff;
display: flex;
align-items: center;
padding:20px 10px;
margin-bottom: 10px;
}
.platform > li{
margin-right: 50px;
display: flex;
align-items: center;
}
.showNumber{
border:none;
}
.wei_meet{
display: flex;
margin:20px auto;
align-items: center;
}
.wei_meet_info{
margin:0px auto;
}
@@ -0,0 +1,89 @@
import React,{ Component } from "react";
import {Tooltip, Modal, Input } from 'antd'
import moment from 'moment'
import { getUrl, WordsBtn } from 'educoder'
import axios from 'axios'
class AddDirModal extends Component{
constructor(props){
super(props);
this.state = {
}
}
onInput = (e) => {
this.setState({
inputValue: e.target.value
})
}
open = () => {
this.setState({ visible: true, inputValue: '' })
}
onSave = () => {
let coursesId = this.props.match.params.coursesId;
const url = `/courses/${coursesId}/boards.json`
let { inputValue } = this.state;
if (!inputValue) {
this.setState({ errorMsg: '名称不能为空'})
return;
}
if (inputValue.length > 20) {
this.setState({ errorMsg: '名称不能超过20个字'})
return;
}
this.setState({ errorMsg: ''})
axios.post(url,{
name: inputValue
}).then((response)=>{
if (response.data.status == 0) {
this.onCancel()
this.props.showNotification('添加成功')
this.props.addSuccess && this.props.addSuccess()
}
}).catch((error)=>{
console.log(error)
})
}
onCancel = () => {
this.setState({ visible: false, inputValue: '', errorMsg: '' })
}
render(){
let { inputValue, visible, errorMsg } = this.state;
const { title } = this.props;
return(
<Modal
keyboard={false}
title={title}
visible={visible}
className={"Navmodal"}
closable={false}
footer={null}
destroyOnClose={true}
centered={true}
>
<div className="df">
<div className={"fl mt5"}>{this.props.label}</div>
<Input placeholder={this.props.inputPlaceholder || '示例:讨论小组A'}
className={"input-flex-35 greyInput fl"}
maxLength="60"
value={inputValue}
onInput={this.onInput}
/>
</div>
{errorMsg && <span style={{color: 'red', marginTop: '2px', display: 'inline-block' }}>{errorMsg}</span>}
{/* {this.state.NavmodalValuetype===true?<span className={"color-red"}>
{this.state.NavmodalValues}
</span>:""} */}
{/* this.state.NavmodalValuetype===true?"clearfix mt20 edu-txt-center": */}
<div className={"clearfix mt50 edu-txt-center"}>
<a className="task-btn mr30" onClick={this.onCancel}>取消</a>
<a className="task-btn task-btn-orange" onClick={this.onSave}>确定</a>
</div>
</Modal>
)
}
}
export default AddDirModal;
@@ -0,0 +1,146 @@
import React,{ Component } from "react";
import {Tooltip} from 'antd'
import moment from 'moment'
import { getUrl, WordsBtn, ConditionToolTip } from 'educoder'
import './boardsListItem.css';
class BoardsListItem extends Component{
constructor(props){
super(props);
this.state = {
}
}
onTitleClick = (discussMessage) => {
if(this.props.checkIfLogin()===false){
this.props.showLoginDialog()
return
}
// if(this.props.checkIfProfileCompleted()===false){
// this.setState({
// AccountProfiletype:true
// })
// return
// }
// if(this.props.checkIfProfessionalCertification()===false){
// this.props.showProfileCompleteDialog()
// return
// }
const isAdminOrStudent = this.props.isAdminOrStudent();
if (!isAdminOrStudent && discussMessage.is_public == false) {
// 没有权限访问
return;
}
const cid = this.props.match.params.coursesId
const board_id = this.props.match.params.boardId
this.props.toDetailPage(cid, board_id, discussMessage.id)
}
render(){
let { addGroup } = this.state;
const isAdmin = this.props.isAdmin()
const isAdminOrStudent = this.props.isAdminOrStudent()
const { checkBox, discussMessage, onSticky, onItemClick, current_user } = this.props;
if (!discussMessage || !discussMessage.author) {
return '';
}
let canNotLink = !isAdminOrStudent && discussMessage.is_public == false
return(
<div className="panel-inner-fourm boardsList">
<style>{`
.boardsList .panel-list-img {
width: 50px;
height: 50px;
}
`}</style>
{ checkBox }
<a href={`/users/${discussMessage.author.login}`} alt="用户" style={{"width":"50px","height":"50px","display":"block", margin: "0 10px"}}>
{/* /images/avatars/User/1?1529221779 */}
<img
alt="1?1529221779" className="panel-list-img mr15" height="50"
src={`${getUrl()}/images/${discussMessage.author.image_url}`} width="50"
></img>
</a>
<div className="clearfix ds pr pt5 contentSection" onClick={() => onItemClick(discussMessage)}>
<h6>
{canNotLink?<span className="fl mt3 font-16 font-bd color-dark maxwidth580 pointer" title={canNotLink?"私有属性,非课堂成员不能访问":`${discussMessage.subject}`}>{discussMessage.subject}</span>:<a className="panel-list-title hide fl mt5 color-dark font-bd pointer"
style={{ fontWeight: 'bold', maxWidth: '700px' }}
title={discussMessage.subject}
onClick={canNotLink ? () => {} : () => this.onTitleClick(discussMessage)}
>{discussMessage.subject}</a>}
{ !!discussMessage.sticky && <span className="btn-cir btn-cir-red fl mt5 ml5">置顶</span> }
{
discussMessage.is_public == false ? (<Tooltip title={'私有属性,非课堂成员不能访问'} placement="bottom">
<i className="iconfont icon-guansuo color-grey-c ml10 font-16 fl mt4"></i>
</Tooltip>) : ""
}
</h6>
<div className="fr">
{(isAdmin || discussMessage.author.login == current_user.login) &&
<WordsBtn style="blue" className="fr font-16 ml28"
onClick={(e) => { this.props.toEditPage(this.props.match.params.coursesId, this.props.match.params.boardId, discussMessage.id )} }>编辑</WordsBtn> }
{ isAdmin && <WordsBtn style="blue" className="fr font-16 ml28"
onClick={(e) => { debugger; onSticky(discussMessage); e.cancelBubble = true; e.stopPropagation();}}>
{ discussMessage.sticky ? '取消置顶' : '置顶' }</WordsBtn> }
{canNotLink?"":<WordsBtn style="blue" className="font-16 fr " onClick={canNotLink ? () => {} : () => this.onTitleClick(discussMessage)}>查看详情</WordsBtn>}
</div>
<div className="cl"></div>
<p className="color-grey panel-lightgrey mt18 fl">
<span className="mr50">
<a href={`/users/${discussMessage.author.login}`} className="panel-name-small hide fl mr15 mr30 color-grey3 font-14">{discussMessage.author.name}</a>
{ discussMessage.total_replies_count != 0 && <span className="mr15 color-grey9 font-14">{discussMessage.total_replies_count} 回复</span> }
{ discussMessage.praises_count != 0 && <span className="mr15 color-grey9 font-14">{discussMessage.praises_count} 点赞</span> }
{ discussMessage.visits != 0 && <span className="mr15 color-grey9 font-14">{discussMessage.visits} 浏览</span> }
<span className="mr15 color-light-grey-C font-14">{moment(discussMessage.created_on).fromNow()} </span>
</span>
</p>
{
discussMessage.category_name &&
<div className={'directory_style'}>
<ConditionToolTip title={discussMessage.category_name} condition={discussMessage.category_name}>
{ <div className=" color-grey9 task-hide fr"
style={{"maxWidth":"216px"}}
title={discussMessage.category_name}>
所属目录{discussMessage.category_name}
</div>
}
</ConditionToolTip>
</div>
}
{/* { (isAdmin || discussMessage.author.login == current_user.login) &&
<div className="homepagePostSetting" style={{"right":"4px","top":"5px","display":"block"}}>
<ul>
<li className="edu-position edu-position-hidebox">
<i className="fa fa-bars color-grey-b"></i>
<ul className="edu-position-hide undis">
{(isAdmin || discussMessage.author.login == current_user.login) && <li><a href="javascript:void(0)" onClick={(e) => {
this.props.toEditPage(this.props.match.params.coursesId, this.props.match.params.boardId, discussMessage.id )} } >编辑</a></li>
}
{isAdmin && <li><a href="javascript:void(0)" onClick={(e) => { debugger; onSticky(discussMessage); e.cancelBubble = true; e.stopPropagation();} }>
{ discussMessage.sticky ? '取消置顶' : '置顶' }
</a></li>
}
</ul>
</li>
</ul>
</div>
} */}
</div>
</div>
)
}
}
export default BoardsListItem;
@@ -0,0 +1,497 @@
import React,{ Component } from "react";
import {
Form, Input, InputNumber, Switch, Radio,
Slider, Button, Upload, Icon, Rate, Checkbox, message,
Row, Col, Select, Modal, Divider,Tooltip
} from 'antd';
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
import axios from 'axios'
import './board.css'
import "../common/formCommon.css"
import AddDirModal from './AddDirModal'
import { RouteHOC } from './common.js'
import CBreadcrumb from '../common/CBreadcrumb'
import {getUploadActionUrl, bytesToSize, uploadNameSizeSeperator, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll} from 'educoder';
const confirm = Modal.confirm;
const $ = window.$
const { Option } = Select;
const MAX_TITLE_LENGTH = 60
// https://lanhuapp.com/web/#/item/project/board/detail?pid=a3bcd4b1-99ce-4e43-8ead-5b8b0a410807&project_id=a3bcd4b1-99ce-4e43-8ead-5b8b0a410807&image_id=71072679-b925-4824-aceb-4649535e3652
class BoardsNew extends Component{
constructor(props){
super(props);
this.mdRef = React.createRef();
this.state = {
fileList: [],
boards: [],
title_num: 0,
email_notify:false,
isemail_notify:false,
isemail_notifys:false
}
}
addSuccess = () => {
this.fetchBoards()
}
fetchBoards = () => {
const isEdit = this.isEdit
const boardId = this.props.match.params.boardId
const boardsUrl = `/courses/board_list.json?board_id=${boardId}`
axios.get(boardsUrl, { })
.then((response) => {
if (response.data.status == 0) {
this.setState({
boards: response.data.data.boards || [],
boardsdata:response.data.data,
course_id: response.data.data.course_id
})
if (!isEdit) {
response.data.data.boards.forEach( board => {
if (board.id == boardId) {
this.setState({ board_name: board.name })
}
})
// board_name
}
}
})
.catch(function (error) {
console.log(error);
});
}
componentDidMount = () => {
const topicId = this.props.match.params.topicId
const isEdit = !!topicId
this.isEdit = isEdit
const boardId = this.props.match.params.boardId
this.fetchBoards()
if (isEdit) {
const url = `/messages/${topicId}.json`
axios.get(url, {
})
.then((response) => {
if (response.data.status == 0) {
const { id, data } = response.data;
if (data) {
this.editTopic = data;
this.props.form.setFieldsValue({
sticky: !!data.sticky,
content: data.content,
subject: data.subject,
select_board_id: data.board_id // TODO 没返回给前端
});
this.mdRef.current.setValue(data.content)
const _fileList = data.attachments.map(item => {
return {
id: item.id,
uid: item.id,
name: appendFileSizeToUploadFile(item),
url: item.url,
status: 'done'
}
})
this.setState({ fileList: _fileList, board_name: data.board_name, title_num: parseInt(data.subject.length) ,isemail_notifys:response.data.data.email_notify})
}
}
})
.catch(function (error) {
console.log(error);
});
} else {
const boardId = this.props.match.params.boardId
this.props.form.setFieldsValue({
select_board_id: parseInt(boardId)
});
}
}
handleSubmit = (e) => {
e.preventDefault();
const cid = this.state.course_id
const boardId = this.props.match.params.boardId
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
if (this.isEdit == true) {
const editTopic = this.editTopic
const editUrl = `/messages/${editTopic.id}.json`
let attachment_ids = undefined
if (this.state.fileList) {
attachment_ids = this.state.fileList.map(item => {
return item.response ? item.response.id : item.id
})
}
axios.put(editUrl, {
subject: values.subject,
select_board_id: values.select_board_id,
content: values.content,
sticky: values.sticky,
email_notify:this.state.isemail_notify,
attachment_ids,
})
.then((response) => {
if (response.data.status == 0) {
const { id } = response.data;
console.log('--- success')
this.props.toDetailPage(cid, values.select_board_id, editTopic.id)
}
})
.catch(function (error) {
console.log(error);
});
} else {
const url = `/boards/${boardId}/messages.json`
let attachment_ids = undefined
if (this.state.fileList) {
attachment_ids = this.state.fileList.map(item => {
return item.response.id
})
}
axios.post(url, {
...values,
email_notify:this.state.isemail_notify,
course_id: cid,
attachment_ids,
})
.then((response) => {
if (response.data.data && response.data.status == 0) {
const { id } = response.data.data;
if (id) {
console.log('--- success')
this.props.toDetailPage(cid, values.select_board_id, id)
}
}
})
.catch(function (error) {
console.log(error);
});
}
} else {
$("html").animate({ scrollTop: $('html').scrollTop() - 100 })
}
});
}
// 附件相关 START
handleChange = (info) => {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let fileList = info.fileList;
this.setState({ fileList: appendFileSizeToUploadFileAll(fileList) });
}
}
onAttachmentRemove = (file) => {
if(!file.percent || file.percent == 100){
this.props.confirm({
// title: '确定要删除这个附件吗?',
content: '是否确认删除?',
okText: '确定',
cancelText: '取消',
// content: 'Some descriptions',
onOk: () => {
this.deleteAttachment(file)
},
onCancel() {
console.log('Cancel');
},
});
return false;
}
}
deleteAttachment = (file) => {
// 初次上传不能直接取uid
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
axios.delete(url, {
})
.then((response) => {
if (response.data) {
const { status } = response.data;
if (status == 0) {
console.log('--- success')
this.setState((state) => {
const index = state.fileList.indexOf(file);
const newFileList = state.fileList.slice();
newFileList.splice(index, 1);
return {
fileList: newFileList,
};
});
}
}
})
.catch(function (error) {
console.log(error);
});
}
// 附件相关 ------------ END
changeTitle=(e)=>{
console.log(e.target.value.length);
this.setState({
title_num: parseInt(e.target.value.length)
})
}
goBack = () => {
// this.props.history.goBack()
const courseId=this.props.match.params.coursesId;
const boardId = this.props.match.params.boardId
this.props.toListPage(courseId, boardId)
}
setemailchange=(e)=>{
this.setState({
isemail_notify:e.target.checked
})
}
render() {
let { addGroup, fileList, course_id, title_num } = this.state;
const { getFieldDecorator } = this.props.form;
const { current_user } = this.props
const formItemLayout = {
labelCol: {
xs: { span: 24 },
// sm: { span: 8 },
sm: { span: 24 },
},
wrapperCol: {
xs: { span: 24 },
// sm: { span: 16 },
sm: { span: 24 },
},
};
const uploadProps = {
width: 600,
fileList,
multiple: true,
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUploadActionUrl()}`,
onChange: this.handleChange,
onRemove: this.onAttachmentRemove,
beforeUpload: (file) => {
console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
this.props.showNotification('文件大小必须小于150MB!');
}
return isLt150M;
},
};
const isAdmin = this.props.isAdmin()
const courseId=this.props.match.params.coursesId;
const boardId = this.props.match.params.boardId
const isCourseEnd = this.props.isCourseEnd();
document.title=this.props.coursedata&&this.props.coursedata.name;
// console.log(this.state)
return(
<div className="newMain ">
<AddDirModal {...this.props}
title="新建目录"
label="目录名称"
ref="addDirModal"
addSuccess={this.addSuccess}
></AddDirModal>
<style>{`
.courseForm .ant-form {
}
.courseForm .formBlock {
padding: 20px 30px 30px 30px;
border-bottom: 1px solid #EDEDED;
margin-bottom: 0px;
background: #fff;
}
.courseForm .noBorder {
border-bottom: none;
}
`}</style>
<div className="edu-class-container edu-position courseForm">
<CBreadcrumb items={[
{ to: current_user&&current_user.first_category_url, name: this.props.coursedata ? this.props.coursedata.name : ''},
{ to: `/courses/${courseId}/boards/${boardId}`, name: this.state.board_name },
{ name: this.isEdit ? '帖子编辑' : '帖子新建'}
]}></CBreadcrumb>
<p className="clearfix mt20 mb20">
<span className="fl font-24 color-grey-3">{this.isEdit ? "编辑" : "新建"}帖子</span>
<a href="javascript:void(0)" className="color-grey-6 fr font-16 mr2"
onClick={this.goBack}>
返回
</a>
</p>
{/* notRequired */}
<Form {...formItemLayout} onSubmit={this.handleSubmit}>
<div className="formBlock" style={{paddingBottom: '0px', position: 'relative'}}>
{this.state.boardsdata&&this.state.boardsdata.email_notify===true?this.props.isAdminOrTeacher()===true?<Tooltip placement="bottom" title={this.state.isemail_notifys===true?"邮件只能发送一次":""}><span className={"setemail fr mr70 setemailposition"}>
<Checkbox onChange={this.setemailchange} checked={this.state.isemail_notifys===true?this.state.isemail_notifys:this.state.isemail_notify} disabled={this.state.isemail_notifys}>发送邮件提醒</Checkbox>
</span></Tooltip>:"":""}
{ isAdmin &&
<React.Fragment>
{getFieldDecorator('sticky', {
valuePropName: 'checked',
})(
isAdmin && <Checkbox style={{ right: '22px',
top: '17px',
position: 'absolute'
}}>置顶</Checkbox>
)}
{/* checkbox 有个边距样式 .ant-checkbox-wrapper + span, */}
<span style={{ "padding-left": 0, "padding-right": 0 }}></span>
</React.Fragment>
}
<style>
{
`
.yslboardsnewinputaddonAfter .ant-input{
border-right: none !important;
height: 40px !important;
}
`
}
</style>
<Form.Item
label="标题"
className="topicTitle "
>
{getFieldDecorator('subject', {
rules: [{
required: true, message: '请输入标题',
}, {
max: MAX_TITLE_LENGTH, message: `最大限制为${MAX_TITLE_LENGTH}个字符`,
}],
})(
<Input placeholder={`请输入帖子标题,最大限制${MAX_TITLE_LENGTH}个字符 `}className="searchViewAfter yslboardsnewinputaddonAfter" maxLength={MAX_TITLE_LENGTH}
onInput={this.changeTitle} addonAfter={`${title_num}/${MAX_TITLE_LENGTH}`} />
)}
</Form.Item>
<Form.Item
label=""
style={{ display: 'inline-block' }}
>
{getFieldDecorator('select_board_id', {
// initialValue: '3779',
})(
<Select style={{ width: 230 }}
dropdownRender={menu => (
<div>
{menu}
{
isAdmin && !isCourseEnd &&
<React.Fragment>
<Divider style={{ margin: '4px 0' }} />
<div style={{ padding: '8px', cursor: 'pointer' }} onMouseDown={() => this.refs['addDirModal'].open()}>
<Icon type="plus" /> 新建目录
</div>
</React.Fragment>
}
</div>
)}
>
{this.state.boards.map(item => {
return (
<Option value={item.id}>{item.name}</Option>
)
})}
</Select>
)}
</Form.Item>
{/* { isAdmin && <Form.Item
label=""
style={{ display: 'inline-block', marginLeft: "14px" }}
>
{getFieldDecorator('sticky', {
})(
<Checkbox>置顶</Checkbox>
)}
</Form.Item> } */}
</div>
<style>{`
.courseMessageMD {
width: 1140px;
}
.uploadBtn.ant-btn {
border: none;
color: #4CACFF;
box-shadow: none;
background: transparent;
padding: 0 6px;
}
.upload_1 .ant-upload-list {
width: 350px;
}
`}</style>
<div className="formBlock noBorder">
<Form.Item
label="内容"
className="mdInForm"
>
{getFieldDecorator('content', {
rules: [{
required: true, message: '请输入帖子内容',
}, {
max: 10000, message: '最大限制为10000个字符',
}],
})(
<TPMMDEditor ref={this.mdRef} placeholder={'请在此输入帖子详情,最大限制为10000个字符'}
mdID={'courseMessageMD'} initValue={this.editTopic ? this.editTopic.content : ''} className="courseMessageMD"></TPMMDEditor>
)}
</Form.Item>
<Upload {...uploadProps} className="upload_1">
<Button className="uploadBtn">
<Icon type="upload" /> 上传附件
</Button>
(单个文件150M以内)
</Upload>
</div>
<Form.Item>
<div className="clearfix mt30 mb30">
<Button type="primary" htmlType="submit" className="defalutSubmitbtn fl mr20">提交</Button>
<a className="defalutCancelbtn fl"
onClick={() => this.isEdit ?
this.props.toDetailPage(Object.assign({}, this.props.match.params, {'coursesId': course_id})) :
this.props.toListPage(Object.assign({}, this.props.match.params, {'coursesId': course_id})) }>取消</ a>
</div>
</Form.Item>
</Form>
</div>
</div>
)
}
}
const WrappedBoardsNew = Form.create({ name: 'boardsNew' })(BoardsNew);
export default RouteHOC()(WrappedBoardsNew);
@@ -0,0 +1,202 @@
import React,{ Component } from "react";
import {
Form, Input, InputNumber, Switch, Radio,
Slider, Button, Upload, Icon, Rate, Checkbox,
Row, Col, Select
} from 'antd';
const $ = window.$
const { Option } = Select;
// 红点在输入框前面的版本
// https://lanhuapp.com/web/#/item/project/board/detail?pid=a3bcd4b1-99ce-4e43-8ead-5b8b0a410807&project_id=a3bcd4b1-99ce-4e43-8ead-5b8b0a410807&image_id=71072679-b925-4824-aceb-4649535e3652
class BoardsNew extends Component{
constructor(props){
super(props);
this.state = {
}
}
compareToFirstPassword = (rule, value, callback) => {
const form = this.props.form;
if (value && value !== form.getFieldValue('password')) {
callback('Two passwords that you enter is inconsistent!');
} else {
callback();
}
}
validateToNextPassword = (rule, value, callback) => {
const form = this.props.form;
if (value && this.state.confirmDirty) {
form.validateFields(['confirm'], { force: true });
}
callback();
}
handleConfirmBlur = (e) => {
const value = e.target.value;
this.setState({ confirmDirty: this.state.confirmDirty || !!value });
}
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
} else {
$("html").animate({ scrollTop: $('html').scrollTop() - 100 })
}
});
}
render() {
let { addGroup } = this.state;
const { getFieldDecorator } = this.props.form;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
// sm: { span: 8 },
sm: { span: 24 },
},
wrapperCol: {
xs: { span: 24 },
// sm: { span: 16 },
sm: { span: 24 },
},
};
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 24,
offset: 0,
},
sm: {
span: 16,
offset: 8,
},
},
};
return(
<div className="newMain ">
<style>{`
.edu-class-container {
width: 1200px;
margin: 10px auto 20px;
}
/* form 样式 --------------------------------------------- */
/* 不知道被哪个样式影响,这里需要重置 */
.ant-input:focus {
border-color: #40a9ff;
}
@media (min-width: 576px) {
.ant-col-sm-24 {
text-align: left;
}
}
.ant-form-item-control-wrapper.ant-col-xs-24.ant-col-sm-24 {
margin-left: 2px;
}
/*必填*/
.ant-form-item-required::before {
display: none;
}
.ant-form-item-control-wrapper.ant-col-xs-24.ant-col-sm-24::before {
display: inline-block;
margin-right: 8px;
color: #f5222d;
font-size: 14px;
font-family: SimSun, sans-serif;
line-height: 1;
content: '*';
}
.ant-form-item-control {
display: inline-block;
}
/* form 样式 --------------------------------------------- */
.topicTitle.ant-form-item {
margin-bottom: 8px;
}
/* errorInline ----------- */
/* 这里需要指定form组件的宽度 style={{ width: 270 }} */
.errorInline .ant-form-item-children input {
width: auto
}
.errorInline .ant-form-explain {
display: inline;
margin-left: 10px;
}
/* errorInline ----------- */
`}</style>
<div className="edu-class-container edu-position">
<Form {...formItemLayout} onSubmit={this.handleSubmit}>
<Form.Item
label="E-mail"
className="topicTitle errorInline"
>
{getFieldDecorator('email', {
rules: [{
type: 'email', message: 'The input is not valid E-mail!',
}, {
required: true, message: 'Please input your E-mail!',
}],
})(
<Input style={{ width: 270 }} />
)}
</Form.Item>
<Form.Item
label=""
>
{getFieldDecorator('prefix', {
initialValue: '86',
})(
<Select style={{ width: 270 }}>
<Option value="86">+86</Option>
<Option value="87">+87</Option>
</Select>
)}
</Form.Item>
<Form.Item
label="Password"
>
{getFieldDecorator('password', {
rules: [{
required: true, message: 'Please input your password!',
}, {
validator: this.validateToNextPassword,
}],
})(
<Input type="password" />
)}
</Form.Item>
<Form.Item
label="Confirm Password"
>
{getFieldDecorator('confirm', {
rules: [{
required: true, message: 'Please confirm your password!',
}, {
validator: this.compareToFirstPassword,
}],
})(
<Input type="password" onBlur={this.handleConfirmBlur} />
)}
</Form.Item>
<Form.Item {...tailFormItemLayout}>
<Button type="primary" htmlType="submit">Register</Button>
</Form.Item>
</Form>
</div>
</div>
)
}
}
const WrappedBoardsNew = Form.create({ name: 'boardsNew' })(BoardsNew);
export default WrappedBoardsNew;
@@ -0,0 +1,15 @@
.edu-class-container {
width: 1200px;
margin: 10px auto 20px;
}
#forum_index_list {
margin-top: 90px;
margin-bottom: 320px;
}
#yslforum_index_list {
margin-top: 20px;
margin-bottom: 320px;
}
.uploadBtn {
margin-left: 0px;
}
@@ -0,0 +1,748 @@
import React, { Component } from 'react';
import axios from 'axios'
import moment from 'moment'
import Comments from '../../comment/Comments'
import {ImageLayerOfCommentHOC} from '../../page/layers/ImageLayerOfCommentHOC'
import MemoDetailMDEditor from '../../forums/MemoDetailMDEditor'
import { RouteHOC } from './common.js'
import '../../forums/Post.css'
import '../../forums/RightSection.css'
import './TopicDetail.css'
import '../common/courseMessage.css'
import { Pagination, Tooltip,Button } from 'antd'
import { bytesToSize, ConditionToolTip, markdownToHTML, MarkdownToHtml , setImagesUrl } from 'educoder'
import SendToCourseModal from '../coursesPublic/modal/SendToCourseModal'
import CBreadcrumb from '../common/CBreadcrumb'
import { generateComments, generateChildComments, _findById, handleContentBeforeCreateNew, addNewComment
, addSecondLevelComment, NEED_TO_WRITE_CONTENT, handleContentBeforeCreateSecondLevelComment
, handleDeleteComment, handleCommentPraise, handleHiddenComment } from '../common/CommentsHelper'
const $ = window.$
const REPLY_PAGE_COUNT = 10;
let setTime;
function urlStringify(params) {
let noParams = true;
let paramsUrl = '';
for (let key in params) {
noParams = false;
paramsUrl += `${key}=${params[key]}&`
}
if (noParams) {
return '';
}
paramsUrl = paramsUrl.substring(0, paramsUrl.length - 1);
return paramsUrl;
}
class TopicDetail extends Component {
constructor(props) {
super(props)
this.state = {
memo: {},
memoLoading: true,
hasMoreComments: false,
pageCount: 1,
comments: [],
goldRewardDialogOpen: false,
author:undefined,
commentstype:false
}
}
componentDidMount() {
window.$("html,body").animate({"scrollTop":0})
const topicId = this.props.match.params.topicId
const bid = this.props.match.params.boardId
const memoUrl = `/messages/${topicId}.json`;
this.setState({
memoLoading: true
})
axios.get(memoUrl,{
})
.then((response) => {
if (response.data.status === -1) {
setTimeout(() => {
this.props.showNotification('帖子不存在!')
}, 300)
// this.props.toListPage(response.data.data.course_id, bid)
return;
} else {
this.setState({
memo: Object.assign({}, {
...response.data.data,
replies_count: response.data.data.total_replies_count
}, {...this.state.memo}),
author:response.data.data.author
}, () => {
})
// const { memo_replies, memo } = response.data;
// let hasMoreComments = false;
// if (memo_replies && memo_replies.length === 10 && memo.total_replies_count > 10) {
// // 遍历一遍,计算下是否还有评论未加载
// let totalCount = 10;
// memo_replies.forEach(item=>{
// totalCount += item.children.length
// })
// if (totalCount < memo.total_replies_count) {
// hasMoreComments = true;
// }
// }
// this.setState({
// hasMoreComments,
// pageCount: 1,
// comments: memo_replies
// })
// delete response.data.memo_replies;
// this.setState(response.data)
// const user = response.data.current_user;
// user.tidding_count = response.data.tidding_count;
// this.props.initCommonState(user)
}
this.setState({
memoLoading: false
})
}).catch((error) => {
console.log(error)
})
this.fetchReplies()
$('body>#root').on('onMemoDelete', (event) => {
// const val = $('body>#root').data('onMemoDelete')
const val = window.onMemoDelete ;
this.onMemoDelete( JSON.parse(decodeURIComponent(val)) )
})
}
onPaginationChange = (pageCount) => {
this.setState({ pageCount }, () => {
this.fetchReplies()
})
}
componentWillUnmount() {
$('body>#root').off('onMemoDelete')
}
onMemoDelete(memo) {
const deleteUrl = `/commons/delete.json`;
// 获取memo list
axios.delete(deleteUrl, { data: {
object_id: memo.id,
object_type: 'message'
}
})
.then((response) => {
const status = response.data.status
if (status === 0) {
this.props.showNotification('删除成功');
const props = Object.assign({}, this.props, {})
this.props.toListPage( Object.assign({}, this.props.match.params, {'coursesId': this.state.memo.course_id} ) )
} else if (status === -1) {
this.props.showNotification('帖子已被删除');
this.props.history.push(`/forums`)
}
}).catch((error) => {
console.log(error)
})
}
componentDidUpdate(prevProps, prevState, snapshot) {
// if (this.state.memo && this.state.memo.content
// && (!prevProps.memo || prevProps.memo.content != this.state.memo.content) ) {
if (this.state.memo && this.state.memo.content && prevState.memoLoading === true && this.state.memoLoading === false) {
// md渲染content,等xhr执行完(即memoLoading变化),memo.content更新后初始化md
setTimeout(()=>{
// var shixunDescr = window.editormd.markdownToHTML("memo_content_editorMd", {
// htmlDecode: "style,script,iframe", // you can filter tags decode
// taskList: true,
// tex: true, // 默认不解析
// flowChart: true, // 默认不解析
// sequenceDiagram: true // 默认不解析
// });
}, 200)
}
}
clickPraise(){
const { memo } = this.state;
// const url = `/api/v1/discusses/${memo.id}/plus`;
const url = memo.user_praise ? '/praise_tread/unlike.json' : `/praise_tread/like.json`;
const _method = memo.user_praise ? axios.delete : axios.post
let _data = {
object_id: memo.id,
object_type: 'message', //Discuss
}
if (memo.user_praise) {
_data = {
data: _data
}
}
_method(url, {
..._data
},
{
}
).then((response) => {
const newMemo = Object.assign({}, this.state.memo)
newMemo.praises_count = newMemo.user_praise ? newMemo.praises_count - 1 : newMemo.praises_count + 1
newMemo.total_praises_count = newMemo.user_praise ? newMemo.total_praises_count - 1 : newMemo.total_praises_count + 1
newMemo.user_praise = !newMemo.user_praise
this.setState({memo : newMemo })
}).catch((error) => {
console.log(error)
})
}
renderAttachment() {
const { memo } = this.state;
const attachments = []
memo.attachments.forEach((item, index) => {
const ar = item.url.split('/')
const fileName = item.title || ar[ar.length - 1]
let filesize = 0
if (item.filesize) {
filesize = item.filesize
// filesize = bytesToSize(item.filesize)
}
attachments.push(
// <p className="clearfix" key={index} >
// <a href={item.url} className="color-green clearfix notefileDownload">
// <i className="iconfont icon-fujian color-green ml5 fl"></i>
// {fileName && <ConditionToolTip title={fileName} condition={fileName.length > 30 }>
// <span className="fl task-hide upload_item" style={{ color: '#333'}}>{fileName}</span>
// </ConditionToolTip>}
// <span className="fl" style={{ color: '#999', marginLeft: '6px'}}>{filesize? ` ${filesize.replace(' ', '')}` : ''}</span>
// </a>
// </p>
<div className="color-grey df" key={index}>
<a className="color-grey ">
<i className="font-14 color-green iconfont icon-fujian mr8" aria-hidden="true"></i>
</a>
{/* {fileName && <ConditionToolTip title={fileName} condition={fileName.length > 30 }> </ConditionToolTip>} */}
<a href={item.url} title={fileName.length > 30 ? fileName : ''}
className="mr12 color9B9B overflowHidden1" length="58" style={{maxWidth: '480px'}}>
{fileName}
</a>
<span className="color656565 mt2 color-grey-6 font-12 mr8">{item.filesize}</span>
</div>
)
})
return attachments;
}
// ------------------------------------------------------------------------------------------- comments START
// ------------------------------------------------------------------------------------------- comments START
transformReply = (reply, children = []) => {
const isAdmin = this.props.isAdmin()
const isSuperAdmin = this.props.isSuperAdmin()
return {
isSuperAdmin: isSuperAdmin,
admin: isAdmin, //
permission: true, //
children: children,
child_message_count: reply.total_count,
hidden: reply.is_hidden,
id: reply.id,
image_url: reply.author.image_url,
reward: null, //
time: moment(reply.created_on).fromNow(),
user_id: reply.author.id,
user_login: reply.author.login,
user_praise: reply.liked,
username: reply.author.name,
content: reply.content,
praise_count: reply.praises_count
}
}
fetchReplies = () => {
const topicId = this.props.match.params.topicId
const url = `/messages/${topicId}/reply_list.json?page=${this.state.pageCount}&page_size=${REPLY_PAGE_COUNT}`
axios.get(url,{
})
.then((response) => {
const { replies, liked, total_replies_count, total_count } = response.data.data
const memo = Object.assign({}, this.state.memo)
memo.user_praise = liked
memo.total_replies_count = total_replies_count;
this.setState({
memo,
comments: generateComments(replies, this.transformReply, 'replies'),
// : this.state.comments.concat(comments),
total_count: total_count
})
}).catch((error) => {
console.log(error)
})
}
_getUser() {
const { current_user } = this.props;
current_user.user_url = `/users/${current_user.login}`;
return current_user;
}
_findById = _findById
replyComment = (commentContent, id, editor) => {
const { showNotification } = this.props;
// if (!commentContent || commentContent.length === 0) {
// showNotification(NEED_TO_WRITE_CONTENT)
// return;
// }
if (this.state.memo.id === id ) { // 回复帖子
this.createNewComment(commentContent, id, editor);
return;
}
const url = `/messages/${id}/reply.json`;
const { comments } = this.state;
const user = this._getUser();
/*
移除末尾的空行
.replace(/(\n<p>\n\t<br \/>\n<\/p>)*$/g,'');
*/
commentContent = handleContentBeforeCreateSecondLevelComment(commentContent)
if (!commentContent) {
this.props.showNotification('不能为空')
return;
}
axios.post(url, {
content: commentContent
},
{
}
).then((response) => {
if (response.data.data.id) {
let newId = response.data.data.id;
const commentIndex = this._findById(id, comments);
const parentComment = comments[commentIndex]
this.setState({
// runTesting: false,
comments: addSecondLevelComment(comments, parentComment, commentIndex, newId, commentContent, user, editor)
}, ()=>{
// keditor代码美化
editor.html && window.prettyPrint()
})
const newMemo2 = Object.assign({}, this.state.memo);
newMemo2.total_replies_count = newMemo2.total_replies_count + 1;
this.setState({
memo: newMemo2
})
}
}).catch((error) => {
console.log(error)
})
}
// 公共接口 --- 删除回复
deleteComment = (parrentComment, childCommentId) => {
handleDeleteComment(this, parrentComment, childCommentId, 'message')
}
// 公共接口 --- 回复点赞
commentPraise = (discussId) => {
handleCommentPraise(this, discussId, 'message', (old_user_praise) => {
const newMemo2 = Object.assign({}, this.state.memo);
newMemo2.total_praises_count = old_user_praise
? newMemo2.total_praises_count - 1 : newMemo2.total_praises_count + 1;
this.setState({
memo: newMemo2
})
})
}
// 公共接口 --- 隐藏回复
hiddenComment = (item, childCommentId) => {
handleHiddenComment(this, item, childCommentId, 'message')
}
createNewComment = (commentContent, id, editor) => {
let content = handleContentBeforeCreateNew(commentContent);
const { memo } = this.props;
const url = `/messages/${id}/reply.json`;
// const url = `/api/v1/memos/${memo.id}/reply`;
let { comments } = this.state;
axios.post(url, {
content: content
},
{
}
).then((response) => {
if (response.data.status === -1) {
console.error('服务端异常')
return;
}
// this.props.showNotification('帖子发表成功')
if (response.data) {
const _id = response.data.data.id;
// ke
editor.html && editor.html('');
editor.afterBlur && editor.afterBlur()
// md
editor.setValue && editor.setValue('')
const user = this._getUser();
this.setState({
comments: addNewComment(comments, _id, content, user, this.props.isSuperAdmin(), this)
})
const newMemo2 = Object.assign({}, this.state.memo);
newMemo2.total_replies_count = newMemo2.total_replies_count + 1;
this.setState({
memo: newMemo2
})
this.refs.editor.showEditor();
this.refs.editor.close();
}
}).catch((error) => {
console.log(error)
})
}
/**
* parent.isAllChildrenLoaded 为 true的时候,表示已经没有更多子回复了
*/
loadMoreChildComments = (parent) => {
const url = `/messages/${parent.id}/reply_list.json?page=1&page_size=500`
axios.get(url,{
})
.then((response) => {
const { replies, liked, total_replies_count } = response.data.data
// const memo = Object.assign({}, this.state.memo)
// memo.total_replies_count = total_replies_count;
this.setState({
// memo,
comments: generateChildComments(replies, this.state.comments, parent, this.transformReply)
})
}).catch((error) => {
console.log(error)
})
}
// ------------------------------------------------------------------------------------------- comments END
// ------------------------------------------------------------------------------------------- comments END
// 置顶
setTop(memo) {
// const params = {
// sticky: memo.sticky ? 0 : 1,
// }
// if (this.state.p_s_order) {
// params.order = this.state.p_s_order;
// }
// if (this.state.p_forum_id) {
// params.forum_id = this.state.p_forum_id;
// }
// let paramsUrl = urlStringify(params)
const set_top_or_down_Url = `/messages/${memo.id}/sticky_top.json`;
// 获取memo list
axios.put(set_top_or_down_Url, {
})
.then((response) => {
const status = response.data.status
if (status === 0) {
this.props.showNotification( memo.sticky ? '取消置顶成功' : '置顶成功');
memo.sticky = memo.sticky ? false : true
this.setState({
memo: Object.assign({}, memo)
})
}
}).catch((error) => {
console.log(error)
})
}
setRewardDialogVisible = (visible) => {
this.setState({
goldRewardDialogOpen: visible
})
}
showRewardDialog = () => {
this.setState({
goldRewardDialogOpen: true
})
}
// --------------------------------------------------------------------------------------------帖子獎勵 END
showCommentInput = () => {
if (window.__useKindEditor === true) {
this.refs.editor.showEditor();
} else {
this.refs.editor.showEditor();
}
}
initReply = (parent) => {
if (!parent.isAllChildrenLoaded) {
this.loadMoreChildComments(parent)
}
}
startcomments=()=>{
setTime = setInterval( ()=> {
this.fetchReplies()
}, 5000);
this.setState({
commentstype:true
})
}
clearcomments=()=>{
clearInterval(setTime);
this.setState({
commentstype:false
})
}
render() {
const { match, history } = this.props
const { recommend_shixun, current_user,author_info } = this.props;
const { memo, comments, hasMoreComments, commentstype, pageCount, total_count , author } = this.state;
const messageId = match.params.topicId
if (this.state.memoLoading || !current_user) {
return <div className="edu-back-white" id="forum_index_list"></div>
}
current_user.user_url = `/users/${current_user.login}`;
const isCurrentUserTheAuthor = current_user.login == memo.author.login
const isAdmin = this.props.isAdmin()
// TODO 图片上传地址
const courseId=this.props.match.params.coursesId;
const boardId = this.props.match.params.boardId;
const isCourseEnd = this.props.isCourseEnd();
document.title=this.props.coursedata&&this.props.coursedata.name;
return (
<div className="edu-back-white edu-class-container edu-position course-message topicDetail" id="yslforum_index_list"> {/* fl with100 */}
<style>{`
.topicDetail #forum_list .return_btn.no_mr {
margin-right: 1px;
}
/* 有内容时,编辑器下方的边框*/
.topicDetail .borderBottom.commentInputs {
border-bottom: 1px solid rgb(238, 238, 238);
}
.independent {
background: rgb(250, 250, 250);
padding-bottom: 20px;
margin-bottom: 0px !important;
}
.course-message.topicDetail .panel-comment_item .comment_orig_content {
width: 1072px;
}
`}</style>
<CBreadcrumb className={'independent'} items={[
{ to: current_user&&current_user.first_category_url, name: this.props.coursedata.name},
{ to: `/courses/${courseId}/boards/${boardId}`, name: memo.board_name },
{ name: '帖子详情'}
]}></CBreadcrumb>
<SendToCourseModal
ref="sendToCourseModal"
{...this.props}
moduleName="帖子"
seletedMessageIds={[messageId]}
></SendToCourseModal>
<div className="clearfix">
<div id="forum_list" className="forum_table mh650">
<div className="padding30 bor-bottom-greyE" style={{paddingBottom: '20px'}}>
<div className="font-16 cdefault clearfix pr pr35">
<span className="noteDetailTitle">{memo.subject}</span>
{ !!memo.sticky && <span className="btn-cir btn-cir-red ml10"
style={{position: 'relative', bottom: '4px'}}>置顶</span>}
{ !!memo.reward && <span className="color-orange font-14 ml15"
data-tip-down={`获得平台奖励金币:${memo.reward}`} >
<i className="iconfont icon-gift mr5"></i>{memo.reward}
</span> }
{/* || current_user.user_id === author_info.user_id */}
{ current_user && (isAdmin || isCurrentUserTheAuthor) &&
<div className="edu-position-hidebox" style={{position: 'absolute', right: '2px',top:'4px'}}>
<a href="javascript:void(0);"><i className="fa fa-bars font-16"></i></a>
<ul className="edu-position-hide undis">
{ ( isCurrentUserTheAuthor || isAdmin ) &&
<li><a
onClick={() => this.props.toEditPage( Object.assign({}, this.props.match.params, {'coursesId': this.state.memo.course_id}) ) }
>&nbsp;&nbsp;</a></li>}
{ isAdmin &&
( memo.sticky == true ?
<li><a href="javascript:void(0);" onClick={() => this.setTop(memo)}>取消置顶</a></li>
:
<li><a href="javascript:void(0);" onClick={() => this.setTop(memo)}>&nbsp;&nbsp;</a></li> )
}
{ isAdmin &&
<li><a href="javascript:void(0);" onClick={() => this.refs.sendToCourseModal.setVisible(true)}>&nbsp;&nbsp;</a></li>
}
{ ( isCurrentUserTheAuthor || isAdmin ) && <li>
<a href="javascript:void(0)" onClick={() =>
window.delete_confirm_box_2_react(`onMemoDelete`, '您确定要删除吗?' , memo)}>
&nbsp;&nbsp;</a>
</li>
}
</ul>
</div>
}
</div>
<div className="df mt20">
<img src={setImagesUrl(`/images/${author && author.image_url}`)} className="radius mr10 mt2" width="40px" height="40px"/>
<div className="flex1">
<div className="color-grey-9 lineh-20">
<span class="color-grey-3 mr20 fl" style={{"fontWeight":"400"}}>{author && author.name}</span>
<span className="fl">{moment(memo.created_on).fromNow()} 发布</span>
</div>
<div className="color-grey-9 clearfix">
<span className="fl" style={{marginTop: '4px'}}>
{/* { current_user.admin && <Tooltip title={ "帖子奖励" }>
<span className="noteDetailNum rightline cdefault" style={{padding: '0 4px', cursor: 'pointer'}}>
<i className="iconfont icon-jiangli mr5" onClick={this.showRewardDialog}></i>
</span>
</Tooltip> } */}
<Tooltip title={"浏览数"}>
<span className={`noteDetailNum `} style={{paddingLeft: '0px'}}>
<i className="iconfont icon-liulanyan mr5"></i>
<span style={{ top: "1px", position: "relative" }}>{memo.visits || '1'}</span>
</span>
</Tooltip>
{ !!memo.total_replies_count &&
<Tooltip title={"回复数"}>
<a href="javascript:void(0)" className="noteDetailNum">
<i className="iconfont icon-huifu1 mr5" onClick={this.showCommentInput}></i>
<span style={{ top: "2px", position: "relative" }}>{ memo.total_replies_count }</span>
</a>
</Tooltip>
}
{!!memo.total_praises_count &&
<Tooltip title={"点赞数"}>
<span className={`noteDetailNum `} style={{}}>
<i className="iconfont icon-dianzan-xian mr5"></i>
<span style={{ top: "2px", position: "relative" }}>{ memo.total_praises_count }</span>
</span>
</Tooltip>
}
</span>
<div className="fr">
{/* || current_user.user_id === author_info.user_id */}
<a className={`task-hide fr return_btn color-grey-6 ${ current_user && (isAdmin
) ? '': 'no_mr'} `} onClick={() => this.props.toListPage(Object.assign({}, this.props.match.params, {'coursesId': this.state.memo.course_id})) } >
返回
</a>
</div>
</div>
</div>
</div>
</div>
<div className="padding30 memoContent new_li" style={{ paddingBottom: '10px'}}>
{/* <MarkdownToHtml content={memo.content}></MarkdownToHtml> */}
{memo.is_md == true ?
<MarkdownToHtml content={memo.content}></MarkdownToHtml>
:
<div dangerouslySetInnerHTML={{ __html: memo.content }}></div>
}
</div>
<div className="padding30 bor-bottom-greyE" style={{paddingTop: '2px'}}>
<div className="mt10 mb20">
{/* ${memo.user_praise ? '' : ''} */}
<Tooltip title={`${memo.liked ? '取消点赞' : '点赞'}`}>
<p className={`noteDetailPoint ${memo.user_praise ? 'Pointed' : ''}`} onClick={()=>{this.clickPraise()}} >
<i className="iconfont icon-dianzan"></i><br/>
<span>{memo.praises_count}</span>
</p>
</Tooltip>
</div>
{ memo.attachments && !!memo.attachments.length &&
<div>
{this.renderAttachment()}
</div>
}
</div>
{!isCourseEnd && <MemoDetailMDEditor ref="editor" memo={memo} usingMockInput={true} placeholder="说点什么"
{...this.props}
height={160} showError={true} buttonText={'发表'} className={comments && comments.length && 'borderBottom'}></MemoDetailMDEditor>}
{/* onClick={ this.createNewComment }
enableReplyTo={true}
*/}
<div className="padding20 memoReplies commentsDelegateParent comments_hideSecondReplyUserHeader"
style={{ display: (comments && !!comments.length) ? 'block' : 'none', paddingBottom: '0px' }}>
<div className="replies_count commentstypetop">
{this.props.isAdmin()===true? <span className="commentstypebutton font-16 ">
{this.state.commentstype===false?<Button type="primary" onClick={()=>this.startcomments()}>开启刷新评论</Button>
:<Button type="danger" onClick={()=>this.clearcomments()}>停止刷新</Button>}
</span>:""}
<span className="labal font-16">全部回复</span>
<span className="count font-16">({memo.total_replies_count})</span>
</div>
<Comments comments={comments} user={current_user}
replyComment={this.replyComment}
deleteComment={this.deleteComment}
commentPraise={this.commentPraise}
rewardCode={this.rewardCode}
hiddenComment={this.hiddenComment}
buttonText={'发表'}
usingAntdModal={true}
isChildCommentPagination={true}
loadMoreChildComments={this.loadMoreChildComments}
initReply={this.initReply}
showRewardButton={false}
showReply={!isCourseEnd}
onlySuperAdminCouldHide={true}
></Comments>
{/* { true ? :
<div className="memoMore">
<div className="writeCommentBtn" onClick={this.showCommentInput}>写评论</div>
</div>} */}
</div>
<div className="memoMore" style={{'margin-top': '20px'}}>
{ total_count > REPLY_PAGE_COUNT &&
<Pagination showQuickJumper onChange={this.onPaginationChange} current={pageCount} total={total_count} pageSize={10}/>
}
{!isCourseEnd && <div className="writeCommentBtn" onClick={this.showCommentInput}>写评论</div>}
</div>
</div>
</div>
</div>
);
}
}
export default ImageLayerOfCommentHOC() ( RouteHOC()(TopicDetail) );
@@ -0,0 +1,46 @@
/*必填*/
/*
.ant-form-item-required::before {
display: none;
}
.ant-form-item-control-wrapper.ant-col-xs-24.ant-col-sm-24::before {
display: inline-block;
margin-right: 8px;
color: #f5222d;
font-size: 14px;
font-family: SimSun, sans-serif;
line-height: 1;
content: '*';
}*/
/*
.ant-form-item-control {
display: inline-block;
}*/
/* form 样式 --------------------------------------------- */
.topicTitle {
margin-bottom: 8px;
}
.topicTitle .ant-form-item-control-wrapper {
width: 100%;
}
.ant-pagination.coursePagination {
text-align: center;
margin-top: 16px;
}
.setemail{
margin-left: 20px;
}
.setemail .ant-checkbox-wrapper{
margin-top: 8px;
}
@@ -0,0 +1,9 @@
.directory_style {
position: absolute;
right: 0;
bottom: 0;
maxSize: 20px;
overflow: hidden;
whiteSpace: nowrap;
textOverflow: ellipsis
}
@@ -0,0 +1,66 @@
import React, { Component } from 'react';
export function RouteHOC(options = {}) {
return function wrap(WrappedComponent) {
return class Wrapper extends Component {
constructor(props) {
super(props);
this.state = {
}
}
toDetailPage = (_courseId, boardId, topicId) => {
if (typeof _courseId == "object") {
const topicId = _courseId.topicId
const boardId = _courseId.boardId
const courseId = _courseId.coursesId
this.props.history.push(`/courses/${courseId}/boards/${boardId}/messages/${topicId}`)
} else {
this.props.history.push(`/courses/${_courseId}/boards/${boardId}/messages/${topicId}`)
}
}
toEditPage = (_courseId, boardId, topicId) => {
if (typeof _courseId == "object") {
const topicId = _courseId.topicId
const boardId = _courseId.boardId
const courseId = _courseId.coursesId
this.props.history.push(`/courses/${courseId}/boards/${boardId}/messages/${topicId}/edit`)
} else {
this.props.history.push(`/courses/${_courseId}/boards/${boardId}/messages/${topicId}/edit`)
}
}
toNewPage = (courseId, boardId) => {
this.props.history.push(`/courses/${courseId}/boards/${boardId}/messages/new`)
}
toListPage = (_courseId, boardId) => {
if (typeof _courseId == "object") {
const boardId = _courseId.boardId
const courseId = _courseId.coursesId
this.props.history.push(`/courses/${courseId}/boards/${boardId}`)
} else {
this.props.history.push(`/courses/${_courseId}/boards${boardId ? '/' + boardId : ''}`)
}
}
render() {
const { snackbarOpen} = this.state;
return (
<React.Fragment>
<WrappedComponent {...this.props}
toDetailPage={this.toDetailPage}
toEditPage={this.toEditPage}
toNewPage={this.toNewPage}
toListPage={this.toListPage}
>
</WrappedComponent>
</React.Fragment>
)
}
}
}
}
@@ -0,0 +1,466 @@
import React,{ Component } from "react";
import { Input, Checkbox, Table, Tooltip, Pagination,Spin } from "antd";
import CourseLayoutcomponent from '../common/CourseLayoutComponent'
import BoardsListItem from './BoardsListItem'
import Titlesearchsection from '../common/titleSearch/TitleSearchSection'
import ColorCountText from '../common/titleSearch/ColorCountText'
import SendToCourseModal from '../coursesPublic/modal/SendToCourseModal'
import NoneData from "../coursesPublic/NoneData"
import axios from 'axios'
import _ from 'lodash'
import './board.css'
import '../css/members.css'
import { RouteHOC } from './common.js'
import { on, off, trigger, WordsBtn } from 'educoder'
class Boards extends Component{
constructor(props){
super(props);
this.state = {
searchValue: '',
checkAllValue: false,
checkBoxValues: [],
pagination: {
page: 1,
page_size: 15,
total_count: 0
},
messages: [
],
sort_type: 'time',
boardid:undefined,
isSpin:false
}
}
fetchAll = (searchText, page) => {
this.setState({
isSpin:true
})
const _serachText = searchText || this.state.searchValue
const _page = page || this.state.pagination.page
const cid = this.props.match.params.coursesId
const bid = this.props.match.params.boardId
//
// hot
const sort_type = this.state.sort_type
// page_size
const url = `/boards/${bid}/messages.json?page_size=15&search=${_serachText || ''}&page=${_page}&sort=0&sort_type=${sort_type}`
axios.get(encodeURI(url)).then((response) => {
if (response.data.status == 0 && response.data.data) {
let _newBoards = response.data.data.messages
// if (_page > 1) {
// _newBoards = this.state.boards.slice().concat(_newBoards)
// }
this.setState({
messages: _newBoards,
boardName: response.data.data.name,
boardid:response.data.data.id,
parent_id: response.data.data.parent_id,
pagination: response.data.pagination || Object.assign({}, this.state.pagination
, { page: _page, total_count: response.data.data.total_count }),
isSpin:false
})
}
})
.catch(function (error) {
console.log(error);
this.setState({
isSpin:false
})
});
}
fetchBoards = () => {
const boardId = this.props.match.params.boardId
const boardsUrl = `/courses/board_list.json?board_id=${boardId}`
axios.get(boardsUrl, { })
.then((response) => {
if (response.data.status == 0) {
this.setState({
boards: response.data.data.boards,
course_id: response.data.data.course_id
})
}
})
.catch(function (error) {
console.log(error);
});
}
componentDidMount = () => {
this.setState({
isSpin:true
})
this.fetchBoards()
this.fetchAll()
on('updateNavSuccess', this.updateNavSuccess)
}
componentWillUnmount() {
off('updateNavSuccess', this.updateNavSuccess)
}
updateNavSuccess = () => {
this.fetchBoards()
if (this.props.match.params.boardId == this.state.boardid) {
this.fetchAll()
}
}
componentDidUpdate = (prevProps) => {
if ( prevProps.match.params.boardId != this.props.match.params.boardId ) {
this.setState({
isSpin:true
})
this.clearAllCheck()
this.fetchAll(null, 1)
}
}
moveTo = (board) => {
const len = this.state.checkBoxValues.length
if (len == 0) {
this.props.showNotification('请先在列表中选择要移动的帖子')
return;
}
const bid = this.props.match.params.boardId
const checkBoxValues = this.state.checkBoxValues
const url = `/boards/${bid}/messages/bulk_move.json`
axios.put(url, {
ids: checkBoxValues,
to_board_id: board.id
})
.then((response) => {
if (response.data.status == 0) {
console.log('--- 成功')
this.props.showNotification('帖子移动成功')
this.props.updataleftNavfun()
// TODO 停留在当前board 或 目标board
this.clearAllCheck()
this.fetchAll()
}
})
.catch(function (error) {
console.log(error);
});
}
onDelete = () => {
const len = this.state.checkBoxValues.length
if (len == 0) {
this.props.showNotification('请先选择要删除的帖子')
return;
}
this.props.confirm({
// content: `确认要删除所选的${len}个帖子吗?`,
content: `是否确认删除?`,
onOk: () => {
// const cid = this.props.match.params.coursesId
const bid = this.props.match.params.boardId
const url = `/boards/${bid}/messages/bulk_delete.json`
const board_id = 5897
axios.delete(url, { data: {
// course_id: cid,
ids: this.state.checkBoxValues
}})
.then((response) => {
if (response.data.status == 0) {
// {"status":1,"message":"删除成功"}
console.log('--- 删除成功')
this.clearAllCheck()
this.fetchAll(null, 1)
this.props.updataleftNavfun()
}
})
.catch(function (error) {
console.log(error);
});
}
})
}
onSticky = (message) => {
const cid = this.props.match.params.coursesId
const url = `/messages/${message.id}/sticky_top.json`
axios.put(url, {
course_id: cid,
})
.then((response) => {
if (response.data.status == 0) {
console.log('--- 置顶/取消置顶成功')
// const boards = this.state.boards;
// for (let _i = 0; _i < boards.length; _i++) {
// boards[]
// }
this.fetchAll()
}
})
.catch(function (error) {
console.log(error);
});
}
onItemClick = (item) => {
const checkBoxValues = this.state.checkBoxValues.slice(0);
const index = checkBoxValues.indexOf(item.id);
if (index != -1) {
_.remove(checkBoxValues, (listItem)=> listItem === item.id)
} else {
checkBoxValues.push(item.id)
}
this.onCheckBoxChange(checkBoxValues)
}
onPressEnter = (e) => {
clearTimeout(this.timeoutHandler)
this.timeoutHandler = null;
this.fetchAll(this.state.searchValue, 1)
}
onInputSearchChange = (e) => {
this.setState({
searchValue:e.target.value
})
if (this.timeoutHandler) {
clearTimeout(this.timeoutHandler)
this.timeoutHandler = null;
}
this.timeoutHandler = setTimeout(() => {
this.fetchAll(this.state.searchValue, 1)
}, 1200)
}
addDir = () => {
let {boardid}=this.state;
trigger('boardAdd', parseInt(boardid))
}
renameDir = () => {
const boardId = this.props.match.params.boardId
trigger('boardRename', { category_id: parseInt(boardId), category_name: this.state.boardName})
}
onToBoardsNew = () => {
const courseId = this.state.course_id
const boardId = this.props.match.params.boardId
this.props.toNewPage(courseId, boardId)
}
onCheckAll = (e) => {
this.setState({
checkAllValue: e.target.checked
})
const values = this.state.messages.map(item => {
return item.id
})
if (e.target.checked) {
const concated = this.state.checkBoxValues.concat(values);
const sortedUniqed = _.uniq(concated)
this.setState({
checkBoxValues: sortedUniqed
})
} else {
this.setState({
checkBoxValues: _.difference(this.state.checkBoxValues, values)
})
}
}
clearAllCheck = () => {
this.setState({
checkBoxValues: [],
checkAllValue: false
})
}
onCheckBoxChange = (checkedValues) => {
this.setState({
checkBoxValues: checkedValues,
checkAllValue: checkedValues.length == this.state.messages.length
})
console.log('checked = ', checkedValues);
}
onPageChange = (pageNumber) => {
this.setState({
checkBoxValues:[]
})
this.fetchAll(null, pageNumber)
}
onSendToOtherCourse = () => {
const len = this.state.checkBoxValues.length
if (len == 0) {
this.props.showNotification('请先在列表中选择要发送的帖子')
return;
}
this.refs.sendToCourseModal.setVisible(true)
}
onSetToOpen = () => {
const { checkBoxValues } = this.state;
if (checkBoxValues.length == 0) {
this.props.showNotification('请先在列表中选择要公开的帖子')
return;
}
const boardId = this.props.match.params.boardId
const url = `/boards/${boardId}/messages/bulk_public.json`
axios.put(url, {
ids: checkBoxValues
})
.then((response) => {
if (response.data.status == 0) {
this.props.showNotification('操作成功')
this.fetchAll()
}
})
.catch(function (error) {
console.log(error);
});
}
onSortTypeChange = (sort_type) => {
this.setState({ sort_type }, () => {
this.fetchAll()
})
}
render(){
const isAdmin = this.props.isAdmin()
const isCourseEnd = this.props.isCourseEnd()
const isAdminOrStudent = this.props.isAdminOrStudent()
let { boardName, searchValue, boards, messages, checkBoxValues,
checkAllValue, pagination, sort_type, parent_id } = this.state;
const { coursedata } = this.props;
const bid = this.props.match.params.boardId
const haveMore = pagination.total_count - pagination.page_size * pagination.page > 0
return(
<React.Fragment >
<SendToCourseModal
ref="sendToCourseModal"
{...this.props}
moduleName="帖子"
selectedMessageIds={checkBoxValues}
></SendToCourseModal>
<Titlesearchsection
title={boardName || "帖子列表"}
searchValue={ searchValue }
onInputSearchChange={this.onInputSearchChange}
allowClearonChange={this.onInputSearchChange}
showSearchInput={messages.length >= 10}
searchPlaceholder={ '请输入帖子名称进行搜索' }
firstRowRight={
<React.Fragment>
{ !isCourseEnd && isAdmin && !parent_id && <WordsBtn style="blue" className="mr30" onClick={()=>this.addDir()}>新建目录</WordsBtn> }
{ isAdmin && !!parent_id && <WordsBtn style="blue" className="mr30" onClick={()=>this.renameDir()}>目录重命名</WordsBtn> }
{ !isCourseEnd && isAdminOrStudent && <WordsBtn style="blue" className="" onClick={()=>this.onToBoardsNew()}>我要发贴</WordsBtn> }
</React.Fragment>
}
secondRowLeft={
pagination ? <ColorCountText count={pagination.total_count} name="个帖子"></ColorCountText> : ''
}
onPressEnter={this.onPressEnter}
></Titlesearchsection>
{/* <BoardsListItem></BoardsListItem>
<FilesListItem></FilesListItem> */}
{messages&&messages.length == 0?"": isAdmin && <div className="mt20 edu-back-white padding20-30">
<div className="clearfix">
{isAdmin&&<Checkbox className="fl" onChange={this.onCheckAll} checked={checkAllValue}>已选 {checkBoxValues.length} 不支持跨页勾选</Checkbox>}
<div className="studentList_operation_ul">
{ !!isAdmin &&
<React.Fragment>
<li className="li_line"><a href="javascript:void(0)" className="color-grey-9" onClick={this.onDelete}>删除</a></li>
<li className="li_line"><a href="javascript:void(0)" className="color-grey-9" onClick={this.onSendToOtherCourse}>发送</a></li>
{ coursedata && !!coursedata.course_public && <li className="li_line"><a href="javascript:void(0)" className="color-grey-9" onClick={this.onSetToOpen}>设为公开</a></li> }
<li className="li_line drop_down">
移动到...<i className="iconfont icon-xiajiantou font-12 ml2"></i>
<ul className="drop_down_menu"
style={{"right":"0px","left":"unset", maxHeight: '318px', overflowY: 'auto', minWidth: '200px'}}>
{ boards && boards.length > 10 && <p className="drop_down_search">
<Input placeholder="搜索" value={this.state.dirSearchValue} onChange={(e) => {this.setState({dirSearchValue: e.target.value})}}/>
</p> }
{
boards && boards.filter((item)=> {
return item.id != bid && (!this.state.dirSearchValue || item.name.indexOf(this.state.dirSearchValue) != -1)
}).map( (item, index) => {
return <li key={`i_${index}`} onClick={() => this.moveTo(item)} title={item.name}>{item.name}</li>
})
}
{ isAdmin && !isCourseEnd &&
<p className="drop_down_btn">
<a href="javascript:void(0)" className="color-grey-6"
onClick={()=>this.addDir()}
>新建目录...</a>
</p>
}
{/* <p className="drop_down_btn"><a href="javascript:void(0)" className="color-grey-6">添加分班...</a></p> */}
</ul>
</li>
</React.Fragment>
}
<li className="drop_down">
{sort_type == 'time' ? '时间排序' : '热度排序'}<i className="iconfont icon-xiajiantou font-12 ml2"></i>
<ul className="drop_down_normal">
<li onClick={() => this.onSortTypeChange('time')}>时间排序</li>
<li onClick={() => this.onSortTypeChange('hot')}>热度排序</li>
</ul>
</li>
</div>
</div>
</div>
}
<style>{`
.panel-inner-fourm {
border-bottom: none;
}
.panel-inner-fourm:hover {
background: #fff;
}
`}</style>
<Spin size="large" spinning={this.state.isSpin}>
<div className="clearfix stu_table">
<Checkbox.Group style={{ width: '100%' }} onChange={this.onCheckBoxChange} value={checkBoxValues}>
{ messages.map((item, index) => {
return (
<div className="mt20 edu-back-white padding02010" key={`d_${index}`}>
<BoardsListItem
{...this.props}
discussMessage={item}
checkBox={ isAdmin ? <Checkbox value={item.id} key={item.id}></Checkbox> : ''}
onItemClick={this.onItemClick}
onSticky={this.onSticky}
></BoardsListItem>
</div>
)
})
}
</Checkbox.Group>
</div>
</Spin>
{
( !this.state.isSpin && (!messages || messages.length == 0) ) && <NoneData></NoneData>
}
{/* { haveMore && <p className="edu-txt-center pt30 pb10 clearfix">
<a className={"clickmorebox"} onClick={() => this.fetchAll(null, parseInt(pagination.page) + 1)}>
<Tooltip title="点击查看更多" placement="bottom" arrowPointAtCenter={true}
>
<i className="iconfont icon-xiajiantou color-grey-9 font-16"></i>
</Tooltip>
</a>
</p> } */}
{pagination.total_count > 15 && <Pagination className="coursePagination"
current={pagination.page}
showQuickJumper pageSize={15} total={pagination.total_count} onChange={this.onPageChange} />}
</React.Fragment>
)
}
}
export default RouteHOC()(Boards);
@@ -0,0 +1,113 @@
import React,{Component} from "react";
import { Form, Select, Input, Button,Checkbox,Upload,Icon,message,Modal, Table, Divider, Tag,DatePicker,Radio,Tooltip} from "antd";
import {Link} from 'react-router-dom';
import locale from 'antd/lib/date-picker/locale/zh_CN';
import { WordsBtn, MarkdownToHtml, on, off } from 'educoder';
import axios from 'axios';
import Modals from '../../modals/Modals';
import CoursesListType from '../coursesPublic/CoursesListType';
import HomeworkModal from "../coursesPublic/HomeworkModal";
import CheckAllGroup from '../common/button/CheckAllGroup'
import moment from 'moment';
import CheckCodeModal from '../coursesPublic/modal/CheckCodeModal'
import NoneData from '../coursesPublic/NoneData'
import '../css/Courses.css'
import WorkDetailPageHeader from './common/WorkDetailPageHeader'
import TabRightComponents from './common/TabRightComponents'
const { Option} = Select;
const CheckboxGroup = Checkbox.Group;
const confirm = Modal.confirm;
let GraduationTasksnewtype=true;
const $ = window.$;
const Search = Input.Search;
const RadioGroup = Radio.Group;
class CommonWorkAnswer extends Component{
constructor(props){
super(props)
this.state={
course_name:"",
homework_name:"",
search: ''
}
}
fetchData = () => {
let workId=this.props.match.params.workId;
const url = `/homework_commons/${workId}/reference_answer.json`
axios.get(url).then((result)=> {
if (result.data.course_id) {
this.setState({
...result.data
}, () => {
})
this.props.initWorkDetailCommonState && this.props.initWorkDetailCommonState( Object.assign({...result.data}, {
moduleName: '参考答案'
}))
}
}).catch((error)=>{
console.log(error)
})
}
componentDidMount() {
this.fetchData()
try {
this.props.triggerRef(this);
}catch (e) {
}
on('commonwork_fetch_all', this.fetchAllListener)
}
componentWillUnmount() {
off('commonwork_fetch_all', this.fetchAllListener)
}
fetchAllListener = () => {
this.fetchData()
}
render(){
const dateFormat = 'YYYY-MM-DD HH:mm';
let {course_name, homework_name, search, page, loadingstate, homework_status, reference_answer,
attachments
} =this.state;
let courseId=this.props.match.params.coursesId;
let category_id=this.props.match.params.category_id;
let workId=this.props.match.params.workId;
const exportResultUrl = `/api/homework_commons/${workId}/works_list.xlsx`
return(
<React.Fragment
>
{/* 内容区 */}
<div className="padding40 memoContent new_li">
{reference_answer ? <MarkdownToHtml content={reference_answer || "还未设置答案。"}></MarkdownToHtml> :
reference_answer === undefined ? '' : ((!attachments || attachments.length == 0) && <NoneData></NoneData>)}
{ attachments && attachments.map((item) => {
return ( <div className="color-grey">
<a className="color-grey">
<i className="font-14 color-green iconfont icon-fujian mr8" aria-hidden="true"></i>
</a>
<a href={item.url} className="mr12" length="58">{item.title}</a>
<span className="color-grey mt2 color-grey-6 font-12">{item.filesize}</span>
</div> )
}) }
</div>
</React.Fragment>
)
}
}
export default CommonWorkAnswer;
@@ -0,0 +1,284 @@
import '../katex.css';
import '../css/Courses.css';
import React,{Component} from "react";
import {markdownToHTML, ImageLayer2 } from 'educoder';
import axios from 'axios';
import Modals from '../../modals/Modals';
import moment from 'moment';
import WorkDetailPageHeader from './common/WorkDetailPageHeader'
import CommonWorkAppraiseReply from './reply/CommonWorkAppraiseReply'
import CommonWorkAppraiseReviseAttachments from './CommonWorkAppraiseReviseAttachments'
import LeaderIcon from './common/LeaderIcon';
import showdown from 'showdown'
import showdownKatex from 'showdown-katex'
class CommonWorkAppraise extends Component{
constructor(props){
super(props)
this.state={
course_name:"",
homework_name:"",
search: '',
attachments: [],
revise_attachments: [],
}
}
getWork = () => {
let workId =this.props.match.params.workId;
let studentWorkId =this.props.match.params.studentWorkId;
// https://www.showdoc.cc/127895880302646?page_id=1957586480118445
const url = `/student_works/${studentWorkId}.json`
axios.get(url).then((result)=> {
if (result.data.course_id) {
this.setState({
...result.data
})
}
}).catch((error)=>{
console.log(error)
})
}
exportMdtoHtml=(md)=> {
let newmd=md;
const converter = new showdown.Converter({
extensions: [
showdownKatex({
// maybe you want katex to throwOnError
throwOnError: true,
// disable displayMode
displayMode: false,
// change errorColor to blue
errorColor: '#1500ff',
}),
],
});
return converter.makeHtml(newmd);
}
getReviseAttachments = () => {
let studentWorkId =this.props.match.params.studentWorkId;
const url = `/student_works/${studentWorkId}/supply_attachments.json`;
axios.get(url).then((result)=> {
if (result.data.revise_attachments) {
this.setState({
...result.data
})
}
}).catch((error)=>{
console.log(error)
})
}
componentDidUpdate(prevProps, prevState) {
if (this.props.match.params.studentWorkId != prevProps.match.params.studentWorkId) {
this.getWork();
this.getReviseAttachments()
this.commonWorkAppraiseReply && this.commonWorkAppraiseReply.fetchAllComments()
}
}
componentDidMount() {
this.getWork();
this.getReviseAttachments()
}
onAttachmentRemove = (id) => {
this.setState({
Modalstype:true,
// Modalstopval:'确定要删除这个附件吗?',
Modalstopval: '是否确认删除?',
ModalSave: ()=>this.deleteAttachment(id),
ModalCancel:this.cancelAttachment
})
}
deleteAttachment = (id) => {
const url = `/attachments/${id}.json`
axios.delete(url, {
})
.then((response) => {
if (response.data) {
const { status } = response.data;
if (status == 0) {
this.getWork()
this.getReviseAttachments();
this.cancelAttachment();
}
}
})
.catch(function (error) {
console.log(error);
});
}
cancelAttachment=()=>{
this.setState({
Modalstype:false,
Modalstopval:'',
ModalSave:"",
ModalCancel:""
})
}
onReplySuccess = () => {
this.getReviseAttachments();
}
render(){
const dateFormat = 'YYYY-MM-DD HH:mm';
let {course_name, homework_name, search, page, loadingstate, homework_status, reference_answer,
attachments, homework_id, project_info, work_members, is_evaluation,
description, update_user_name, commit_user_name, update_time, commit_time, author_name,
revise_attachments, revise_reason, atta_update_user, atta_update_time, atta_update_user_login,
Modalstype,Modalstopval,ModalCancel,ModalSave,loadtype, is_leader_work
} =this.state;
let courseId=this.props.match.params.coursesId;
let category_id=this.props.match.params.category_id;
let studentWorkId=this.props.match.params.studentWorkId;
const isAdmin = this.props.isAdmin()
document.title=course_name&&course_name;
return(
<WorkDetailPageHeader
{...this.props} {...this.state}
noTab={true}
moduleName={'作业详情'}
getReviseAttachments={this.getReviseAttachments}
>
{/*提示*/}
<Modals
modalsType={Modalstype}
modalsTopval={Modalstopval}
modalCancel={ModalCancel}
modalSave={ModalSave}
loadtype={loadtype}
/>
{/* 内容区 */}
{/* <div className="padding40 memoContent new_li">
</div> */}
<div className={"appraise imageLayerParent "} style={{}}>
<style>{`
.workAppraise>div:last-child {
border-bottom: none !important;
}
`}</style>
<ImageLayer2 parentSel=".workAppraise" childSel="a.imageTarget"></ImageLayer2>
<div className={"workAppraise"}>
{(description || (attachments && attachments.length != 0)) && <div className={"stud-class-set edu-back-white padding20-30"}>
<div className={"color-grey-6 mb15 font-16"}>
内容
</div>
<div className="markdown-body ml20" dangerouslySetInnerHTML={{__html: markdownToHTML(description)}}>
</div>
{attachments.map((item,key)=>{
return(
<div className="color-grey" key={key}>
<a className="color-grey ml20">
<i className="font-14 color-green iconfont icon-fujian mr8" aria-hidden="true"></i>
</a>
<a href={item.url}
className="mr12 color9B9B imageTarget" length="58" title={`${item.title && item.title.length > 40 ? item.title : ''}`}>
{item.title}
</a>
<span className="color656565 mt2 color-grey-6 font-12 mr8">{item.filesize}</span>
{/*{item.delete===true?<i className="font-14 iconfont icon-guanbi " id={item.id} aria-hidden="true" onClick={()=>this.onAttachmentRemove(item.id)}></i>:""}*/}
{item.delete===true?<i className="font-14 iconfont icon-guanbi " id={item.id} aria-hidden="true" onClick={()=>this.onAttachmentRemove(item.id)}></i>:""}
{/* style={{display: 'none'}} */}
</div>
)
})}
<div className={"color-grey-6 h20 ml20"}>
{ update_time &&
<React.Fragment>
<span className={"color9B9B fr"}>更新</span>
<span className={"fr font-14 mr10 ml10"}>{update_user_name}</span>
<span className={"color9B9B fr"}>
{moment(update_time).format('YYYY-MM-DD HH:mm')==="Invalid date"?"":moment(update_time).format('YYYY-MM-DD HH:mm')}
</span>
</React.Fragment> }
{ commit_time &&
<React.Fragment>
<span className={"color9B9B fr mr30"}>提交</span>
<span className={"fr font-14 mr10 ml10"}>{commit_user_name}</span>
<span className={" color9B9B fr"}>
{moment(commit_time).format('YYYY-MM-DD HH:mm')==="Invalid date"?"":moment(commit_time).format('YYYY-MM-DD HH:mm')}
</span>
</React.Fragment> }
</div>
</div> }
<CommonWorkAppraiseReviseAttachments {...this.state}
onAttachmentRemove={this.onAttachmentRemove}
></CommonWorkAppraiseReviseAttachments>
{is_evaluation != true && project_info && project_info.name && <div className={"stud-class-set bor-top-greyE edu-back-white padding20-30"}>
<div className={"color-grey-6 mb10"}>
关联项目
</div>
<div className={"ml20"}>
<a className="color-blue" href={`/projects/${project_info.id}`}>{project_info.name}</a>
</div>
</div>
}
{is_evaluation != true && work_members && !!work_members.length && <div className={"stud-class-set bor-top-greyE edu-back-white padding20-30"}>
<div className={"color-grey-6 mb10"}>
全部组员
</div>
<div className={"ml20 color-grey-6"}>
<div className="">
当前组员{author_name} {is_leader_work && <LeaderIcon small={true} ></LeaderIcon>}
</div>
<div>
其他组员
{work_members.map((item, index) => {
return <React.Fragment>
{isAdmin ?
<a className={`color-blue ${index == 0 ? '' : 'ml12'}`} href="javascript:void(0)"
onClick={() => this.props.toWorkDetailPage(this.props.match.params, null, item.work_id)}
>
{item.user_name}
</a> : <span className={`${index == 0 ? '' : 'ml12'}`} >{item.user_name}</span>}
{item.is_leader && <LeaderIcon small={true} ></LeaderIcon>}
</React.Fragment>
})}
</div>
{isAdmin && <div className="font-12 color-grey-9">
<span >温馨提示</span>
点击其他组员的姓名可以快速评阅TA的作品
</div>}
</div>
</div>
}
</div>
{/* task_type={datalist&&datalist.task_type} */}
<CommonWorkAppraiseReply {...this.props} task_id={studentWorkId}
onReplySuccess={this.onReplySuccess} {...this.state}
wrappedComponentRef={(ref) => {this.commonWorkAppraiseReply = ref}}
></CommonWorkAppraiseReply>
</div>
</WorkDetailPageHeader>
)
}
}
export default CommonWorkAppraise;
@@ -0,0 +1,52 @@
import React, { useState } from 'react'
import moment from 'moment'
// import Example from './TestHooks'
function CommonWorkAppraiseReviseAttachments(props) {
const { revise_attachments, revise_reason, atta_update_time, atta_update_user} = props
if (!revise_attachments) return ''
return (
<React.Fragment>
{/* {Example()} */}
{revise_attachments.length===0?"":<div className={"stud-class-set bor-top-greyE padding20-30 edu-back-white"}>
<style>{`
.color-grey:hover i {
display: inline !important;
}
`}</style>
<div className={"color-grey-6 mb10 font-16"}>
补交附件
</div>
{/* {age} */}
<div className={"ml20"}>
{revise_reason}
</div>
{revise_attachments.map((item,key)=>{
return(
<div className="color-grey" key={key}>
<a className="color-grey ml20">
<i className="font-14 color-green iconfont icon-fujian mr8" aria-hidden="true"></i>
</a>
<a href={item.url}
className="mr12 color9B9B imageTarget" length="58">
{item.title}
</a>
<span className="color656565 mt2 color-grey-6 font-12 mr8">{item.filesize}</span>
{item.delete===true?<i className="font-14 iconfont icon-guanbi " style={{display: 'none'}} id={item.id} aria-hidden="true" onClick={()=>props.onAttachmentRemove(item.id)}></i>:""}
</div>
)
})}
<div className={"color-grey-6 clearfix lineh-25 ml20"}>
<span className={"color9B9B fr"}>更新</span>
<span className={"fr font-13 mr10 ml10"}>{atta_update_user}</span>
<span className={"color9B9B fr"}>
{moment(atta_update_time).format('YYYY-MM-DD HH:mm')==="Invalid date"?"":moment(atta_update_time).format('YYYY-MM-DD HH:mm')}
</span>
</div>
</div>}
</React.Fragment>
)
}
export default CommonWorkAppraiseReviseAttachments;
@@ -0,0 +1,482 @@
import React, { Component } from 'react';
import { getRandomNumber } from 'educoder';
import {Link} from 'react-router-dom';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import Loadable from 'react-loadable';
import Loading from '../../../Loading';
import { WordsBtn, MarkdownToHtml, trigger, queryString, getRandomcode } from 'educoder';
import axios from 'axios';
import CoursesListType from '../coursesPublic/CoursesListType';
import AccessoryModal from "../coursesPublic/AccessoryModal";
import PublishRightnow from './PublishRightnow'
import '../css/Courses.css'
import CBreadcrumb from '../common/CBreadcrumb'
import DownloadMessageysl from "../../modals/DownloadMessageysl";
import ConnectProject from './ConnectProject'
import { Spin } from 'antd'
//引入对应跳转的组件
//新建分组/普通作业
const NewWork = Loadable({
loader: () => import('./NewWork'),
loading:Loading,
})
const CommonWorkSetting = Loadable({
loader: () => import('./CommonWorkSetting'),
loading:Loading,
})
//普通作业列表
const CommonWorkList = Loadable({
loader: () => import('./CommonWorkList'),
loading:Loading,
})
const CommonWorkQuestion = Loadable({
loader: () => import('./CommonWorkQuestion'),
loading:Loading,
})
const CommonWorkAnswer = Loadable({
loader: () => import('./CommonWorkAnswer'),
loading:Loading,
})
const CommonWorkAppraise = Loadable({
loader: () => import('./CommonWorkAppraise'),
loading:Loading,
})
const CommonWorkPost = Loadable({
loader: () => import('./CommonWorkPost'),
loading:Loading,
})
const CommonWork = Loadable({
loader: () => import('./commonWork'),
loading:Loading,
})
class CommonWorkDetailIndex extends Component{
constructor(props) {
super(props)
this.publishModal = React.createRef();
this.endModal = React.createRef();
this.state = {
DownloadType:false,
DownloadMessageval:undefined,
donwloading:false,
}
}
initWorkDetailCommonState = (data) => {
this.setState({
...data
})
}
goback = () => {
let workId=this.props.match.params.workId;
//
if ( window.location.pathname.indexOf('appraise') == -1) {
let category_id= this.state.category.category_id;
this.props.toListPage(this.props.match.params, category_id)
} else {
this.props.toWorkListPage(this.props.match.params, workId)
}
// this.props.history.goBack()
}
// 补交附件
Cancelvisible=()=>{
this.setState({
accessoryVisible:false
})
}
addAccessory=()=>{
this.setState({
accessoryVisible:true
})
}
setupdate = () => {
}
doWhenSuccess = () => {
trigger('commonwork_fetch_all')
}
/// 确认是否下载
confirmysl(url){
axios.get(url+ '&export=true').then((response) => {
if(response===undefined){
return
}
if(response.data.status&&response.data.status===-1){
}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
})
}
}else {
this.props.slowDownload(getRandomcode(url))
// this.props.showNotification(`正在下载中`);
// this.setState({ donwloading: true })
// downloadFile({
// url: url,
// successCallback: (url) => {
// this.setState({ donwloading: false })
// console.log('successCallback')
// },
// failCallback: (responseHtml, url) => {
// this.setState({ donwloading: false })
// console.log('failCallback')
// }
// })
// window.open("/api"+url, '_blank');
}
}).catch((error) => {
console.log(error)
});
}
Downloadcal=()=>{
this.setState({
DownloadType:false,
DownloadMessageval:undefined
})
}
bindRef = ref => { this.child = ref };
// 关联项目
openConnectionProject = (work) => {
this.refs['connectProject'].openConnectionProject(work)
}
connectSuccess = () => {
this.child.fetchData && this.child.fetchData()
}
cancelConnectionProject = (work) => {
let workId=this.props.match.params.workId;
let courseId=this.props.match.params.coursesId;
const url = `/homework_commons/${work.homework_id}/student_works/cancel_relate_project.json`
axios.get(url).then((response)=> {
if (response.data.status == 0) {
this.child.fetchData && this.child.fetchData()
this.props.showNotification('取消关联成功')
}
}).catch((error)=>{
console.log(error)
})
}
// 关联项目 END
render() {
let {course_name, homework_name, homework_status, noTab
, view_answer, author_name, category, work_id
, end_immediately, publish_immediately, work_statuses, accessoryVisible
} =this.state;
const { current_user } = this.props
let courseId=this.props.match.params.coursesId;
let category_id= category && category.category_id;
let category_name= category && category.category_name;
let workId=this.props.match.params.workId;
const studentWorkId = this.props.match.params.studentWorkId
const isGroup = this.props.isGroup()
const moduleName = !isGroup? "普通作业":"分组作业";
const moduleEngName = this.props.getModuleName()
const childModuleName = this.state.moduleName
const commonHandler = {
initWorkDetailCommonState: this.initWorkDetailCommonState,
triggerRef: this.bindRef
}
const isAdmin = this.props.isAdmin()
let exportParams = {}
const isListModule = childModuleName == '作品列表';
// 是列表页
let params = {}
if (isListModule) {
// TODO
if(this.child && this.child._getRequestParams) {
params = this.child._getRequestParams() !== undefined ? this.child._getRequestParams() : {};
}
}
// console.log("普通作业176176176");
// console.log(params);
let exportUrl = `/homework_commons/${workId}/works_list.zip?${queryString.stringify(params)}`
let exportResultUrl = `/homework_commons/${workId}/works_list.xlsx?${queryString.stringify(params)}`
document.title=course_name === undefined ? "" : course_name;
return (
<div>
<PublishRightnow ref={this.publishModal} showActionButton={false} {...this.props} checkBoxValues={[workId]}
isPublish={true} doWhenSuccess={this.doWhenSuccess} checkBeforePost={this.saveWorkSetting}
onToPublishClick={this.onToPublishClick}
></PublishRightnow>
<DownloadMessageysl
{...this.props}
value={this.state.DownloadMessageval}
modalCancel={this.Downloadcal}
modalsType={this.state.DownloadType}
/>
<PublishRightnow ref={this.endModal} showActionButton={false} {...this.props} checkBoxValues={[workId]}
isPublish={false} doWhenSuccess={this.doWhenSuccess}></PublishRightnow>
<div className="newMain clearfix worklist1">
<div className={"educontent mt20"}>
<style>{`
.memoContent {
background: #fff;
}
`}</style>
{current_user && <CBreadcrumb items={[
{ to: current_user&&current_user.first_category_url , name: course_name},
{ to: `/courses/${courseId}/${moduleEngName}/${category_id}`, name: category_name },
window.location.pathname.indexOf('appraise') == -1 ? { } : { to: `/courses/${courseId}/${moduleEngName}/${workId}/list`, name: '作业详情' },
// 1. 与上一条联动,当匿评他人作品时,TA人作品的作者真实姓名切换为“匿名”
window.location.pathname.indexOf('appraise') == -1 ? { name: '作业详情' } : { name: author_name },
// window.location.pathname.indexOf('appraise') == -1 ? { } : { to: `/courses/${courseId}/${moduleEngName}/${workId}/list`, name: '作品列表' },
// { name: childModuleName }
]}></CBreadcrumb>}
<div style={{ width:'100%',height:'52px'}} >
<span className=" fl color-black summaryname" title={`${homework_name && homework_name.length > 36 ? homework_name : ''}`}
style={{height: 'auto'}}
>
{homework_name}
{/* <Link to={"/courses/"+courseId+"/graduation"+"/graduation_tasks/"}>{homework_name}</Link> */}
</span>
<CoursesListType
typelist={homework_status}
typesylename={"mt12"}
/>
{category && <a className="color-grey-6 fr font-16 ml30 mt5 mr20" onClick={this.goback} style={{ marginRight: '26px'}}>返回</a>}
{this.state.update_atta &&
<React.Fragment>
{this.state.accessoryVisible===true? <AccessoryModal
{...this.props}
modalname={"补交附件"}
visible={this.state.accessoryVisible}
Cancelname={"取消"}
Savesname={"确认"}
Cancel={this.Cancelvisible}
setupdate={this.setupdate}
seeworks={undefined}
reviseAttachmentUrl={`/student_works/${work_id || studentWorkId}/revise_attachment.json`}
/>:""}
<a className={"fr color-blue font-16 "} href={"javascript:void(0)"}
onClick={this.addAccessory} style={{ 'marginTop': '-4px' }}
>补交附件</a>
</React.Fragment>
}
</div>
{ noTab !== true && <div className="stud-class-set bor-bottom-greyE floatSpinParent">
<div className="mt10 clearfix edu-back-white poll_list pl5">
<Link
onClick={() => this.setState({moduleName: '作品列表'})}
className={`${isListModule ? 'active' : '' } `}
to={`/courses/${courseId}/${moduleEngName}/${workId}/list`}>作品列表</Link>
<Link
onClick={() => this.setState({moduleName: '作业描述'})}
className={`${childModuleName == '作业描述' ? 'active' : '' } `}
to={`/courses/${courseId}/${moduleEngName}/${workId}/question`}>作业描述</Link>
{view_answer == true && <Link
onClick={() => this.setState({moduleName: '参考答案'})}
className={`${childModuleName == '参考答案' ? 'active' : '' } `}
to={`/courses/${courseId}/${moduleEngName}/${workId}/answer`}>参考答案</Link>}
<Link
onClick={() => this.setState({moduleName: '设置'})}
className={`${childModuleName == '设置' ? 'active' : '' } `}
style={{paddingLeft:this.props.isAdmin()?'38px':'20px'}}
to={`/courses/${courseId}/${moduleEngName}/${workId}/setting`}>{this.props.isAdmin()?"设置":"得分规则"}</Link>
{/* { this.props.tabRightComponents } */}
<React.Fragment>
<style>{`
.drop_down_menu li a {
padding: 0px;
font-size: 14px;
}
.drop_down_menu {
width: 121px;
}
.drop_down_menu li {
overflow: visible;
width: 121px;
}
.drop_down_menu, .drop_down_normal {
padding-top: 10px;
padding-bottom: 8px;
}
.floatSpinParent .ant-spin-nested-loading {
float: right;
}
`}</style>
{this.props.isAdmin()? <Spin spinning={this.state.donwloading} style={{ }}>
<li className="li_line drop_down fr color-blue font-16 mt20" style={{"padding":"0 20px"}}>
导出<i className="iconfont icon-xiajiantou font-12 ml2"></i>
<ul className="drop_down_menu" style={{"right":"-34px","left":"unset","height":"auto"}}>
<li>
<a href={"javascript:void(0)"} className="color-dark"
onClick={() => this.confirmysl(exportResultUrl)}
>导出成绩</a>
</li>
<li>
<a href={"javascript:void(0)"} className="color-dark"
onClick={() => this.confirmysl(exportUrl)}
>导出作品附件</a>
</li>
</ul>
</li>
</Spin>:""}
{/* {isAdmin && <a className={"fr color-blue font-16"} href={exportUrl}>导出作品附件</a>}
{isAdmin && <a className={"fr color-blue font-16"} href={exportResultUrl}>导出成绩</a>} */}
{/*<a className={"fr color-blue font-16"}>导出</a>*/}
{end_immediately && <a className={"fr color-blue font-16"} onClick={() => { this.endModal.current.open() } }>立即截止</a>}
{publish_immediately && <a className={"fr color-blue font-16"} onClick={() => { this.publishModal.current.open() } } >立即发布</a>}
{/*<a className={"fr color-blue font-16"}>项目在线质量检测</a>*/}
{isAdmin && <a className={"fr color-blue font-16"} onClick={() => this.props.toEditPage(this.props.match.params, workId)}>编辑作业</a>}
{ //
work_statuses && work_statuses.indexOf('关联项目') != -1 &&
<React.Fragment>
<ConnectProject ref="connectProject" {...this.props} connectSuccess={this.connectSuccess}></ConnectProject>
<WordsBtn style="blue" className={` font-16 fr`} onClick={() => this.openConnectionProject({ homework_id: workId })}>关联项目</WordsBtn>
<WordsBtn style="blue" className={` font-16 fr`} onClick={() => this.props.toCreateProject()}>创建项目</WordsBtn>
</React.Fragment>
}
{ //
work_statuses && work_statuses.indexOf('取消关联') != -1 &&
<WordsBtn style="blue" className={` font-16 fr`} onClick={() => this.cancelConnectionProject( {homework_id: workId} )}>取消关联</WordsBtn>
}
{work_statuses && work_statuses.indexOf('提交作品') != -1 && <a className={"fr color-blue font-16"} href={"javascript:void(0)"}
onClick={() => { this.props.toWorkPostPage(this.props.match.params)}}
>提交作品</a>}
{ work_statuses && work_statuses.indexOf('补交作品') != -1 &&
<a className={"fr color-blue font-16"} href={"javascript:void(0)"}
onClick={() => { this.props.toWorkPostPage(this.props.match.params)}}>
补交作品</a> }
{work_statuses && work_statuses.indexOf('修改作品') != -1 && <a className={"fr color-blue font-16"} href={"javascript:void(0)"}
onClick={() => { this.props.toWorkPostPage(this.props.match.params, null, true, work_id)}}
>修改作品</a>}
{work_statuses && work_statuses.indexOf('补交附件') != -1 &&
<React.Fragment>
<AccessoryModal
{...this.props}
modalname={"补交附件"}
visible={accessoryVisible}
Cancelname={"取消"}
Savesname={"确认"}
Cancel={this.Cancelvisible}
setupdate={this.setupdate}
reviseAttachmentUrl={`/student_works/${work_id}/revise_attachment.json`}
/>
<a className={"fr color-blue font-16"} href={"javascript:void(0)"}
onClick={this.addAccessory}
>补交附件</a>
</React.Fragment>
}
</React.Fragment>
</div>
</div> }
{/* 内容区 */}
<Switch {...this.props}>
{/* --------------------------------------------------------------------- */}
{/* 作业设置 */}
{/* http://localhost:3007/courses/1309/homework/9300/setting */}
<Route exact path="/courses/:coursesId/common_homeworks/:workId/setting"
render={
(props) => (<CommonWorkSetting {...this.props} {...props} {...this.state} {...commonHandler}/>)
}
></Route>
{/* 作品列表 */}
<Route exact path="/courses/:coursesId/common_homeworks/:workId/list"
render={
(props) => (<CommonWorkList ref="commonWorkList" triggerRef={this.bindRef} {...this.props} {...props} {...this.state} {...commonHandler}/>)
}
></Route>
{/* 作业问答 */}
<Route exact path="/courses/:coursesId/common_homeworks/:workId/question"
render={
(props) => (<CommonWorkQuestion {...this.props} {...props} {...this.state} {...commonHandler}/>)
}
></Route>
<Route exact path="/courses/:coursesId/common_homeworks/:workId/answer"
render={
(props) => (<CommonWorkAnswer {...this.props} {...props} {...this.state} {...commonHandler}/>)
}
></Route>
<Route exact path="/courses/:coursesId/group_homeworks/:workId/setting"
render={
(props) => (<CommonWorkSetting {...this.props} {...props} {...this.state} {...commonHandler}/>)
}
></Route>
{/* 作品列表 */}
<Route exact path="/courses/:coursesId/group_homeworks/:workId/list"
render={
(props) => (<CommonWorkList triggerRef={this.bindRef} {...this.props} {...props} {...this.state} {...commonHandler}/>)
}
></Route>
{/* 作业问答 */}
<Route exact path="/courses/:coursesId/group_homeworks/:workId/question"
render={
(props) => (<CommonWorkQuestion {...this.props} {...props} {...this.state} {...commonHandler}/>)
}
></Route>
<Route exact path="/courses/:coursesId/group_homeworks/:workId/answer"
render={
(props) => (<CommonWorkAnswer {...this.props} {...props} {...this.state} {...commonHandler}/>)
}
></Route>
</Switch>
</div>
</div>
</div>
)
}
}
export default ( CommonWorkDetailIndex) ;
@@ -0,0 +1,299 @@
import React,{ Component } from "react";
import { Input,Checkbox, Modal,Menu, Tooltip, Pagination } from "antd";
import '../css/members.css'
import '../css/busyWork.css'
import CoursesListType from '../coursesPublic/CoursesListType'
import { WordsBtn } from 'educoder'
import AccessoryModal2 from '../coursesPublic/AccessoryModal2'
import AccessoryModal from '../coursesPublic/AccessoryModal'
import axios from 'axios'
class CommonWorkItem extends Component{
constructor(props){
super(props);
this.state = {
aModalVisible: false
}
}
onItemClick = (item) => {
if(this.props.checkIfLogin()===false){
this.props.showLoginDialog()
return
}
// if(this.props.checkIfProfileCompleted()===false){
// this.setState({
// AccountProfiletype:true
// })
// return
// }
// if(this.props.checkIfProfessionalCertification()===false){
// this.props.showProfileCompleteDialog()
// return
// }
const isStudent = this.props.isStudent()
if (isStudent) {
this.props.toWorkQuestionPage(this.props.match.params, item.homework_id)
} else {
this.props.toWorkListPage(this.props.match.params, item.homework_id)
}
}
sendMoreAttachment = (item) => {
this.handleItem = item;
const url = `/student_works/${item.work_id}/supply_attachments.json`
axios.get(url, {
})
.then((response) => {
if (response.data.revise_attachments) {
// response.data.revise_attachments = [
// {
// "id": 8686,
// "title": "lasdl",
// "filesize": 12323,
// "description": "1原因3",
// "url": "/attachments/:id/download",
// "delete": true
// }
// ]
// response.data.revise_reason = 'qweqweqweqe';
// revise_reason revise_attachments 传给modal
// attach to fileList
const fileList = response.data.revise_attachments.map(item => {
return {
id: item.id,
uid: item.id,
name: item.title,
url: item.url,
status: 'done',
description: item.description,
delete: item.delete,
filesize: item.filesize,
}
})
this.setState({ aModalVisible: true, fileList, revise_reason: response.data.revise_reason })
}
})
.catch(function (error) {
console.log(error);
});
}
toProductPage = (item) => {
}
// Cancelvisible = () => {
// this.setState({ aModalVisible: false })
// }
onAccessoryModalSaves = (newfileList, description) => {
const url = `/student_works/${this.handleItem.work_id}/revise_attachment.json`
axios.post(url, {
"description": description,
"attachment_ids": newfileList
})
.then((response) => {
if (response.data.status == 0) {
this.props.showNotification('提交成功')
}
})
.catch(function (error) {
console.log(error);
});
}
// 补交附件
Cancelvisible=()=>{
this.setState({
accessoryVisible:false
})
}
addAccessory=()=>{
this.setState({
accessoryVisible:true
})
}
setupdate = () => {
}
render(){
let { mainList,workType }=this.props;
const { aModalVisible, fileList, revise_reason } = this.state
let coursesId = this.props.match.params.coursesId;
const isStudent = this.props.isStudent()
const isAdmin = this.props.isAdmin()
const isAdminOrStudent = this.props.isAdminOrStudent()
return(
<div className="workList">
{/* <AccessoryModal2
visible={aModalVisible}
Cancel={this.Cancelvisible}
Saves={this.onAccessoryModalSaves}
fileList={fileList}
description={revise_reason}
></AccessoryModal2> */}
<style>
{
`
.comnonwidth580{
max-width: 580px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
a:hover{
color: #4cacff;
}
`
}
</style>
{
mainList && mainList.homeworks && mainList.homeworks.length>0 && mainList.homeworks.map((item,index)=>{
let canNotLink = !isAdminOrStudent && item.private_icon == true
return(
<div className="mt20 edu-back-white padding02010" key={index} >
<div className="clearfix">
<div className="workList_Item">
{
mainList && isAdmin &&
<span className="fl mr12"><Checkbox value={item.homework_id} key={item.homework_id}></Checkbox></span>
}
<div className="flex1" onClick={() => this.props.onItemClick(Object.assign({}, item, {id: item.homework_id})) }>
<p className="clearfix mb20">
{canNotLink?<span className={"fl font-16 font-bd mt2 comnonwidth580 pointer color-grey-name"}
title={ canNotLink ? "私有属性,非课堂成员不能访问" : item.name}
>
{item.name}
</span>:<a className={"fl font-16 font-bd mt2 comnonwidth580 pointer"}
onClick={ canNotLink ? () => {} : () => this.onItemClick(item)}
title={item.name}
>{item.name}</a>}
{/* 只有非课堂成员且作业是私有的情况下才会为true */}
{
item.private_icon===true ?
(<Tooltip title={"私有属性,非课堂成员不能访问"} placement="bottom" >
<i className="iconfont icon-guansuo color-grey-c ml10 font-16 fl"></i>
</Tooltip>) : ""
}
<span className="fl">
<CoursesListType typelist={item.status} />
</span>
{/* {
mainList && isAdmin &&
<li className="fr drop_down">
<i className="iconfont icon-caidan font-16 color-grey-c"></i>
<ul className="drop_down_normal" style={{ textAlign: 'center'}}>
<li onClick={ () => { this.props.toEditPage(this.props.match.params, item.homework_id) }}>
<a href={`javascript:void(0)`} >编辑</a>
</li>
<li onClick={ () => { this.props.toWorkSettingPage(this.props.match.params, item.homework_id) }}>
<a href={`javascript:void(0)`}
>设置</a>
</li>
</ul>
</li>
} */}
</p>
<p className="color-grey-9 clearfix">
{ item.author && <span className="mr20 fl">{item.author}</span> }
{item.commit_count===undefined?"":<span className="mr20 fl">{item.commit_count} 已交</span>}
{item.uncommit_count===undefined?"":<span className="mr20 fl">{item.uncommit_count} 未交</span>}
{
item.status_time!="" &&
<span className="mr20 fl">{item.status_time}</span>
}
{/*<Tooltip placement="bottom" title={ item.status.indexOf('提交中') != -1 ? '提交剩余时间' :*/}
{/* item.status.indexOf('补交中') != -1 ? '补交剩余时间' :*/}
{/* item.status.indexOf('申诉中') != -1 ? '申诉剩余时间' :*/}
{/* item.status.indexOf('匿评中') != -1 ? '匿评剩余时间' :*/}
{/* item.status.indexOf('匿评申诉中') != -1 ? '匿评申诉剩余时间' : ''}>*/}
{/*</Tooltip>*/}
{isAdmin && <div className="fr">
<WordsBtn style="blue" className={"fl font-16 ml28"}
onClick={ canNotLink ? () => {} : () => this.onItemClick(item)}
>查看详情</WordsBtn>
<WordsBtn style="blue" className="fl font-16 ml28" onClick={ () => { this.props.toEditPage(this.props.match.params, item.homework_id) }}>编辑</WordsBtn>
<WordsBtn style="blue" className="fl font-16 ml28" onClick={ () => { this.props.toWorkSettingPage(this.props.match.params, item.homework_id) }}>设置</WordsBtn>
</div>}
{
//
isStudent &&
<li className="fr">
<WordsBtn style="blue" className={"fl font-16"}
onClick={ canNotLink ? () => {} : () => this.onItemClick(item)}
>查看详情</WordsBtn>
{ //
item.work_status && item.work_status.indexOf('关联项目') != -1 &&
<React.Fragment>
<WordsBtn style="blue" className={` font-16 fl ml28`} onClick={() => this.props.toCreateProject(item)}>创建项目</WordsBtn>
<WordsBtn style="blue" className={` font-16 fl ml28`} onClick={() => this.props.openConnectionProject(item)}>关联项目</WordsBtn>
</React.Fragment>
}
{ //
item.work_status && item.work_status.indexOf('取消关联') != -1 &&
<WordsBtn style="blue" className={` font-16 fl ml28`} onClick={() => this.props.cancelConnectionProject(item)}>取消关联</WordsBtn>
}
{ //
item.work_status && item.work_status.indexOf('提交作品') != -1 &&
<WordsBtn style="blue" className="fl font-16 ml28" onClick={() => this.props.toWorkPostPage(this.props.match.params, item.homework_id)}>提交作品</WordsBtn>
}
{
//
item.work_status && item.work_status.indexOf('补交作品') != -1 &&
<WordsBtn style="blue" className="fl font-16 ml28" onClick={() => this.props.toWorkPostPage(this.props.match.params, item.homework_id)}>补交作品</WordsBtn> }
{ //
item.work_status && item.work_status.indexOf('修改作品') != -1 &&
<WordsBtn style="blue" className="fl font-16 ml28" onClick={() => this.props.toWorkPostPage(this.props.match.params, item.homework_id, true, item.work_id)}>修改作品</WordsBtn>
}
{ //
item.work_status && item.work_status.indexOf('补交附件') != -1 &&
<React.Fragment>
<AccessoryModal
{...this.props}
modalname={"补交附件"}
visible={this.state.accessoryVisible}
Cancelname={"取消"}
Savesname={"确认"}
Cancel={this.Cancelvisible}
setupdate={this.setupdate}
reviseAttachmentUrl={`/student_works/${item.work_id}/revise_attachment.json`}
seeworks={`/courses/${this.props.match.params.coursesId}/common_homeworks/${item.homework_id}/${item.work_id}/appraise`}
/>
<WordsBtn style="blue" className="fl font-16 ml28" onClick={() => this.addAccessory(item)}>补交附件</WordsBtn>
</React.Fragment>
}
{
//
item.work_status && item.work_status.indexOf('查看作品') != -1 &&
<WordsBtn style="blue" className="fl font-16 ml28" onClick={() => this.props.toWorkDetailPage(this.props.match.params, item.homework_id, item.work_id)}>查看作品</WordsBtn> }
</li>
}
</p>
</div>
</div>
</div>
</div>
)
})
}
</div>
)
}
}
export default CommonWorkItem;
@@ -0,0 +1,913 @@
import React,{Component} from "react";
import { Form, Select, Input, Button,Checkbox,Upload,Icon,message,Modal, Table, Divider, Tag,DatePicker,Radio,Tooltip,Spin, Pagination} from "antd";
import {WordsBtn, ConditionToolTip, queryString, publicSearchs, on, off, NoneData, sortDirections} from 'educoder';
import axios from 'axios';
import CheckAllGroup from '../common/button/CheckAllGroup'
import moment from 'moment';
import CheckCodeModal from '../coursesPublic/modal/CheckCodeModal'
import '../css/Courses.css';
import PublishRightnow from './PublishRightnow'
import ModulationModal from "../coursesPublic/ModulationModal";
import AccessoryModal from "../coursesPublic/AccessoryModal";
import LeaderIcon from './common/LeaderIcon'
const $ = window.$;
const Search = Input.Search;
function renderScore(score, content) {
let color = '#747A7F'
if (score >= 90) {
color = '#DD1717'
} else if (score >= 60) {
color = '#FF6800'
}
return <a href="javascript:;" style={{ color, cursor: 'default', minWidth: '30px', display: 'inline-block', textAlign: 'center' }}>
{score == null || score == undefined || score == '--' ? '--': (content || score)}
</a>
}
function getScoreTip(score, dom) {
return score == '--' ? '未评分' : score == '**' ? '未公开' : dom
}
function buildColumns(that, student_works, studentData) {
let gotWorkGroup = false;
let gotProjectInfo = false;
if (student_works && student_works.length) {
student_works.forEach(item => {
if (item.work_group) {
gotWorkGroup = true
}
if (item.project_info && item.project_info.name) {
gotProjectInfo = true;
}
})
} else if (studentData && studentData[0]) {
if (studentData[0].work_group) {
gotWorkGroup = true
}
if (studentData[0].project_info) {
gotProjectInfo = true;
}
}
let courseId= that.props.match.params.coursesId;
let workId= that.props.match.params.workId;
const { course_group_count, homework_status } = that.state;
const isAdmin = that.props.isAdmin()
const isAdminOrStudent = that.props.isAdminOrStudent()
const isStudent = that.props.isStudent()
const isNiPing = homework_status && homework_status.indexOf('匿评中') != -1
const isAppeal = homework_status && homework_status.indexOf('申诉中') != -1
// https://www.trustie.net/issues/21450 分组作业作品列表 学时视角,匿评阶段的列表显示信息不正确
const niPingAndIsStudent = isStudent && (isNiPing || isAppeal)
let columns = [{
width: 60,
title: '序号',
dataIndex: 'id',
key: 'id',
render: (text, record, index) => {
return (
<span style={{ minWidth: '50px', display: 'inline-block', textAlign: 'center' }} >
{record.isMine == true && (student_works && student_works.length) ? '我' : (that.state.page - 1) * PAGE_SIZE + index + 1}
</span>
)},
}, {
title: '姓名',
width: 90,
dataIndex: 'user_name',
key: 'user_name',
// width: '110px',
render: (text, record) => (
<div style={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
width: '74px',
margin: '0 auto'
}} title={text && text.length > 5 ? text : ''}>
{/* <Tooltip placement="bottom" title={text}>
</Tooltip> */}
{record.is_leader ?
<div style={{ display: 'flex', 'flex-direction': 'column', 'align-items': 'center'}}>
<div >{text}</div>
<LeaderIcon></LeaderIcon>
</div>
: <React.Fragment>{text}</React.Fragment>}
</div>
),
}]
if (!niPingAndIsStudent && isAdminOrStudent) {
columns.push({
width: isStudent ? undefined : 88,
title: '学号',
dataIndex: 'student_id',
key: 'student_id',
sorter: true,
sortDirections: sortDirections,
render: (text, record) => (
<span>
<a href="javascript:;"
title={text && text.length > 12 ? text : ''}
style={{
color: '#9A9A9A',
'text-overflow': 'ellipsis',
'white-space': 'nowrap',
'width': '98px',
display: 'block',
overflow: 'hidden'
, margin: '0 auto', cursor: 'default'}}
>{record.student_id}</a>
</span>
),
})
}
// TODO 只有有分班时才显示分班列
if (course_group_count != 0 && !niPingAndIsStudent) {
columns.push( {
title: '分班',
key: 'group_name',
dataIndex: 'group_name',
render: (text, record) => (
<span>
<a href="javascript:;" style={{color:'#9A9A9A', cursor: 'default'}}>{record.group_name}</a>
</span>
),
} )
}
if (gotWorkGroup && !niPingAndIsStudent) {
columns.push({
width: 72,
title: '分组',
dataIndex: 'work_group',
key: 'work_group',
render: (text, record) => (
<span>
<a href="javascript:void(0);" style={{color:'#4CACFF'}}>{record.work_group}</a>
</span>
),
})
}
if (gotProjectInfo) {
columns.push({
width: 72,
title: '关联项目',
dataIndex: 'project_info',
key: 'project_info',
render: (project_info, record) => (
<span>
{project_info && project_info.name &&
<a href={project_info.id == -1 ? 'javascript:void(0)' : `/projects/${project_info.id}`}
target={project_info.id == -1 ? '' : "_blank"}
className="overflowHidden1" style={{
color: '#4CACFF',
width: that.state.anonymous_comment ? '80px' : '130px',
margin: '0 auto',
display: 'block'
}} title={project_info.name}
>{project_info.name}</a>}
</span>
),
})
}
columns = columns.concat([{
width: 88,
title: '作品状态',
dataIndex: 'work_status',
key: 'work_status',
render: (status, record) => {
let color;
let text
if (status === 2) {
color = '#DD1717'
text = '延时提交'
} else if (status === 0 ) {
color = '#747A7F'
text = '未提交'
} else {
color = '#29BD8B'
text = '按时提交'
}
return (
<span>
<a href="javascript:;" style={{color: color, cursor: 'default'}}>
{status === 0 ? "未提交" : status === 1 ? "按时提交" : status === 2 ? "延时提交" : ""}
</a>
</span>
)},
}, {
width: 106, // isStudent ? undefined : 106 , // 匿评中 只有这几列: 序号 姓名 提交状态 更新时间 匿评评分 操作
title: '更新时间',
dataIndex: 'update_time',
key: 'update_time',
sorter: true,
defaultSortOrder: 'descend',
sortDirections: sortDirections,
render: (update_time, record) => (
<span>
<a href="javascript:;" style={{color:'#989898', cursor: 'default'}}>{update_time ? moment(update_time).format('YYYY-MM-DD HH:mm') : '--'}</a>
</span>
),
}])
if (!niPingAndIsStudent) {
columns.push({
width: 70,
title: '教师评分',
key: 'teacher_score',
dataIndex: 'teacher_score',
render: (teacher_score, record) => (
<Tooltip title={ getScoreTip(teacher_score, teacher_score) }>
<span>
{ renderScore(teacher_score) }
</span>
</Tooltip>
),
})
columns.push({
width: 70,
title: '助教评分',
key: 'teaching_asistant_score',
dataIndex: 'teaching_asistant_score',
/**
* 2名助教进行了评分
平均分:85.0分
*
*/
render: (teaching_asistant_score, record) => (
<span>
{ <Tooltip placement="bottom" title={
getScoreTip(teaching_asistant_score,
<div>
<div>{record.ta_comment_count}名助教进行了评分</div>
<div>{that.state.ta_mode == 1 ? '平均分': '复审分'}{teaching_asistant_score}</div>
</div>) }
>
{renderScore(teaching_asistant_score) }
</Tooltip> }
</span>
),
})
}
if (that.state.anonymous_comment) {
/**
开启了匿评的才显示此列,悬浮TIP示例:
3名学生进行了匿评
有效平均分:80.0分
*/
columns.push({
width: 84,
// title: <div style={{color: 'rgba(0,0,0,.85)'}}><div style={{color: 'rgba(0,0,0,.85)'}}>匿评</div>评分</div>,
title: <div style={{color: 'rgba(0,0,0,.85)'}}>匿评评分</div>,
key: 'student_score',
dataIndex: 'student_score',
render: (student_score, record) => (
<span>
<Tooltip title={
getScoreTip(student_score,
that.state.is_evaluation ? `你的评阅分数:${record.student_score}` : <div>
{record.student_comment_count && <div>{`${record.student_comment_count}名学生进行了匿评`}</div>}
<div>有效平均分{record.student_score}</div>
</div>
)
}>
{ renderScore(student_score, `${student_score}${record.student_comment_count ? ` (${record.student_comment_count})` : ''}`) }
</Tooltip>
</span>
),
})
}
if (that.state.anonymous_appeal) {
columns.push({
width: 70,
title: '匿评申诉',
key: 'appeal_all_count',
dataIndex: 'appeal_all_count',
render: (appeal_all_count, record) => (
<span>
{!!appeal_all_count &&
<Tooltip placement="bottom" title={`共有${appeal_all_count}条匿评申诉,${record.appeal_deal_count}条待处理`}>
<span style={{ minWidth: '30px', display: 'inline-block', textAlign: 'center' }}>
{`${record.appeal_deal_count}/${appeal_all_count}`}
</span>
</Tooltip> }
{ !appeal_all_count && <span style={{ color: '#747A7F' }}>-/-</span>}
</span>
),
})
}
if (!niPingAndIsStudent) {
columns.push({
width: '113px',
title: '当前成绩',
key: 'work_score',
dataIndex: 'work_score',
sorter: true,
sortDirections: sortDirections,
render: (work_score, record) => {
return (
<span>
<Tooltip title={
getScoreTip(work_score,
<div>
<div>{`${record.user_name}${record.user_login}`}</div>
{record.ultimate_score ?
<div>最终调整成绩{record.work_score}</div> :
<div>
{ record.final_score && <div>作业评分{record.final_score}</div> }
{ record.late_penalty >= 0 && <div>迟交扣分{record.late_penalty}</div>}
{ record.absence_penalty >= 0 && <div>缺评扣分{record.absence_penalty}</div>}
{ record.appeal_penalty >= 0 && <div>违规匿评扣分{record.appeal_penalty}</div>}
<div>最终成绩{record.work_score}</div>
</div> }
</div>)
}>
{ renderScore(work_score) }
</Tooltip>
</span>
)
},
})
}
if (isAdminOrStudent || that.props.work_public == true) {
columns.push({
width: 72,
title: '操作',
key: 'operation',
dataIndex: 'operation',
render: (operation, record) => (
record.work_status === 0 && !isAdmin ? <span style={{ color: '#747A7F' }}>--</span>:
<div>
{/* 0 未提交 */}
{/*<React.Fragment>*/}
{/*</React.Fragment>*/}
{ isAdmin && <Tooltip placement="bottom" title={<pre>调整学生当前成绩<br/>其它历史评分将全部失效</pre>}>
<a style={{color: "#4CACFF"}}
onClick={() => that.showModulationModal(record)}
>调分</a>
</Tooltip> }
{/* toWorkDetailPage */}
{/* /courses/"+courseId+"/common_homeworks/"+workId+ '/' + record.id +"/appraise */}
<a style={{color: '#4CACFF', marginLeft: '4px'}} id={"asdasdasdasd"}
onMouseDown={(e) => that.props.toWorkDetailPage2(e, courseId, workId, record.id)}
onClick={() => that.props.toWorkDetailPage(courseId, workId, record.id)}>{isAdmin ? '评阅' : '查看'}</a>
</div>
),
});
}
return columns;
}
// update_time,最终成绩:work_score,学号:student_id
// desc:倒序 asc:顺序
const orderMap = {
update_time: 'desc',
work_score: 'asc',
student_id: 'asc',
}
const PAGE_SIZE = 20
// 类似页面 http://localhost:3007/courses/1309/graduation/graduation_tasks/48/76/setting
class CommonWorkList extends Component{
constructor(props){
super(props)
this.publishModal = React.createRef();
this.endModal = React.createRef();
this.state={
course_name:"",
homework_name:"",
search: '',
task_status: [],
teacher_comment: [],
course_group_info: [],
arg_work_status: [],
arg_teacher_comment: [],
arg_course_group: [],
order: 'update_time',
page: 1,
isSpin:true,
left_time: {},
category: {},
b_order: 'desc',
searchtypes:false,
loadingstate:false
}
}
onTablePagination = (page) => {
this.setState({ page: page }, () => {
this.fetchList()
})
}
onSearchValue = (val) => {
if (val === ""||val===undefined) {
this.setState({
search: undefined,
searchtypes:false
})
}else{
this.setState({
searchtypes:true,
loadingstate:true
})
}
this.fetchList()
}
onSearchValueInput = (e) => {
if (e.target.value === ""||e.target.value===undefined) {
this.setState({
search: undefined,
})
} else {
this.setState({search: e.target.value})
}
}
componentDidMount() {
console.log("CommonWorkList 分班list 开始加载");
this.fetchList()
on('commonwork_fetch_all', this.fetchAllListener)
$("html").animate({ scrollTop: $('html').scrollTop() - 100 });
try {
this.props.triggerRef(this);
}catch (e) {
}
}
componentWillUnmount() {
off('commonwork_fetch_all', this.fetchAllListener)
}
fetchAllListener = () => {
this.fetchList()
}
_getRequestParams() {
const {search, arg_work_status, arg_teacher_comment, arg_course_group, order, page, arg_member_work, b_order} = this.state
return {
page,
search,
work_status: arg_work_status, // [0]
course_group: arg_course_group,
teacher_comment: arg_teacher_comment.length == 0 ? '' : arg_teacher_comment[0],
order,
limit: PAGE_SIZE,
b_order: b_order,
group_id:arg_course_group,
member_work: arg_member_work
}
}
fetchData = () => {
this.fetchList()
}
fetchList = () => {
let workId=this.props.match.params.workId;
let courseId=this.props.match.params.coursesId;
const url = `/homework_commons/${workId}/works_list.json`
let params = this._getRequestParams()
axios.post(url, params).then((response)=> {
if (response.data) {
this.setState({
...response.data,
isSpin:false,
loadingstate:false
})
this.props.initWorkDetailCommonState && this.props.initWorkDetailCommonState( Object.assign({...response.data}, {
moduleName: '作品列表'
}))
}
}).catch((error)=>{
console.log(error)
this.setState({
isSpin:false,
loadingstate:false
})
})
}
teacherCommentOptionChange = (values, isAllChecked) => {
this.setState({arg_teacher_comment: isAllChecked ? [] : values, page: 1}, () => {
this.fetchList()
})
}
statusOptionChange = (values, isAllChecked) => {
this.setState({arg_work_status: isAllChecked ? [] : values, page: 1}, () => {
this.fetchList()
})
}
courseGroupOptionChange = (values, isAllChecked) => {
this.setState({arg_course_group: isAllChecked ? [] : values, page: 1}, () => {
this.fetchList()
})
}
memberWorkChange = (values, isAllChecked) => {
this.setState({arg_member_work: isAllChecked ? '' : values[0], page: 1}, () => {
this.fetchList()
})
}
funorder = (order, b_order) => {
this.setState({
order: order
,
b_order: b_order
}, () => {
this.fetchList()
})
}
doWhenSuccess = () => {
this.fetchList()
}
// --------------调分
showModulationModal = (item) => {
this.modulationItem = item;
this.setState({ modulationModalVisible: true })
}
cancelModulationModel = () => {
this.setState({ modulationModalVisible: false })
}
saveModulationModal = (value, num) => {
const item = this.modulationItem
const url = `/student_works/${item.id}/adjust_score.json`
axios.post(url, {
score: num,
comment: value
})
.then((response) => {
if (response.data.status == '0') {
this.setState({ modulationModalVisible: false })
this.props.showNotification('调分成功')
this.fetchList()
}
})
.catch(function (error) {
console.log(error);
});
}
// --------------调分 END
// 补交附件
Cancelvisible=()=>{
this.setState({
visible:false
})
}
addAccessory=()=>{
this.setState({
visible:true
})
}
setupdate = () => {
}
//普通作业tbale 列表塞选数据
table1handleChange = (pagination, filters, sorter) => {
//"ascend" 升序
//"descend" 降序
if (JSON.stringify(sorter) === "{}") {
//没有选择
} else {
// 时间
try {
if (sorter.columnKey === "update_time") {
let myyslorder = "";
if (sorter.order === "ascend") {
myyslorder = "asc";
} else if (sorter.order === "descend") {
myyslorder = "desc";
}
this.funorder("update_time", myyslorder);
}
} catch (e) {
}
//成绩
try {
if (sorter.columnKey === "work_score") {
let myyslorder = "";
if (sorter.order === "ascend") {
myyslorder = "asc";
} else if (sorter.order === "descend") {
myyslorder = "desc";
}
this.funorder("work_score", myyslorder)
}
} catch (e) {
}
//学号
try {
if (sorter.columnKey === "student_id") {
let myyslorder = "";
if (sorter.order === "ascend") {
myyslorder = "asc";
} else if (sorter.order === "descend") {
myyslorder = "desc";
}
this.funorder("student_id", myyslorder)
}
} catch (e) {
}
}
}
render(){
const { getFieldDecorator } = this.props.form;
const dateFormat = 'YYYY-MM-DD HH:mm';
let {course_name, homework_name, search, page, loadingstate, student_works, work_count,
all_member_count, time_status,
// 老师显示的三个过滤条件
task_status, teacher_comment, course_group_info, order,
commit_count, uncommit_count, left_time,
modulationModalVisible, work_statuses,
id, user_name, user_login, student_id, group_name, work_status, update_time, teacher_score, teaching_asistant_score, student_score,
ultimate_score, work_score, student_comment_count, appeal_all_count, appeal_deal_count,
late_penalty, absence_penalty, appeal_penalty
, end_immediately, publish_immediately
, homework_id, visible, work_group, project_info, is_leader
} =this.state;
let courseId=this.props.match.params.coursesId;
let category_id=this.props.match.params.category_id;
let workId=this.props.match.params.workId;
const radioStyle = {
display: 'block',
height: '30px',
lineHeight: '30px',
};
const options_status = task_status.map((item) => {
return { label: `${item.name}(${item.count})`, value: item.id }
})
const options_course_group = course_group_info.map((item) => {
return { label: `${item.group_group_name}(${item.count})`, value: item.course_group_id }
})
const options_teacher_comment = teacher_comment.map((item) => {
return { label: `${item.name}(${item.count})`, value: item.id }
})
// 1:组长, 0:组员,“” 不限
const member_works = [{
name: '组长', id: 1
}, {
name: '组员', id: 0
}]
const options_member_work = member_works.map((item) => {
return { label: `${item.name}`, value: item.id }
})
const isAdmin = this.props.isAdmin()
const isStudent = this.props.isStudent()
const isAdminOrStudent = this.props.isAdminOrStudent()
const isGroup = this.props.isGroup();
// work_group
let StudentData;
if(id===undefined){
StudentData=undefined
}else{
StudentData=isStudent ? [{ id, user_name, user_login, student_id, group_name, work_status, update_time, teacher_score, teaching_asistant_score, student_score,
ultimate_score, work_score, student_comment_count, appeal_all_count, appeal_deal_count,
late_penalty, absence_penalty, appeal_penalty, project_info, is_leader,
work_group, isMine: true }] : []
}
const columns = buildColumns(this, student_works, StudentData)
let params = this._getRequestParams()
let exportUrl = `/api/homework_commons/${workId}/works_list.zip?${queryString.stringify(params)}`;
const exportResultUrl = `/api/homework_commons/${workId}/works_list.xlsx`;
let appraisetype=false;
let appraiselist=this.state.homework_status;
appraiselist&&appraiselist.map((item,key)=>{
if(item==="评阅中"){
appraisetype=true
return
}
})
// time_status int 时间对应的状态: 0:未发布,1:提交中,2:补交中,3:匿评中,4:申诉中,5:评阅中,6:已结束
let timeMsg = '提交剩余时间'
if (time_status === 1) {
} else if (time_status === 2) {
timeMsg = '补交剩余时间'
} else if (time_status === 3) {
timeMsg = '匿评剩余时间'
} else if (time_status === 4) {
timeMsg = '申诉剩余时间'
}
const hasData = this.state.homework_status && this.state.homework_status.indexOf("未发布") == -1 // student_works && !!student_works.length && page == 1 &&
// console.log(StudentData)
// console.log(student_works)
return(
<React.Fragment>
<CheckCodeModal
ref="checkCodeModal"
{...this.props}
></CheckCodeModal>
<AccessoryModal
{...this.props}
modalname={"补交附件"}
visible={visible}
Cancelname={"取消"}
Savesname={"确认"}
Cancel={this.Cancelvisible}
categoryid={category_id}
setupdate={this.setupdate}
reviseAttachmentUrl={`/student_works/${id}/revise_attachment.json`}
/>
<PublishRightnow ref={this.publishModal} showActionButton={false} {...this.props} checkBoxValues={[workId]}
isPublish={true} doWhenSuccess={this.doWhenSuccess}></PublishRightnow>
<PublishRightnow ref={this.endModal} showActionButton={false} {...this.props} checkBoxValues={[workId]}
isPublish={false} doWhenSuccess={this.doWhenSuccess}></PublishRightnow>
{
modulationModalVisible === true ?
<ModulationModal
visible={modulationModalVisible}
Cancel={this.cancelModulationModel}
Saves={(value, num) => this.saveModulationModal(value, num)}
/>
: ""
}
{/* 内容区 */}
<style>{`
.ant-table-thead > tr > th, .ant-table-tbody > tr > td {
text-align: center;
}
.worklist1 .search-new {
margin-bottom: -30px !important;
top: 22px;
position: absolute;
right: 18px;
}
.workListContent .ant-table-thead > tr > th, .workListContent .ant-table-tbody > tr > td {
padding: 10px 1px;
}
`}</style>
<div style={{ background: '#fff'}} className="workListContent">
{ isAdmin && hasData && <ul className="clearfix" style={{padding: "20px 40px 10px", position: 'relative', paddingLeft: '24px'}}>
<CheckAllGroup options={options_teacher_comment} label={'你的评阅:'} onChange={this.teacherCommentOptionChange}></CheckAllGroup>
<CheckAllGroup options={options_status} label={'作品状态:'} onChange={this.statusOptionChange}></CheckAllGroup>
{isGroup && <CheckAllGroup options={options_member_work} label={'组内角色:'} onChange={this.memberWorkChange}></CheckAllGroup>}
{options_course_group.length > 1 && <CheckAllGroup options={options_course_group} label={'分班情况:'} onChange={this.courseGroupOptionChange} checkboxGroupStyle={{width: '980px'}}></CheckAllGroup>}
{/* value={search} */}
<div className="fr search-new mr8" style={{marginBottom:'1px'}}>
{/*<Search*/}
{/* placeholder="请输入姓名或学号搜索1"*/}
{/* id="subject_search_input"*/}
{/* onInput={this.onSearchValueInput}*/}
{/* onSearch={this.onSearchValue}*/}
{/* autoComplete="off"*/}
{/*></Search>*/}
{publicSearchs("请输入姓名或学号搜索",this.onSearchValue,this.onSearchValueInput,this.onSearchValueInput)}
</div>
</ul> }
{ <div id="graduation_work_list" style={{padding: isStudent ? '10px 24px 10px 24px' : '0px 24px 10px 24px'}}>
<div className="clearfix">
{hasData && <span className="fl color-grey-6 font-12">
{ isAdmin ?
(!!all_member_count) && <React.Fragment>
<span className="color-orange-tip">{work_count || '0'}</span>{all_member_count}
</React.Fragment> :
(!!commit_count || !!uncommit_count) &&
<React.Fragment>
<span className="color-orange-tip">{commit_count}</span> {uncommit_count} 
{timeMsg || ''}
<span className="color-orange-tip">{left_time.time}</span>
</React.Fragment>
}
</span>}
</div>
</div> }
<style>{`
.workListContent .ant-table-thead > tr > th {
border-bottom: none;
}
.studentTable .ant-table-tbody { background: '#F1F9FF' }
.studentTable table, .stageTable table{
font-size: 13px !important;
}
`}</style>
{ isStudent &&StudentData===undefined?"":StudentData===undefined?"":
<Table
className="studentTable"
dataSource={StudentData}
onChange={this.table1handleChange}
columns={columns}
pagination={false}
showHeader={ !student_works || student_works.length == 0}
/>
}
<div className={"justify break_full_word new_li edu-back-white course_table_wrap"} style={{minHeight:"480px", marginBottom: '30px'}}>
<style>{`
// .ant-spin-nested-loading > div > .ant-spin .ant-spin-dot {
// top: 72%;}
// }
.singleLine tr.ant-table-row {
background: #f1f9ff;
}
.course_table_wrap .ant-pagination.ant-table-pagination {
float: none;
text-align: center;
}
`}</style>
{isStudent && student_works && student_works.length == 0 || !isStudent && student_works===undefined?"":JSON.stringify(student_works) === "[]" ||student_works===undefined?
<Spin size="large" spinning={this.state.isSpin}>
<div id="forum_list" className="forum_table">
<div className="mh650 edu-back-white">
<NoneData
{...this.props}
{...this.state}
></NoneData>
</div>
</div>
</Spin>
:
<React.Fragment>
<Spin tip="正在加载..." spinning={loadingstate}>
<Table
className="stageTable"
dataSource={student_works}
columns={columns}
showQuickJumper
pagination={false}
onChange={this.table1handleChange}
// loading={loadingstate}
/>
</Spin>
</React.Fragment>
}
</div>
</div>
{work_count > PAGE_SIZE && <Pagination
style={{ textAlign: 'center', marginBottom: '20px' }}
showQuickJumper pageSize={PAGE_SIZE} onChange={this.onTablePagination} current={page} total={work_count} />}
</React.Fragment>
)
}
}
const CommonWorkListForm = Form.create({ name: 'commonworkListForm' })(CommonWorkList);
export default CommonWorkListForm;
@@ -0,0 +1,135 @@
import React,{Component} from "react";
import { Form, Select, Input, Button,Checkbox,Upload,Icon,message,Modal, Table, Divider, Tag,DatePicker,Radio,Tooltip} from "antd";
import {Link} from 'react-router-dom';
import locale from 'antd/lib/date-picker/locale/zh_CN';
import { WordsBtn } from 'educoder';
import axios from 'axios';
import Modals from '../../modals/Modals';
import CoursesListType from '../coursesPublic/CoursesListType';
import HomeworkModal from "../coursesPublic/HomeworkModal";
import moment from 'moment';
import '../css/Courses.css'
const { Option} = Select;
const CheckboxGroup = Checkbox.Group;
const confirm = Modal.confirm;
let GraduationTasksnewtype=true;
const $ = window.$;
const Search = Input.Search;
const RadioGroup = Radio.Group;
function scrollTo(className) {
$("html").animate({ scrollTop: $(`${className}`).offset().top - 400 })
}
function range(start, end) {
const result = [];
for (let i = start; i < end; i++) {
result.push(i);
}
return result;
}
function disabledDateTime() {
return {
// disabledHours: () => range(0, 24).splice(4, 20),
disabledMinutes: () => range(1, 30).concat(range(31, 60)),
// disabledSeconds: () => [55, 56],
};
}
function handleDateString (dateString) {
if (!dateString) return dateString;
const ar = dateString.split(':')
if (ar[1] != '00' && ar[1] != '30') {
return [ar[0], '00'].join(':')
}
return dateString
}
// 类似页面 http://localhost:3007/courses/1309/graduation/graduation_tasks/48/76/setting
class CommonWorkSetting extends Component{
constructor(props){
super(props)
this.state={
course_name:"",
homework_name:"",
}
}
render(){
const { getFieldDecorator } = this.props.form;
const dateFormat = 'YYYY-MM-DD HH:mm';
let {course_name, homework_name
} =this.state;
let courseId=this.props.match.params.coursesId;
let category_id=this.props.match.params.category_id;
let task_Id=this.props.match.params.task_Id;
const radioStyle = {
display: 'block',
height: '30px',
lineHeight: '30px',
};
return(
<React.Fragment>
{ <div>
<div className="newMain clearfix">
<div className={"educontent mb20"}>
<p className="clearfix mt10">
<WordsBtn style="grey" className="fl"> <Link to={"/courses/"+courseId} className="color-grey-6">{course_name}</Link></WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn style="grey" className="fl"> <Link to={"/courses/"+courseId+"/graduation"} className="color-grey-6">毕设任务</Link></WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<span>任务详情</span>
</p>
<div style={{ width:'100%',height:'75px'}} >
<p className=" fl color-black mt25 summaryname">
<Link to={"/courses/"+courseId+"/graduation"+"/graduation_tasks/"}>{homework_name}</Link>
</p>
<CoursesListType
typelist={["已开启补交"]}
typesylename={"mt22"}
/>
<a className="color-grey-6 fr font-16 ml30 mt10 mr20" onClick={this.goback}>返回</a>
</div>
<div className="stud-class-set bor-bottom-greyE">
<div className="mt10 clearfix edu-back-white poll_list pl20">
<Link
className="active"
to={"/courses/"+courseId+"/graduation/graduation_tasks/"+category_id+"/"+task_Id+"/list"}>作品列表</Link>
<Link to={"/courses/"+courseId+"/graduation/graduation_tasks/"+category_id+"/"+task_Id+"/questions"}>作业描述</Link>
<Link to={"/courses/"+courseId+"/graduation/graduation_tasks/"+category_id+"/"+task_Id+"/questions"}>参考答案</Link>
<Link
style={{paddingLeft:'38px'}}
to={"/courses/"+courseId+"/graduation/graduation_tasks/"+category_id+"/"+task_Id+"/setting"}>设置</Link>
<a className={"fr color-blue font-16"} onClick={() => { this.end()} }>立即截止</a>
<a className={"fr color-blue font-16"} onClick={() => { this.publish()} }>立即发布</a>
<a className={"fr color-blue font-16"} href={"/courses/"+courseId+"/graduation/graduation_tasks/"+task_Id+"/edit"}>编辑作业</a>
</div>
</div>
</div>
</div>
</div>}
</React.Fragment>
)
}
}
const CommonWorkSettingForm = Form.create({ name: 'commonworkSettingForm' })(CommonWorkSetting);
export default CommonWorkSettingForm;
@@ -0,0 +1,851 @@
import React,{Component} from "react";
import { Form, Select, Input, Button,Checkbox,Upload,Icon,message,Modal,Tooltip} from "antd";
import {Link} from 'react-router-dom';
import axios from 'axios';
import _ from 'lodash'
import { WordsBtn, getUploadActionUrl, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll, ConditionToolTip } from 'educoder';
import Modals from '../../modals/Modals';
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
import LeaderIcon from './common/LeaderIcon'
const Search = Input.Search;
const CheckboxGroup = Checkbox.Group;
class CommonWorkPost extends Component{
constructor(props){
super(props)
this.mdRef = React.createRef();
this.state={
coursename:"",
coursesearch:"",
title_num:20,
title_value:"",
fileList: [],
contents: [{val:"",id:1}],
type:true,
workslist:undefined,
search:"",
task_status:[],
members: [],
selectmemberslist:[],
minvalue:"",
setvalue:"",
minmaxtype:false,
selectobjct:undefined,
Loadtype:false,
limit:20,
memberNumMin: 2,
memberNumMax: 5,
}
}
componentDidMount(){
const studentWorkId = this.props.match.params.studentWorkId
this.isEdit = !!studentWorkId
window.location.pathname.indexOf('post_edit') != -1
let coursesId=this.props.match.params.coursesId;
let workId=this.props.match.params.workId;
let url = `/homework_commons/${workId}/student_works/new.json`
const isGroup = this.props.isGroup()
if (isGroup) {
this.searchList('',1, this.state.limit)
}
if (this.isEdit) {
url = `/student_works/${studentWorkId}/edit.json`
axios.get(url).then((response)=>{
if(response.data.course_id){
// TODO
// response.data.attachments
const _fileList = response.data.attachments.map(item => {
return {
id: item.id,
uid: item.id,
name: appendFileSizeToUploadFile(item),
url: item.url,
delete: item.delete,
status: 'done'
}
})
const _memebers = response.data.members ? response.data.members.slice(0) : [];
this._edit_init_memebers = _memebers
delete response.data.members;
this.setState({
...response.data,
selectmemberslist: _memebers || [],
// members: [],
task_status: [], //_memebers ? _memebers.map(item => item.user_id) : [],
fileList: _fileList,
memberNumMin: response.data.min_num,
memberNumMax: response.data.max_num,
})
this.mine = _memebers.length ? _memebers[0] : null
// 分组
// this.setState({
// task_status:checkedValues,
// selectmemberslist:selects
// })
this.props.form.setFieldsValue({
description: response.data.description,
}, () => {});
this.mdRef.current.setValue(response.data.description)
}
}).catch((error)=>{
console.log(error)
})
} else {
axios.get(url).then((response)=>{
if(response.data.course_id){
const mine = {
user_id: response.data.user_id,
user_name: response.data.user_name,
student_id: response.data.user_student_id,
group_name: response.data.group_name,
}
this.mine = mine
// const _memebers = response.data.members.slice(0);
if (response.data.members) {
delete response.data.members;
}
this.setState({
...response.data,
selectmemberslist: [mine],
memberNumMin: response.data.min_num,
memberNumMax: response.data.max_num,
})
}
}).catch((error)=>{
console.log(error)
})
}
}
setedit=(workId)=>{
let coursesId=this.props.match.params.coursesId;
window.location.href="courses/"+coursesId+"/graduation_tasks/"+workId+"/appraise";
}
handleSubmit=(e) => {
let {fileList,selectmemberslist,workslist, memberNumMin, memberNumMax}=this.state;
let userids=[];
if(selectmemberslist!=undefined&&selectmemberslist.length>0) {
for (var list of selectmemberslist) {
if (list.user_id != undefined && list.user_id != null) {
userids.push(list.user_id)
}
}
}
const isGroup = this.props.isGroup()
if(!isGroup){
userids=undefined
}
let listid=[];
if(fileList!=undefined&&fileList.length>0) {
for (var list of fileList) {
listid.push(list.id || list.response.id)
}
}
e.preventDefault();
if( true ){
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log(values.description);
// console.log(fileList);
if(values.description===undefined||values.description===""){
return
}
if(isGroup){
if(userids!=undefined){
if(userids.length < memberNumMin){
this.setState({
minvalue: memberNumMin,
setvalue:"小于",
minmaxtype:true
})
return
}else if(userids.length > memberNumMax){
this.setState({
minvalue: memberNumMax,
setvalue:"大于",
minmaxtype:true
})
return
} else {
this.setState({
minmaxtype: false
})
}
}
}
if (!listid || listid.length == 0) {
this.props.confirm({
content: <div>
<div>还未上传附件</div>
<div>是否确认提交作品</div>
</div>,
onOk: () => {
this.doCommit(values, listid, userids)
}
})
} else {
this.doCommit(values, listid, userids)
}
}
});
}
}
doCommit = (values, listid, userids) => {
let workId=this.props.match.params.workId;
let studentWorkId=this.props.match.params.studentWorkId;
let coursesId=this.props.match.params.coursesId;
// let url="/graduation_tasks/"+id+"/graduation_works.json";
let url = `/homework_commons/${workId}/student_works.json`
const moduleName = this.props.getModuleName()
if (this.isEdit) {
url = `/student_works/${studentWorkId}.json`
axios.put(url, {
description:values.description,
attachment_ids:listid,
user_ids:userids
}
).then((response) => {
if(response.status===200) {
if(response.data.status===0){
// this.props.showNotification('保存成功')
}else{
}
this.props.history.push(`/courses/${coursesId}/${moduleName}/${workId}/${studentWorkId}/appraise`)
}
}).catch((error) => {
console.log(error)
})
} else {
axios.post(url, {
description:values.description,
attachment_ids:listid,
user_ids:userids
}
).then((response) => {
if(response.status===200) {
if(response.data.status===0){
this.props.history.push(`/courses/${coursesId}/${moduleName}/${workId}/${response.data.work_id}/appraise`)
// this.props.showNotification('保存成功')
// this.setState({
// Modalstype:true,
// Modalstopval:response.data.message,
// ModalSave:()=>this.setedit(response.data.work_id),
// Loadtype:true
// })
}else{
}
}
}).catch((error) => {
console.log(error)
})
}
}
handleSelectChange = (value) => {
console.log(value);
this.props.form.setFieldsValue({
note: `Hi, ${value === 'male' ? 'man' : 'lady'}!`,
});
}
goback=()=>{
// this.props.toListPage(this.props.match.params, this.state.category ? this.state.category.category_id : '')
this.props.history.goBack()
}
// 输入title
changeTitle=(e)=>{
this.setState({
title_num:20-parseInt(e.target.value.length),
title_value:e.target.value
})
}
// 附件相关 START
handleChange = (info) => {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let fileList = info.fileList;
this.setState({ fileList: appendFileSizeToUploadFileAll(fileList) });
}
}
onAttachmentRemove = (file) => {
// confirm({
// title: '确定要删除这个附件吗?',
// okText: '确定',
// cancelText: '取消',
// // content: 'Some descriptions',
// onOk: () => {
// this.deleteAttachment(file)
// },
// onCancel() {
// console.log('Cancel');
// },
// });
// return false;
// this.setState({
// Modalstype:true,
// // Modalstopval:'确定要删除这个附件吗?',
// Modalstopval:'是否确认删除?',
// ModalSave: ()=>this.deleteAttachment(file),
// ModalCancel:this.cancelAttachment
// })
if (file.delete === false) {
this.props.showNotification(`该作品已被评阅,不能删除该附件`);
return false
} else {
if (!file.percent || file.percent == 100) {
this.props.confirm({
content: '是否确认删除?',
onOk: () => {
this.deleteAttachment(file)
},
onCancel() {
console.log('Cancel');
},
});
return false;
}
}
}
cancelAttachment=()=>{
this.setState({
Modalstype:false,
// Modalstopval:'确定要删除这个附件吗?',
Modalstopval:'是否确认删除?',
ModalSave:"",
ModalCancel:""
})
}
deleteAttachment = (file) => {
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
axios.delete(url, {
})
.then((response) => {
if (response.data) {
// const { status } = response.data;
if (response.data.status === 0) {
console.log('--- success')
// this.setState({
// Modalstype:true,
// Modalstopval:response.data.message,
// ModalSave:this.cancelAttachment,
// Loadtype:true,
// })
this.setState((state) => {
const index = state.fileList.indexOf(file);
const newFileList = state.fileList.slice();
newFileList.splice(index, 1);
return {
fileList: newFileList,
};
});
this.cancelAttachment()
}
}
})
.catch(function (error) {
console.log(error);
});
}
inputSearchValue=(e)=>{
if(e.target.value===""){
this.setState({
search:null
})
}else{
this.setState({
search:e.target.value
})
}
}
searchValue=()=>{
let {search, limit} = this.state;
this.searchList(search,1, limit)
}
searchList=(search,page,limit)=>{
let id=this.props.match.params.workId;
let workId=this.props.match.params.workId;
// let url="/graduation_tasks/"+id+"/graduation_works/search_member_list.json";
let url = `/homework_commons/${workId}/student_works/search_member_list.json`
axios.get(url,{
params: {
search: search,
page: page,
limit: limit
}
}).then((result)=>{
if(result){
this.setState({
page: result.data.members.length == 0 ? page - 1 : page,
members: page != 1 && search == this.state.lastSearch ? this.state.members.concat(result.data.members) : result.data.members,
search: search,
lastSearch: search,
})
}
}).catch((error)=>{
console.log(error)
})
}
contentViewScroll=(e)=>{
//滑动到底判断
let newscrollTop=parseInt(e.currentTarget.scrollTop);
let allclientHeight=e.currentTarget.clientHeight+newscrollTop;
if(e.currentTarget.scrollHeight-allclientHeight===0||e.currentTarget.scrollHeight-allclientHeight===1||e.currentTarget.scrollHeight-allclientHeight===-1){
// console.log("到达底部");
let{ search,page,limit }=this.state;
let newpage=page+1
this.searchList(search,newpage,limit)
}
}
funtaskstatus=(checkedValues)=>{
/**
比较 checkedValues this.state.selectmemberslist
checkedValues length > this.state.task_status.length 是新增 反之是删除
比较找到不同的id
去除重复的checkedValues留下的是新增task_status留下的是删除
*/
const _checkedValues = checkedValues.slice(0)
const _task_status = this.state.task_status.slice(0);
checkedValues.forEach(item => {
this.state.task_status.forEach(_item => {
if (item == _item) {
_.remove(_checkedValues, (item)=> item == _item)
_.remove(_task_status, (item)=> item == _item)
}
})
})
let _selectmemberslist = this.state.selectmemberslist.slice(0)
if (_checkedValues.length) { // 新增
_selectmemberslist.push( this.state.members.filter(item => item.user_id == _checkedValues[0])[0])
} else if (_task_status.length) { // 删除
_.remove(_selectmemberslist, (item)=> item.user_id == _task_status[0])
}
// let{members}=this.state;
// let newlist =members.concat(this.state.selectmemberslist);
// let newcheckedValues=checkedValues;
// let selects= this.mine ? [this.mine] : [];
// // const selectobjct = this._findByUserId(check)
// // selects.push(selectobjct)
// for(var z=0; z<newcheckedValues.length; z++){
// for(var i=0; i<newlist.length; i++){
// if(newlist[i].user_id===newcheckedValues[z]){
// selects.push(newlist[i])
// break;
// }
// }
// }
this.setState({
task_status:checkedValues,
selectmemberslist: _selectmemberslist
// selectmemberslist:checkedValues
})
}
doDelete = (id) => {
let{selectmemberslist,task_status}=this.state;
let newlist=task_status.slice(0);
let selects=selectmemberslist;
for(var i=0; i<newlist.length; i++){
if(newlist[i]===id){
newlist.splice(i,1)
}
}
for(var z=0; z<selects.length; z++){
if(selects[z].user_id===id){
selects.splice(z,1)
}
}
this.setState({
task_status:newlist,
selectmemberslist:selects
})
}
delecttask_status=(id)=>{
if (this.isEdit) {
let deleteOldMemberIndex = -1;
if (this._edit_init_memebers && this._edit_init_memebers.length) {
this._edit_init_memebers.some((item, index) => {
if (item.user_id == id) {
deleteOldMemberIndex = index;
return true
}
})
if (deleteOldMemberIndex == -1) {
this.doDelete(id)
return;
} else {
}
}
this.props.confirm({
content: <div>
<div>TA的作品将被删除</div>
<div>是否确认删除</div>
</div>,
onOk: () => {
let workId=this.props.match.params.workId;
const url = `/homework_commons/${workId}/student_works/delete_work.json`;
axios.delete(url, { data: {
user_id: id
}})
.then((response) => {
if (response.data.status == 0) {
this.searchValue()
this.doDelete(id)
deleteOldMemberIndex != -1 && this._edit_init_memebers.splice(deleteOldMemberIndex, 1)
}
})
.catch(function (error) {
console.log(error);
});
}
})
} else {
this.doDelete(id)
}
}
gocannel=()=>{
this.props.history.goBack()
}
render(){
const { getFieldDecorator } = this.props.form;
const isEdit = this.isEdit
let {search,fileList, workslist,setvalue,minvalue,minmaxtype,Loadtype,
Modalstype,Modalstopval,ModalCancel,ModalSave,task_status,selectmemberslist,members,
category,
course_name, homework_name,
memberNumMax, memberNumMin
} =this.state;
const { current_user } = this.props
let courseId=this.props.match.params.coursesId;
let workId=this.props.match.params.workId;
const uploadProps = {
width: 600,
fileList,
multiple: true,
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUploadActionUrl()}`,
onChange: this.handleChange,
onRemove: this.onAttachmentRemove,
beforeUpload: (file) => {
// TODO 文件存在则提示并退出
let gotSameFile = false;
this.state.fileList && this.state.fileList.some((item) => {
if (item.name && item.name.startsWith(file.name)) {
gotSameFile = true;
return true
}
})
if (gotSameFile) {
this.props.showNotification("该附件已被上传!")
return false
}
console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
this.props.showNotification('文件大小必须小于150MB!');
}
return isLt150M;
},
};
// console.log(workslist&&workslist)
// console.log(selectmemberslist&&selectmemberslist)
const moduleName = this.props.getModuleName()
const moduleCHName = this.props.getModuleName(true)
const isGroup = this.props.isGroup()
return(
<React.Fragment>
<div>
{/*提示*/}
<Modals
modalsType={Modalstype}
modalsTopval={Modalstopval}
modalCancel={ModalCancel}
modalSave={ModalSave}
loadtype={Loadtype}
/>
<div className="newMain clearfix">
<div className={"educontent mb20"}>
<p className="clearfix mt10">
<WordsBtn style="grey" className="fl"> <Link to={ current_user && current_user.first_category_url} className="color-grey-6">{course_name}</Link></WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn style="grey" className="fl"> <Link to={`/courses/${courseId}/${moduleName}/${workId}`} className="color-grey-6">{moduleCHName}</Link></WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
{/*<WordsBtn style="grey" className="fl">*/}
{/*<Link to={"/courses/"+courseId+"/graduation/graduation_tasks/"+category_id} className="color-grey-6">{workslist&&workslist.task_name}</Link>*/}
{/*<span className="color-grey-9 ml3 mr3">&gt;</span>*/}
{/*</WordsBtn>*/}
<span>{`${current_user ? current_user.real_name : ''} ${ this.isEdit ? '编辑' : '提交'}作品` }</span>
</p>
<div style={{ width:'100%',height:'75px'}} >
<p className=" fl color-black mt25 summaryname" title={homework_name}>{homework_name}</p>
<a className="color-grey-6 fr font-16 ml30 mt10 mr20" onClick={this.goback}>返回</a>
</div>
{/* */}
<Form >
<div className="stud-class-set pd20 coursenavbox edu-back-white">
<style>{`
.uploadBtn.ant-btn {
border: none;
color: #4CACFF;
box-shadow: none;
background: transparent;
padding: 0 6px;
}
.ant-upload-list-item:hover .ant-upload-list-item-info{
background-color:#fff;
}
.upload_1 .ant-upload-list {
width: 350px;
}
`}</style>
<Form.Item
label="内容"
style={{marginBottom: '4px'}}
className="mdInForm"
>
{getFieldDecorator('description', {
rules: [{
required: true, message: '请输入作品内容',
}, {
max: 5000, message: '最大限制为5000个字符',
}],
})(
<TPMMDEditor ref={this.mdRef} placeholder={'请在此输入作业内容或作业附件的简要描述,最大限制为5000个字符'} maxLent={"5000"}
mdID={'courseMessageMD'} initValue={this.editTopic ? this.editTopic.content : ''} className="courseMessageMD"></TPMMDEditor>
)}
</Form.Item>
<Upload {...uploadProps} className="upload_1 upload_under_markdown_editor">
<Button className="uploadBtn">
<Icon type="upload" /> 上传附件
</Button>
<span className={"color-grey-9"}>(单个文件150M以内)</span>
</Upload>
</div>
{!isGroup?"":
<div className="stud-class-set pd20 coursenavbox edu-back-white" style={{borderTop: '1px solid #EAEAEA'}}>
<Form.Item
label="小组成员"
>
<div>
<div className={"fl ml20"}>成员要求{memberNumMin}{memberNumMax}</div>
<Search
className={"fl mt5 ml20"}
style={{width:"20%"}}
placeholder="请输入姓名或学号搜索"
id="subject_search_input"
value={search}
onInput={this.inputSearchValue}
onSearch={this.searchValue}
autoComplete="off"
></Search>
</div>
</Form.Item>
<style>{`
.fonthidden{
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap
}
.members.fl {
overflow-y: auto;
overflow-x: hidden;
}
`}</style>
<div className={"ml20"} style={{width:"100%"}}>
<div className={"members workPost fl"}
onScroll={this.contentViewScroll}>
<CheckboxGroup value={task_status} onChange={this.funtaskstatus} style={{ paddingTop: '4px'}}>
{members.map((item,key)=>{
return(
<div key={key} style={{
// width: '375px',
height: '30px'
}}>
<Checkbox value={item.user_id} key={item.user_id} checked={parseInt(task_status[key])===item.user_id?true:false}
disabled={item.commit_status || item.user_id == this.props.current_user.user_id ===true?true:false} className="fl "
></Checkbox>
<Tooltip placement="bottom" title={item.user_nam}><div className={"fl ml5 fonthidden"} style={{width: '70px'}}>{item.user_name}</div></Tooltip>
<div className={"fl ml20 color-grey-9 overflowHidden1"} style={{ maxWidth: '147px' }}
title={item.group_name && item.group_name.length > 9 ? item.group_name : ''}
>{item.group_name}</div>
<div className={"fl ml20 color-grey-9 overflowHidden1"} style={{ maxWidth: '99px' }}
title={item.student_id && item.student_id.length > 12 ? item.student_id : ''}
>{item.student_id}</div>
<div className={"fl ml20"}>{item.commit_status===true?<span className={"color-orange"}>已提交</span> :""}</div>
</div>
)
})}
</CheckboxGroup>
</div>
<div className={"youjiangboxs"}>
<i className={"iconfont icon-youjiang fl ml20 mr20"}></i>
</div>
<style>{`
.fonthidden{
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap
}
.members .leaderIcon {
}
.workPost.members {
width: 452px;
}
`}</style>
<div className={"members workPost fl"}>
{selectmemberslist&&selectmemberslist.map((item,key)=>{
const _is_leader = (item.is_leader || !this.isEdit && key==0)
return(
<div key={key} style={{
// width: '375px',
height: '30px',
display:item.user_name===undefined?"none":""
}}>
<ConditionToolTip placement="bottom" title={'组长'} condition={_is_leader && item.user_name.length > 5}>
<div className={"fl ml5 fonthidden"} style={{width: '98px', color: `${item.user_name.length > 5 && _is_leader ? '#4CACFF' : 'inherit'}`}}>
{item.user_name}{ item.user_name.length <= 5 && _is_leader && <LeaderIcon className="leaderIcon" small={true}></LeaderIcon>}
</div>
</ConditionToolTip>
<div className={"fl ml20 color-grey-9 overflowHidden1"} style={{ maxWidth: '147px' }}
title={item.group_name && item.group_name.length > 9 ? item.group_name : ''}
>{item.group_name}</div>
<div className={"fl ml20 color-grey-9 overflowHidden1"} style={{ maxWidth: '99px' }}
title={item.student_id && item.student_id.length > 12 ? item.student_id : ''}
>{item.student_id}</div>
{item.user_id != this.props.current_user.user_id ?
<div className={"fr"}><i className={"iconfont icon-shanchudiao fl "}
style={{marginTop:'-4px'}} onClick={()=>this.delecttask_status(item.user_id)}></i></div>:""}
</div>
)
})}
</div>
<div className={"both"}>
</div>
{minmaxtype===true?<span className={"color-red mt20"}>
人数不能{setvalue+minvalue}个人
</span>:""}
</div>
</div>}
<Form.Item>
<div className="clearfix mt30 mb30">
<Button type="primary" onClick={this.handleSubmit} className="defalutSubmitbtn fl mr20">提交</Button>
<a onClick={this.gocannel} className="defalutCancelbtn fl">取消</a>
</div>
</Form.Item>
</Form>
</div>
</div>
</div>
</React.Fragment>
)
}
}
const CommonWorkPostApp = Form.create({ name: 'coursesNew' })(CommonWorkPost);
export default CommonWorkPostApp;
@@ -0,0 +1,130 @@
import React,{Component} from "react";
import { Form, Select, Input, Button,Checkbox,Upload,Icon,message,Modal, Table, Divider, Tag,DatePicker,Radio,Tooltip} from "antd";
import {Link} from 'react-router-dom';
import locale from 'antd/lib/date-picker/locale/zh_CN';
import { WordsBtn, MarkdownToHtml, on, off } from 'educoder';
import axios from 'axios';
import Modals from '../../modals/Modals';
import CoursesListType from '../coursesPublic/CoursesListType';
import HomeworkModal from "../coursesPublic/HomeworkModal";
import CheckAllGroup from '../common/button/CheckAllGroup'
import moment from 'moment';
import CheckCodeModal from '../coursesPublic/modal/CheckCodeModal'
import '../css/Courses.css'
import WorkDetailPageHeader from './common/WorkDetailPageHeader'
import CommonReply from '../common/comments/CommonReply.js'
import TabRightComponents from './common/TabRightComponents'
const { Option} = Select;
const CheckboxGroup = Checkbox.Group;
const confirm = Modal.confirm;
let GraduationTasksnewtype=true;
const $ = window.$;
const Search = Input.Search;
const RadioGroup = Radio.Group;
class CommonWorkQuestion extends Component{
constructor(props){
super(props)
this.state={
course_name:"",
homework_name:"",
search: ''
}
}
fetchData = () => {
let workId=this.props.match.params.workId;
const url = `/homework_commons/${workId}.json`
axios.get(url).then((result)=> {
if (result.data.course_id) {
this.setState({
...result.data
}, () => {
})
this.props.initWorkDetailCommonState && this.props.initWorkDetailCommonState( Object.assign({...result.data}, {
moduleName: '作业描述'
}))
}
}).catch((error)=>{
console.log(error)
})
}
componentDidMount() {
this.fetchData()
try {
this.props.triggerRef(this);
}catch (e) {
}
on('commonwork_fetch_all', this.fetchAllListener)
}
componentWillUnmount() {
off('commonwork_fetch_all', this.fetchAllListener)
}
fetchAllListener = () => {
this.fetchData()
}
render(){
const dateFormat = 'YYYY-MM-DD HH:mm';
let {course_name, homework_name, search, page, loadingstate, homework_status, description
, homework_id, homework_user_id, attachments, group_info = {}
} =this.state;
let courseId=this.props.match.params.coursesId;
let category_id=this.props.match.params.category_id;
let task_Id=this.props.match.params.task_Id;
const isGroup = this.props.isGroup()
return(
<React.Fragment>
{/* 内容区 */}
<div className="padding40 memoContent new_li">
<MarkdownToHtml content={description} selector="work_content" className="mb10"></MarkdownToHtml>
{ attachments && attachments.map((item) => {
return ( <div className="color-grey">
<a className="color-grey">
<i className="font-14 color-green iconfont icon-fujian mr8" aria-hidden="true"></i>
</a>
<a href={item.url} className="mr12" length="58">{item.title}</a>
<span className="color-grey mt2 color-grey-6 font-12">{item.filesize}</span>
</div> )
}) }
{isGroup && <div className="df mt10">
<div style={{flex: '0 0 160px'}}>
<div>分组要求 {group_info.min_num} ~ {group_info.max_num}</div>
<div>{group_info.base_on_project ? '基于项目实施' : '不基于项目'}</div>
</div>
<div style={{color: '#9B9B9B'}}>
<div>学生提交作品时需要关联同组成员组内成员作品共享</div>
<div>{group_info.base_on_project ? '学生必须在本平台创建项目,项目管理员可以提交作品' : '无需在平台创建项目,任意小组成员均可以提交作品'}</div>
{/* <div>(各小组必须在educoder平台创建项目)</div> */}
</div>
</div>}
</div>
{ homework_id && <CommonReply
memo={{
id: homework_id,
}}
course_id={courseId} {...this.props}
apiRouteName={'homework_commons'}
jour_type={'HomeworkCommon'}
></CommonReply> }
</React.Fragment>
)
}
}
export default CommonWorkQuestion;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,186 @@
import React,{ Component } from "react";
import {Radio,Input ,Modal} from "antd";
import '../css/members.css'
import { WordsBtn } from 'educoder'
import axios from 'axios'
import InfiniteScroll from 'react-infinite-scroller';
import NoneData from "../coursesPublic/NoneData"
const Search =Input.Search;
const LIMIT = 15
class ConnectProject extends Component{
constructor(props){
super(props);
this.state={
project_flag:false,
page: 1,
loading: false,
hasMore: true,
keyword: ''
}
}
componentDidMount() {
}
onSubmit = () => {
const { radioValue, projects } = this.state;
if (projects.length === 0) {
this.closeConnectionProject()
return;
}
if (!radioValue) {
this.props.showNotification('请先在下面的列表中选择项目')
return;
}
this.connectProject(radioValue)
}
connectProject = (project_id) => {
let workId=this.work.homework_id;
const url = `/homework_commons/${workId}/student_works/relate_project.json`
axios.post(url, {
project_id: project_id
}).then((result)=>{
if(result.data.status==0){
this.closeConnectionProject()
this.props.connectSuccess()
this.props.showNotification('关联成功')
}
}).catch((error)=>{
console.log(error);
})
}
onSearchValue = () => {
this.fetchData(1)
}
fetchData = (page = 1) => {
// https://www.showdoc.cc/127895880302646?page_id=2032696709966819
// https://www.showdoc.cc/page/edit/127895880302646/2228554041379772
// /${this.props.current_user.user_id}
const url = `/users/projects/search.json`
this.setState({ loading: true })
const { keyword } = this.state;
axios.get(url, {
params: {
page: page,
per_page: LIMIT,
keyword: keyword
}
}).then((result)=>{
if(result.status==200){
this.setState({
count: result.data.count,
projects: page == 1 ? result.data.projects : this.state.projects.concat(result.data.projects),
page: page,
loading: false,
hasMore: result.data.projects.length != 0
})
if (page == 1) {
this.setState({ haveProjects: result.data.projects.length > 0 })
}
}
}).catch((error)=>{
console.log(error);
})
}
//关联项目
openConnectionProject=(work)=>{
this.work = work;
this.fetchData()
this.setState({
project_flag:true
})
}
closeConnectionProject=()=>{
this.setState({
project_flag:false
})
}
onChange = (e) => {
this.setState({ radioValue: e.target.value })
}
render(){
let {project_flag, projects, loading, hasMore, haveProjects }=this.state;
return(
<React.Fragment>
<Modal
visible={project_flag}
title="关联项目"
centered={true}
closable={false}
keyboard={false}
width="600px"
footer={null}
destroyOnClose={true}
>
<style>{`
.connectProject .ant-radio {
float: left;
line-height: 18px;
}
.connectProject .ant-radio-wrapper .name {
max-width: 480px;
display: inline-block;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
}
.connectProject .ant-radio-wrapper>span:last-child {
line-height: 18px;
}
`}</style>
<div className="newupload_conbox clearfix connectProject">
{
(projects && !!projects.length || this.state.keyword) &&
<div>
<Search placeholder="请输入项目名称进行搜索" className="with100"
value={this.state.keyword} onInput={(e) => {this.setState({keyword: e.target.value})}}
onSearch={this.onSearchValue}
></Search>
<Radio.Group onChange={this.onChange} value={this.state.radioValue} className={"with100"}>
<div className="mt15" style={{"maxHeight":"228px","overflow-y":"auto", "overflow-x": 'hidden'}}>
<InfiniteScroll
threshold={20}
initialLoad={false}
pageStart={0}
loadMore={() => this.fetchData(this.state.page + 1)}
hasMore={!loading && hasMore}
useWindow={false}
>
{ projects.map(item => {
return <p className="mb7" key={item.id}><Radio value={item.id}>
<span title={item.name.length > 12 ? item.name : ''} className="name">{item.name}</span>
</Radio></p>
})}
</InfiniteScroll>
</div>
</Radio.Group>
</div>
}
{
haveProjects && projects.length == 0 &&
<NoneData></NoneData>
}
{
(!haveProjects) &&
<div className="edu-txt-center">
您当前尚未管理任何项目请先<WordsBtn style="blue" className="" onClick={this.props.toCreateProject}>创建项目</WordsBtn>
</div>
}
<div className="mt30 marginauto clearfix edu-txt-center">
<a onClick={this.closeConnectionProject} className="pop_close task-btn mr30">取消</a>
<a className="task-btn task-btn-orange" onClick={this.onSubmit}>确定</a>
</div>
</div>
</Modal>
</React.Fragment>
)
}
}
export default ConnectProject;
@@ -0,0 +1,142 @@
import React, { Component } from 'react';
import { SnackbarHOC } from 'educoder';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import Loadable from 'react-loadable';
import Loading from '../../../Loading';
import { CNotificationHOC } from '../common/CNotificationHOC'
import { RouteHOC } from './common'
import "../css/busyWork.css"
//引入对应跳转的组件
const ListPageIndex = Loadable({
loader: () => import('../ListPageIndex'),
loading:Loading,
})
//新建分组/普通作业
const NewWork = Loadable({
loader: () => import('./NewWork'),
loading:Loading,
})
const CommonWorkDetailIndex = Loadable({
loader: () => import('./CommonWorkDetailIndex'),
loading:Loading,
})
const CommonWorkSetting = Loadable({
loader: () => import('./CommonWorkSetting'),
loading:Loading,
})
const CommonWorkList = Loadable({
loader: () => import('./CommonWorkList'),
loading:Loading,
})
const CommonWorkQuestion = Loadable({
loader: () => import('./CommonWorkQuestion'),
loading:Loading,
})
const CommonWorkAnswer = Loadable({
loader: () => import('./CommonWorkAnswer'),
loading:Loading,
})
const CommonWorkAppraise = Loadable({
loader: () => import('./CommonWorkAppraise'),
loading:Loading,
})
const CommonWorkPost = Loadable({
loader: () => import('./CommonWorkPost'),
loading:Loading,
})
const CommonWork = Loadable({
loader: () => import('./commonWork'),
loading:Loading,
})
class CoursesWorkIndex extends Component{
constructor(props) {
super(props)
}
render() {
return (
<div>
<Switch {...this.props}>
{/* --------------------------------------------------------------------- */}
{/* 作业设置 */}
{/* http://localhost:3007/courses/1309/homework/9300/setting */}
<Route exact path="/courses/:coursesId/common_homeworks/:workId/setting"
render={
(props) => (<CommonWorkDetailIndex {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 作品列表 */}
<Route exact path="/courses/:coursesId/common_homeworks/:workId/list"
render={
(props) => (<CommonWorkDetailIndex {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 作业问答 */}
<Route exact path="/courses/:coursesId/common_homeworks/:workId/question"
render={
(props) => (<CommonWorkDetailIndex {...this.props} {...props} {...this.state} />)
}
></Route>
<Route exact path="/courses/:coursesId/common_homeworks/:workId/answer"
render={
(props) => (<CommonWorkDetailIndex {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 评阅 */}
<Route exact path="/courses/:coursesId/common_homeworks/:workId/:studentWorkId/appraise"
render={
(props) => (<CommonWorkAppraise {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 提交作品 */}
<Route exact path="/courses/:coursesId/common_homeworks/:workId/post"
render={
(props) => (<CommonWorkPost {...this.props} {...props} {...this.state} />)
}
></Route>
<Route exact path="/courses/:coursesId/common_homeworks/:workId/:studentWorkId/post_edit"
render={
(props) => (<CommonWorkPost {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 新建 */}
{/* http://localhost:3007/courses/1309/common_homeworks/9299/edit/1 */}
<Route exact path="/courses/:coursesId/common_homeworks/:workId/:pageType"
render={
(props) => (<NewWork {...this.props} {...props} {...this.state} />)
}
></Route>
<Route exact path="/courses/:coursesId/common_homeworks/:categroy_id/new"
render={
(props) => (<NewWork {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 作业列表 */}
<Route path="/courses/:coursesId/common_homeworks"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} />)
}
></Route>
{/* --------------------------------------------------------------------- */}
</Switch>
</div>
)
}
}
export default RouteHOC() (CNotificationHOC() (SnackbarHOC() ( CoursesWorkIndex) ));
@@ -0,0 +1,140 @@
import React, { Component } from 'react';
import { SnackbarHOC } from 'educoder';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import Loadable from 'react-loadable';
import Loading from '../../../Loading';
import { CNotificationHOC } from '../common/CNotificationHOC'
import { RouteHOC } from './common'
import "../css/busyWork.css"
//引入对应跳转的组件
const ListPageIndex = Loadable({
loader: () => import('../ListPageIndex'),
loading:Loading,
})
//新建分组/普通作业
const NewWork = Loadable({
loader: () => import('./NewWork'),
loading:Loading,
})
const CommonWorkPost = Loadable({
loader: () => import('./CommonWorkPost'),
loading:Loading,
})
const CommonWorkSetting = Loadable({
loader: () => import('./CommonWorkSetting'),
loading:Loading,
})
const CommonWorkList = Loadable({
loader: () => import('./CommonWorkList'),
loading:Loading,
})
const CommonWorkQuestion = Loadable({
loader: () => import('./CommonWorkQuestion'),
loading:Loading,
})
const CommonWorkAnswer = Loadable({
loader: () => import('./CommonWorkAnswer'),
loading:Loading,
})
const CommonWorkAppraise = Loadable({
loader: () => import('./CommonWorkAppraise'),
loading:Loading,
})
const CommonWork = Loadable({
loader: () => import('./commonWork'),
loading:Loading,
})
const CommonWorkDetailIndex = Loadable({
loader: () => import('./CommonWorkDetailIndex'),
loading:Loading,
})
class CoursesWorkIndex extends Component{
constructor(props) {
super(props)
}
render() {
return (
<div>
<Switch {...this.props}>
{/* --------------------------------------------------------------------- */}
{/* --------------------------------------------------------------------- */}
{/* 作业设置 */}
<Route exact path="/courses/:coursesId/group_homeworks/:workId/setting"
render={
(props) => (<CommonWorkDetailIndex {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 作品列表 */}
<Route exact path="/courses/:coursesId/group_homeworks/:workId/list"
render={
(props) => (<CommonWorkDetailIndex {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 作业问答 */}
<Route exact path="/courses/:coursesId/group_homeworks/:workId/question"
render={
(props) => (<CommonWorkDetailIndex {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 参考答案 */}
<Route exact path="/courses/:coursesId/group_homeworks/:workId/answer"
render={
(props) => (<CommonWorkDetailIndex {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 评阅 */}
<Route exact path="/courses/:coursesId/group_homeworks/:workId/:studentWorkId/appraise"
render={
(props) => (<CommonWorkAppraise {...this.props} {...props} {...this.state} />)
}
></Route>
<Route exact path="/courses/:coursesId/group_homeworks/:workId/post"
render={
(props) => (<CommonWorkPost {...this.props} {...props} {...this.state} />)
}
></Route>
<Route exact path="/courses/:coursesId/group_homeworks/:workId/:studentWorkId/post_edit"
render={
(props) => (<CommonWorkPost {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 新建 */}
{/* http://localhost:3007/courses/1309/group_homeworks/9299/edit/1 */}
<Route exact path="/courses/:coursesId/group_homeworks/:workId/:pageType"
render={
(props) => (<NewWork {...this.props} {...props} {...this.state} />)
}
></Route>
<Route exact path="/courses/:coursesId/group_homeworks/:categroy_id/new"
render={
(props) => (<NewWork {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 作业列表 */}
<Route path="/courses/:coursesId/group_homeworks"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} />)
}
></Route>
</Switch>
</div>
)
}
}
export default RouteHOC() (CNotificationHOC() (SnackbarHOC() ( CoursesWorkIndex) ));
@@ -0,0 +1,207 @@
import React,{ Component } from "react";
import { Input, InputNumber, Form, Button, Checkbox, Upload, Icon, message, Modal } from "antd";
import axios from 'axios'
import '../css/busyWork.css'
import '../css/Courses.css'
import { WordsBtn, getUrl, ConditionToolTip, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll
, getUploadActionUrl } from 'educoder'
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
import CBreadcrumb from '../common/CBreadcrumb'
import NewWorkForm from './NewWorkForm'
const confirm = Modal.confirm;
const $ = window.$
const MAX_TITLE_LENGTH = 60;
class NewWork extends Component{
constructor(props){
super(props);
this.contentMdRef = React.createRef();
this.answerMdRef = React.createRef();
this.state={
category: {},
course_name: ''
}
}
componentDidMount () {
let {typeId, coursesId, pageType, workId}=this.props.match.params;
const isEdit = pageType === "edit"
this.isEdit = isEdit;
if (isEdit) {
this.fetchWork(workId)
} else {
this.fetchCourseData(coursesId)
}
}
fetchCourseData = (courseId) => {
const isGroup = this.props.isGroup()
const url = `/courses/${courseId}/homework_commons/new.json?type=${isGroup ? 3 : 1}`
axios.get(url, {
})
.then((response) => {
if (response.data.course_name) {
const data = response.data;
this.setState({
course_id: data.course_id,
course_name: data.course_name,
category: data.category,
})
}
})
.catch(function (error) {
console.log(error);
});
}
fetchWork = (workId) => {
const url = `/homework_commons/${workId}/edit.json`
axios.get(url, {
})
.then((response) => {
if (response.data.name) {
const data = response.data;
data.isEdit = this.isEdit
this.setState({
course_id: data.course_id,
course_name: data.course_name,
category: data.category,
})
this.newWorkFormRef.initValue(response.data)
}
})
.catch(function (error) {
console.log(error);
});
}
onCancel = () => {
this.props.toListPage(this.props.match.params, this.state.category.category_id)
}
doEdit = (params) => {
const workId = this.props.match.params.workId
const newUrl = `/homework_commons/${workId}.json`
axios.put(newUrl, params)
.then((response) => {
if (response.data.status == 0) {
this.props.showNotification('保存成功')
this.props.toWorkListPage(this.props.match.params, workId)
}
})
.catch(function (error) {
console.log(error);
});
}
doNew = (params) => {
const coursesId = this.props.match.params.coursesId
const newUrl = `/courses/${coursesId}/homework_commons.json`
axios.post(newUrl, params)
.then((response) => {
if (response.data.status == 0) {
this.props.showNotification('保存成功')
this.props.toWorkListPage(this.props.match.params, response.data.homework_id)
}
})
.catch(function (error) {
console.log(error);
});
}
handleContentUploadChange = (info) => {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let contentFileList = info.fileList;
this.setState({ contentFileList: appendFileSizeToUploadFileAll(contentFileList) });
}
}
handleAnswerUploadChange = (info) => {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let answerFileList = info.fileList;
this.setState({ answerFileList: appendFileSizeToUploadFileAll(answerFileList) });
}
}
onAttachmentRemove = (file, stateName) => {
if(!file.percent || file.percent == 100){
this.props.confirm({
content: '是否确认删除?',
onOk: () => {
this.deleteAttachment(file, stateName)
},
onCancel() {
console.log('Cancel');
},
});
return false;
}
}
render(){
let {typeId,coursesId,pageType}=this.props.match.params;
const isGroup = this.props.isGroup()
const moduleName = !isGroup? "普通作业":"分组作业";
const moduleEngName = this.props.getModuleName()
let{
course_name, category
}=this.state
const { current_user } = this.props
const courseId = this.state.course_id || coursesId ;
const isEdit = this.isEdit;
const common = {
onCancel:this.onCancel,
isGroup: this.props.isGroup,
doNew: this.doNew,
doEdit: this.doEdit,
}
document.title=this.state.course_name && this.state.course_name
return(
<div className="newMain">
<div className="educontent mt20 mb50">
<CBreadcrumb items={[
{ to: current_user && current_user.first_category_url, name: this.state.course_name},
{ to: `/courses/${courseId}/${moduleEngName}/${category && category.category_id ? category.category_id : ''}`
, name: category && category.category_name },
{ name: `${ this.isEdit ? '编辑' : '新建'}` }
]}></CBreadcrumb>
<p className="clearfix mt20 mb20">
<span className="fl font-24 color-grey-3">{this.isEdit ?"编辑":"新建"}{ moduleName }</span>
<a href="javascript:void(0)" className="color-grey-6 fr font-16 mr2"
onClick={() => this.props.history.goBack()}>
返回
</a>
</p>
<div>
<style>
{
`
.yslnewworkinputaddonAfter .ant-input{
border-right: none !important;
height: 40px !important;
}
`
}
</style>
<NewWorkForm
{...this.props}
{...this.state}
{...common}
wrappedComponentRef={(ref) => {this.newWorkFormRef = ref}}
></NewWorkForm>
</div>
</div>
</div>
)
}
}
export default NewWork;
@@ -0,0 +1,555 @@
import React,{ Component } from "react";
import { Input, InputNumber, Form, Button, Checkbox, Upload, Icon, message, Modal } from "antd";
import axios from 'axios'
import '../css/busyWork.css'
import '../css/Courses.css'
import {getUploadActionUrl, ConditionToolTip, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll } from 'educoder'
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
import CBreadcrumb from '../common/CBreadcrumb'
const confirm = Modal.confirm;
const $ = window.$
const MAX_TITLE_LENGTH = 60;
/**
需要注意的props
isGroup
*/
class NewWorkForm extends Component{
constructor(props){
super(props);
this.contentMdRef = React.createRef();
this.answerMdRef = React.createRef();
this.state={
title_value:"",
title_num: 0,
contentFileList: [],
answerFileList: [],
workLoaded: false,
base_on_project: true,
category: {},
min_num: 2,
max_num: 10,
}
}
initValue = (data) => {
if (data.isEdit) {
const contentFileList = data.attachments.map(item => {
return {
id: item.id,
uid: item.id,
name: appendFileSizeToUploadFile(item),
url: item.url,
filesize: item.filesize,
status: 'done'
}
})
const answerFileList = data.ref_attachments.map(item => {
return {
id: item.id,
uid: item.id,
name: appendFileSizeToUploadFile(item),
url: item.url,
filesize: item.filesize,
status: 'done'
}
})
this.setState({
...data,
// course_id: data.course_id,
// course_name: data.course_name,
// category: data.category,
title_num: parseInt(data.name.length),
workLoaded: true,
init_min_num: data.min_num,
init_max_num: data.max_num,
// description: data.description,
reference_answer: data.reference_answer,
contentFileList,
answerFileList,
}, () => {
// setTimeout(() => {
// this._scrollToTop()
// 阻止setValue的滚动
// $(window).scroll( function() {
// $("html").scrollTop(0)
// $(window).unbind("scroll");
// });
/**
setValue会调用到 codemirror的 o.scrollIntoView(i), 会导致滚动条跳动
*/
// $('.editormd').parent().css('position', 'fixed').css('left', '-1000px')
// this.contentMdRef.current.setValue(data.description || '')
// this.answerMdRef.current.setValue(data.reference_answer || '')
// setTimeout(() => {
// $('.editormd').parent().css('position', '').css('left', 'auto')
// }, 100);
// }, 500)
this.props.form.setFieldsValue({
title: data.name,
description: data.description || '',
reference_answer: data.reference_answer || '',
});
})
} else { // new
}
// this._scrollToTop()
}
_scrollToTop = () => {
// setTimeout(() => {
$("html").scrollTop(0)
// $("html").animate({ scrollTop: 0 })
// }, 1000)
}
// 输入title
changeTitle=(e)=>{
this.setState({
title_num: parseInt(e.target.value.length)
})
}
handleSubmit = () => {
const courseId = this.state.course_id || this.props.match.params.coursesId ;
this.props.form.validateFieldsAndScroll((err, values) => {
if(err && err.personNum) delete err.personNum;
const mdContnet = this.contentMdRef.current.getValue().trim();
values.description = mdContnet;
// return;
{/* max={has_commit ? init_min_num : null } */}
{/* min={has_commit ? init_max_num : (min_num == undefined ? 2 : min_num + 1) } */}
// 已有提交作品,人数范围只能扩大
const { has_commit, max_num, init_max_num, min_num, init_min_num } = this.state;
const isGroup = this.props.isGroup()
if (isGroup) {
if (!min_num) {
this.props.showNotification('最小人数不能为空');
this.props.scrollToAnchor("numberofgroups");
return;
} else if (min_num < 1) {
this.props.showNotification('最小人数不能小于1');
this.props.scrollToAnchor("numberofgroups");
return;
} else if (!max_num) {
this.props.showNotification('最大人数不能为空');
this.props.scrollToAnchor("numberofgroups");
return;
} else if (max_num < min_num) {
this.props.showNotification('最大人数不能小于最小人数');
this.props.scrollToAnchor("numberofgroups");
return;
}
if (has_commit) {
if (max_num < init_max_num || min_num > init_min_num) {
this.props.showNotification(`已有提交作品,人数范围只能扩大(原设置为:${init_min_num} - ${init_max_num})`)
return;
}
}
}
// const errKeys = Object.keys(err); // || errKeys.length == 1 && errKeys[0] == 'content' && mdContnet
if (!err || Object.keys(err).length == 0) {
if (this.state.isEdit) {
this.doEdit(courseId, values)
} else {
this.doNew(courseId, values)
}
} else {
$("html").animate({ scrollTop: $('html').scrollTop() - 100 })
}
})
}
doEdit = (courseId, values) => {
let attachment_ids = this.state.contentFileList.map(item => {
return item.response ? item.response.id : item.id
})
let reference_attachment_ids = this.state.answerFileList.map(item => {
return item.response ? item.response.id : item.id
})
const { min_num, max_num, base_on_project, category } = this.state
const isGroup = this.props.isGroup()
const params = {
type: isGroup ? 3 : 1,
name: values.title,
description: values.description,
reference_answer: values.reference_answer,
attachment_ids,
reference_attachment_ids,
min_num,
max_num,
base_on_project
}
this.props.doEdit && this.props.doEdit(params)
}
doNew = (courseId, values) => {
let attachment_ids = this.state.contentFileList.map(item => {
return item.response ? item.response.id : item.id
})
let reference_attachment_ids = this.state.answerFileList.map(item => {
return item.response ? item.response.id : item.id
})
const isGroup = this.props.isGroup()
const { min_num, max_num, base_on_project, category } = this.state
const params = {
type: isGroup ? 3 : 1,
name: values.title,
description: values.description,
reference_answer: values.reference_answer,
attachment_ids,
reference_attachment_ids,
min_num,
max_num,
base_on_project
}
this.props.doNew && this.props.doNew(params)
}
handleContentUploadChange = (info) => {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let contentFileList = info.fileList;
this.setState({ contentFileList: appendFileSizeToUploadFileAll(contentFileList) });
}
}
handleAnswerUploadChange = (info) => {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let answerFileList = info.fileList;
this.setState({ answerFileList: appendFileSizeToUploadFileAll(answerFileList) });
}
}
onAttachmentRemove = (file, stateName) => {
if(!file.percent || file.percent == 100){
this.props.confirm({
content: '是否确认删除?',
onOk: () => {
this.deleteAttachment(file, stateName)
},
onCancel() {
console.log('Cancel');
},
});
}
return false;
}
deleteAttachment = (file, stateName) => {
// 初次上传不能直接取uid
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
axios.delete(url, {
})
.then((response) => {
if (response.data) {
const { status } = response.data;
if (status == 0) {
this.setState((state) => {
const index = state[stateName].indexOf(file);
const newFileList = state[stateName].slice();
newFileList.splice(index, 1);
return {
[stateName]: newFileList,
};
});
}
}
})
.catch(function (error) {
console.log(error);
});
}
max_num_change = (val) => {
// if (val < 2) {
// // this.setState({
// // max_num: 2,
// // })
// return;
// }
const { min_num } = this.state;
this.setState({
max_num: val,
// min_num: val <= min_num ? val - 1 : min_num
})
}
personNumValidator = (rule, value, callback) => {
const { min_num, max_num } = this.state;
const form = this.props.form;
if (!min_num) {
callback('最小人数不能为空');
} else if (min_num < 1) {
callback('最小人数不能小于1');
} else if (!max_num) {
callback('最大人数不能为空');
} else if (max_num < min_num) {
callback('最大人数不能小于最小人数');
} else {
callback();
}
}
min_num_change = (val) => {
this.setState({ min_num: val })
}
base_on_project_change = () => {
this.setState({ base_on_project: !this.state.base_on_project })
}
componentDidMount() {
window.$('.groupSetting .ant-form-item-label > label').addClass('ant-form-item-required')
this._scrollToTop()
}
render(){
let {typeId,coursesId,pageType}=this.props.match.params;
const { getFieldDecorator } = this.props.form;
const isGroup = this.props.isGroup()
let{
title_value, contentFileList, answerFileList, max_num, min_num, base_on_project,
init_max_num, init_min_num,
title_num, course_name, category, has_commit, has_project
}=this.state
const { current_user } = this.props
const courseId = this.state.course_id || this.props.match.params.coursesId ;
this.isEdit = this.isEdit || this.props.match.url.indexOf('/edit') != -1
if ((this.isEdit) && !this.state.description && this.state.description != '') {
return ''
}
const uploadProps = {
width: 600,
fileList: contentFileList,
multiple: true,
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUploadActionUrl()}`,
onChange: this.handleContentUploadChange,
onRemove: (file) => this.onAttachmentRemove(file, 'contentFileList'),
beforeUpload: (file) => {
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
this.props.showNotification('文件大小必须小于150MB!');
}
return isLt150M;
},
};
const answerUploadProps = {
width: 600,
fileList: answerFileList,
multiple: true,
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUploadActionUrl()}`,
onChange: this.handleAnswerUploadChange,
onRemove: (file) => this.onAttachmentRemove(file, 'answerFileList'),
beforeUpload: (file) => {
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
this.props.showNotification('文件大小必须小于150MB!');
}
return isLt150M;
},
};
return(
<React.Fragment>
<style>
{
`
.yslnewworkinputaddonAfter .ant-input{
border-right: none !important;
height: 40px !important;
}
`
}
</style>
<Form className="courseForm">
<Form.Item
label="标题"
className="AboutInputForm"
>
{getFieldDecorator('title', {
rules: [{
required: true, message: '请输入标题'
}],
})(
<Input placeholder="请输入作业标题,最大限制60个字符" onInput={this.changeTitle} className="searchView yslnewworkinputaddonAfter searchViewAfter" style={{"width":"100%"}} maxLength={MAX_TITLE_LENGTH} addonAfter={`${String(title_num)}/${MAX_TITLE_LENGTH}`}/>
)}
</Form.Item>
<style>{`
.uploadBtn.ant-btn {
border: none;
color: #4CACFF;
box-shadow: none;
background: transparent;
padding: 0 6px;
}
.ant-upload-list-item:hover .ant-upload-list-item-info{
background-color:#fff;
}
.upload_1 .ant-upload-list {
width: 350px;
}
.ant-input-number {
height: 40px;
line-height: 40px;
}
.workContent.AboutInputForm.ant-form-item {
border-bottom: none;
padding-bottom: 0px !important;
}
.newWorkUpload {
padding: 0px 30px 30px 30px!important;
background: #fff;
width: 100%;
display: inline-block;
border-bottom: 1px solid #EDEDED;
}
.courseForm .AboutInputForm.clearPaddingBottom{
padding-bottom:0px!important;
}
.clearPaddingBottom .ant-form-explain{
position:absolute;
bottom:0px;
left:0px
}
.resetNewWorkUpload{
border-bottom:none!important;
}
`}</style>
{ <Form.Item
label="内容"
className="AboutInputForm workContent mdInForm"
>
{getFieldDecorator('description', {
rules: [{
required: true, message: '请输入作业内容和要求',
},{
max: 5000 , message:'最大限制5000个字符'
}],
})(
<TPMMDEditor ref={this.contentMdRef} placeholder="请在此输入作业内容和要求,最大限制5000个字符" mdID={'courseContentMD'} refreshTimeout={1500}
initValue={this.state.description} noSetValueOnInit={!!this.isEdit}
className="courseMessageMD" ></TPMMDEditor>
)}
</Form.Item> }
<Upload {...uploadProps} className="upload_1 newWorkUpload">
<Button className="uploadBtn">
<Icon type="upload" /> 上传附件
</Button>
(单个文件150M以内)
</Upload>
<span id={"numberofgroups"}></span>
{ isGroup &&
<Form.Item
label="分组设置"
className="AboutInputForm groupSetting"
>
{getFieldDecorator('personNum', {
validateTrigger: 'onNone',
// rules: [{
// required: true,
// message: '人数不能为空'
// validator: this.personNumValidator
// required: true, message: '请输入最小人数和最大人数'
// }],
})(
<div >
<p className="clearfix">
<ConditionToolTip condition={has_commit} title={'已有提交作品,人数范围只能扩大'}>
{/* max={has_commit ? init_min_num : null } */}
<InputNumber placeholder="请填写每组最小人数" min={1} className="winput-240-40" value={min_num}
onChange={this.min_num_change} style={{width:'180px'}} />
</ConditionToolTip>
<span className="ml15 mr15">~</span>
{/* min={has_commit ? init_max_num : (min_num == undefined ? 2 : min_num + 1) } */}
<ConditionToolTip condition={has_commit} title={'已有提交作品,人数范围只能扩大'}>
<InputNumber className="winput-240-40" placeholder="请填写每组最大人数" value={max_num} max={10}
onChange={this.max_num_change} style={{width:'180px'}} />
</ConditionToolTip>
<label className="color-grey-9 ml20 font-14">学生提交作品时需要关联同组成员组内成员作品共享</label>
</p>
<p className="mt20">
<ConditionToolTip condition={has_commit || has_project} title={'已有关联项目或作品,不能修改'}>
<Checkbox checked={base_on_project} onChange={this.base_on_project_change}
disabled={has_project || has_commit}
>基于项目实施</Checkbox>
</ConditionToolTip>
<label className="color-grey-9 ml12 font-14">选中则必须在本平台创建项目项目管理员可以提交作品不选中无需在平台创建项目任意小组成员均可以提交作品</label>
</p>
</div>
)}
</Form.Item>
}
<div className="edu-back-white">
<Form.Item
label="参考答案"
className="AboutInputForm clearPaddingBottom pr"
style={{"borderBottom":"none"}}
>
{getFieldDecorator('reference_answer', {
rules: [{
max: 5000 , message:'最大限制5000个字符'
}],
})(
<TPMMDEditor ref={this.answerMdRef} placeholder="请在此输入作业的参考答案,最大限制5000个字符" mdID={'workAnswerMD'}
initValue={this.state.reference_answer || ''} noSetValueOnInit={!!this.isEdit}
className="courseMessageMD" refreshTimeout={1500} ></TPMMDEditor>
)}
</Form.Item>
<Upload {...answerUploadProps} className="upload_1 newWorkUpload resetNewWorkUpload">
<Button className="uploadBtn">
<Icon type="upload" /> 上传附件
</Button>
(单个文件150M以内)
</Upload>
</div>
<Form.Item>
<div className="clearfix mt30 mb30">
{/* htmlType="submit" */}
<Button type="primary" onClick={this.handleSubmit} className="defalutSubmitbtn fl mr20">提交</Button>
<a className="defalutCancelbtn fl" onClick={() => this.props.onCancel()}>取消</ a>
</div>
</Form.Item>
</Form>
</React.Fragment>
)
}
}
const WrappedWorkForm = Form.create({ name: 'NewWorkForm' })(NewWorkForm);
export default WrappedWorkForm;
@@ -0,0 +1,269 @@
import React,{ Component } from "react";
import { Input,Checkbox,Menu,Pagination } from "antd";
import HomeworkModal from '../coursesPublic/HomeworkModal';
import OneSelfOrderModal from "../coursesPublic/OneSelfOrderModal";
import axios from 'axios'
import moment from 'moment'
import { getNextHalfHourOfMoment } from 'educoder'
class PublishRightnow extends Component{
constructor(props){
super(props);
this.state={
course_groups: [],
modalname:undefined,
modaltype:undefined,
visible:false,
Topval:undefined,
Botvalleft:undefined,
Botval:undefined,
starttime:undefined,
endtime:undefined,
Cancelname:undefined,
Savesname:undefined,
Cancel:undefined,
Saves:undefined,
Topvalright:undefined
}
}
open = (usingCheckBeforePost) => {
this.usingCheckBeforePost = usingCheckBeforePost == true
this.homeworkstart()
}
homeworkstart=()=>{
const isPublish = this.props.isPublish;
const isPublishtype = this.props.isPublishtype;
let showdatatypes=isPublish===true||isPublishtype===1;
if (!this.props.checkBoxValues || this.props.checkBoxValues.length == 0) {
this.props.showNotification(`请先选择要立即${showdatatypes? "发布" : "截止"}的作业`)
return;
}
this.fetchCourseGroups();
}
showDialog = (course_groups) => {
const isPublish = this.props.isPublish;
const isPublishtype = this.props.isPublishtype;
const dateFormat = 'YYYY-MM-DD HH:mm';
let showdatatype=isPublish===true&&isPublishtype===undefined;
let showdatatypes=isPublish===true||isPublishtype===1;
// getNextHalfHourOfMoment
const startMoment = (moment());
this.setState({
modalname: showdatatypes ? "立即发布" : "立即截止",
modaltype:course_groups.length> 0 ? 1 : 2,
visible:showdatatype?false:true,
OneSelftype:showdatatype?true:false,
Topval:showdatatypes ? "学生将立即收到作业" : "学生将不能再提交作品",
// Botvalleft: isPublish ? "暂不发布" : "暂不截止",
Botval: this.props.fromListPage ? (showdatatypes ? "本操作只对“未发布”的作业有效" : "本操作只对“提交中”的作业有效") : '',
starttime: showdatatypes? `发布时间:${startMoment.format(dateFormat)}` : '',
starttimes:showdatatypes? `${startMoment.format(dateFormat)}` : '',
endtime:showdatatypes ? `截止时间:${startMoment.add(1, 'months').add(1, 'hours').minutes(0).format(dateFormat)}` : '',
Cancelname:showdatatypes ? "暂不发布" : "暂不截止",
Savesname:showdatatypes ? "立即发布" : "立即截止",
Cancel:this.homeworkhide,
Saves:this.homeworkstartend,
typs:showdatatypes ? "start" : "end",
})
}
homeworkhide=()=>{
this.setState({
modalname:undefined,
modaltype:undefined,
visible:false,
OneSelftype:false,
Topval:undefined,
Topvalright:undefined,
Botvalleft:undefined,
Botval:undefined,
starttime:undefined,
endtime:undefined,
Cancelname:undefined,
Savesname:undefined,
Cancel:undefined,
Saves:undefined,
StudentList_value:undefined,
addname:undefined,
addnametype:false,
addnametab:undefined
})
}
homeworkstartend=(arg_group_ids,endtime)=>{
debugger
if (this.usingCheckBeforePost && this.props.checkBeforePost) {
const goOn = this.props.checkBeforePost();
if (!goOn) {
this.homeworkhide();
return;
}
}
debugger
const isPublish = this.props.isPublish;
let group_ids = arg_group_ids
if (this.usingCheckBeforePost) {
group_ids = this.state.course_groups.map((item) => {
return item.id
})
}
debugger
if(this.state.course_groups.length>0){
if (this.state.course_groups.length && (!group_ids || group_ids&&group_ids.length == 0)) {
this.props.showNotification('请至少选择一个分班');
return;
}
}
let data={}
if(arg_group_ids&&arg_group_ids.length===0){
data = {
homework_ids: this.props.checkBoxValues,
end_time: endtime==="Invalid date"?undefined:endtime,
}
}else if(this.props.islist===true){
data={
homework_ids: this.props.checkBoxValues,
group_ids: group_ids,
end_time:endtime,
}
}else{
data={
homework_ids: this.props.checkBoxValues,
group_ids: group_ids,
group_end_times:endtime,
detail:true
}
}
const isPublishtype = this.props.isPublishtype;
let showdatatypes=isPublish===true||isPublishtype===1;
let coursesId=this.props.match.params.coursesId;
const url = `/courses/${coursesId}/homework_commons/${showdatatypes ? "publish_homework" : "end_homework"}.json`
axios.post(url, data)
.then((response) => {
if (response.data.status == 0) {
this.homeworkhide()
this.props.showNotification(showdatatypes ? "立即发布成功" : "立即截止成功")
this.props.doWhenSuccess && this.props.doWhenSuccess()
this.setState({ visible : false })
this.props.action && this.props.action()
}
})
.catch(function (error) {
console.log(error);
});
}
// componentDidUpdate = (prevProps) => {
// if ( prevProps.match.params.boardId != this.props.match.params.boardId ) {
// this.fetchAll(null, 1)
// }
// }
fetchCourseGroups = () => {
const isPublish = this.props.isPublish;
const isPublishtype = this.props.isPublishtype;
let showdatatypes=isPublish===true||isPublishtype===1;
let coursesId=this.props.match.params.coursesId;
// TODO 这里要改成单选作业,接口使用这个 https://www.showdoc.cc/127895880302646?page_id=2035541497546668
// /homework_commons/:id/publish_groups.json
let url = `/courses/${coursesId}/all_course_groups.json`
if (this.props.checkBoxValues.length == 1) {
const isPublish = this.props.isPublish;
url = `/homework_commons/${this.props.checkBoxValues[0]}/${ showdatatypes ? 'publish_groups' : 'end_groups'}.json`
}
axios.get(url, {
})
.then((response) => {
if (!response || response.data.status == -1) {
this.setState({ visible : false })
return;
}
this.showDialog(response.data.course_groups)
this.setState({
course_groups: response.data.course_groups,
starttimesend:response.data.end_time===undefined||response.data.end_time===null||response.data.end_time===""?undefined:response.data.end_time,
})
})
.catch(function (error) {
console.log(error);
});
}
render(){
const isPublish = this.props.isPublish;
const isPublishtype = this.props.isPublishtype;
let showdatatypes=isPublish===true||isPublishtype===1;
let{
Topvalright,
modalname,
modaltype,
visible,
Topval,
Botvalleft,
Botval,
starttime,
starttimes,
endtime,
Cancelname,
Savesname,
Cancel,
Saves,
course_groups
}=this.state
const { showActionButton } = this.props
return(
<div>
{/*立即截止*/}
{visible===true?<HomeworkModal
modaltype={modaltype}
modalname={modalname}
visible={visible}
Topval={Topval}
Topvalright={Topvalright}
Botvalleft={Botvalleft}
Botval={Botval}
starttime={starttime}
starttimes={starttimes}
endtime={endtime}
Cancelname={Cancelname}
Savesname={Savesname}
Cancel={Cancel}
Saves={Saves}
course_groups={course_groups}
usingCheckBeforePost= {this.usingCheckBeforePost}
onToPublishClick={this.props.onToPublishClick}
typs={this.state.typs}
/>:""}
{/*立即发布*/}
{this.state.OneSelftype===true?<OneSelfOrderModal
modaltype={this.state.modaltype}
modalname={this.state.modalname}
OneSelftype={this.state.OneSelftype}
Topval={this.state.Topval}
Topvalright={this.state.Topvalright}
Botvalleft={this.state.Botvalleft}
Botval={this.state.Botval}
starttime={this.state.starttime}
endtime={this.state.endtime}
Cancelname={this.state.Cancelname}
Savesname={this.state.Savesname}
Cancel={this.state.Cancel}
Saves={this.state.Saves}
course_groups={this.state.course_groups}
starttimes={this.state.starttimes}
starttimesend={this.state.starttimesend}
typs={this.state.typs}
/>:""}
{ showActionButton && <a href="javascript:void(0)" className="color-grey-9" onClick={this.homeworkstart}>{ showdatatypes ? "立即发布" : "立即截止" }</a> }
</div>
)
}
}
export default PublishRightnow;
@@ -0,0 +1,26 @@
// import React, { useState, useEffect } from 'react'
// function Example() {
// const [state, setState] = useState({counter: 0})
// useEffect(() => {
// console.log(' cdm')
// return () => {
// console.log(' cwum')
// };
// })
// const add1ToCounter = () => {
// const newCounterValue = state.counter + 1
// setState({ counter: newCounterValue })
// }
// return (
// <div>
// <p>{state.counter}</p>
// <button onClick={add1ToCounter}>
// Click me
// </button>
// </div>
// )
// }
// export default Example
@@ -0,0 +1,392 @@
import React,{ Component } from "react";
import { Modal,Input, Checkbox,Icon,Spin} from "antd";
import '../css/members.css'
import '../css/busyWork.css'
import { WordsBtn, ConditionToolTip } from 'educoder'
import axios from 'axios'
import InfiniteScroll from 'react-infinite-scroller';
import NoneData from '../coursesPublic/NoneData'
const Search=Input.Search;
const pageCount = 20
// "poll", #normal 普通作业题库; group 分组作业题库; poll问卷题库; exercise试卷题库; gtask 毕设选题题库;gtopic 毕设任务
const engNameMap = {
poll: '问卷',
normal: '普通作业',
group: '分组作业',
exercise: '试卷',
gtask: '毕设任务',
gtopic: '毕设选题',
}
class UseBank extends Component{
constructor(props){
super(props);
this.state={
flag:false,
nav_my: 'myself',
search: '',
page: 1,
checkBoxValues: [],
isChecked:'',
is_teacher:undefined,
hometypepvisible:false
}
}
componentDidMount() {
// console.log("UseBank");
// console.log(this.props);
}
onCheckBoxChange = (checkBoxValues) => {
this.setState({
checkBoxValues: checkBoxValues
})
if(checkBoxValues.length!=0){
this.setState({
isChecked:""
})
}
}
fetchAll = (arg_page) => {
console.log('fetchAll')
const courseId = this.props.match.params.coursesId
const page = arg_page || this.state.page;
const { nav_my, search } = this.state
/**
normal 普通作业题库 group 分组作业题库 poll问卷题库 exercise试卷题库; gtask 毕设选题题库gtopic 毕设任务
filter string public公共题库 2 myself我的题库 1
*/
const object_type = this.props.object_type
let url = `/question_banks/bank_list.json?page=${page}&limit=${pageCount}&object_type=${object_type || ''}&search=${search || ''}&filter=${nav_my}`
this.setState({ loading: true })
axios.get(url
// , {
// params: {
// }
// }
)
.then((response) => {
if(response.data){
this.setState({
is_teacher:response.data.is_teacher
})
}
if (!response.data.object_list || response.data.object_list.length == 0) {
this.setState({
object_list: page == 1 ? [] : this.state.object_list,
page,
loading: false,
hasMore: false,
})
} else {
this.setState({
object_list: page == 1 ? response.data.object_list : this.state.object_list.concat(response.data.object_list),
page,
loading: false,
hasMore: response.data.object_list.length == pageCount
})
}
})
.catch(function (error) {
console.log(error);
});
}
openSelectBank=()=>{
this.setState({
search: '',
flag:true
}, () => {
this.fetchAll()
})
}
closeSelectBank=()=>{
this.setState({
flag:false,
nav_my: 'myself',
search: '',
page: 1,
checkBoxValues: [],
isChecked:'',
is_teacher:undefined,
hometypepvisible:false
})
}
//切换tab
changeNav=(index)=>{
this.setState({
nav_my:index
}, () => {
this.fetchAll(1)
})
}
onSearchChange = (e) => {
this.setState({
search: e.target.value
})
}
onSave = () => {
debugger
const { checkBoxValues } = this.state;
const { object_type } = this.props
if(checkBoxValues.length==0){
this.setState({
isChecked:"请先选择"+engNameMap[object_type]
})
return;
}
this.setState({
hometypepvisible:true
})
const courseId = this.props.match.params.coursesId
let url = `/question_banks/save_banks.json`
this.setState({ loading: true })
axios.post(url
, {
"object_type": object_type,
"bank_id": checkBoxValues,
"course_id": courseId
}
)
.then((response) => {
if (response.data.status == 0) {
this.props.useBankSuccess && this.props.useBankSuccess(checkBoxValues,response.data.object_ids);
this.props.showNotification('题库选用成功')
this.closeSelectBank();
this.props.updataleftNavfun()
this.setState({
hometypepvisible:false,
checkBoxValues:[]
})
} else {
this.setState({
hometypepvisible:false,
checkBoxValues:[]
})
this.props.showNotification(response.data.message)
}
})
.catch( (error) =>{
console.log(error);
this.setState({
hometypepvisible:false,
checkBoxValues:[]
})
});
};
getotiku = (url) => {
window.open(url, '_blank');
}
render(){
let { flag, nav_my, loading, hasMore, object_list, search, checkBoxValues,isChecked,page,is_teacher }=this.state
let { object_type }=this.props;
const antIcon = <Icon type="loading" style={{ fontSize: 24 }} spin />;
// console.log("题库选用2222");
// console.log("UseBank");
// console.log(this.props);
return(
<a>
<style>{`
.bankModal .task-hide {
max-width: 290px;
}
.d_middle{
justify-content: center;
align-items: center;
display: -webkit-flex;
}
.setImgW .edu-nodata-img{
width: 170px !important;
}
.bankwidth{
width:32% !important;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap
}
.bankwidth76{
width:76px !important;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
text-align: center;
}
.bankwidth2{
width:24% !important;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap
}
.with58{
width:58% !important;
Margin-left:15px
}
.with63{
width: 60% !important;
box-sizing: border-box;
margin-left: 15px;
}
`}</style>
{
flag===true?<style>
{
`
body {
overflow: hidden !important;
}
`
}
</style>:""
}
{this.props.user&&this.props.user.main_site===true?<WordsBtn style="blue" className="mr30 font-16" onClick={()=>this.openSelectBank()}>{engNameMap[object_type]=="问卷"?"问卷选用":engNameMap[object_type]=="试卷"?"试卷选用":"题库选用"}</WordsBtn>:""}
{flag===true?<Modal
title="题库选用"
visible={flag}
closable={false}
width="800px"
footer={null}
keyboard={false}
destroyOnClose={true}
centered={true}
>
<div className="newupload_conbox clearfix bankModal">
<Spin indicator={antIcon} spinning={this.state.hometypepvisible} className="newnext-loading" color='#4AC7FF' size="large">
<div className="clearfix mb30">
<ul className="fl mt2 bankNav">
<li className={nav_my==='myself'?"active":""} onClick={()=>this.changeNav('myself')}><a>我的题库</a></li>
<li className={nav_my==='myself'?"":"active"} onClick={()=>this.changeNav('public')}><a>公共题库</a></li>
</ul>
<Search placeholder="输入标题、适用课程进行搜索" className="searchView fr" style={{"width":"268px","height":"30px"}}
onSearch={() => this.fetchAll(1)} onChange={this.onSearchChange} value={search}
></Search>
</div>
{
object_list && object_list.length == 0 && page == 1 ? "" : nav_my === 'myself' ?
<p className="color-grey-6 mb25 edu-txt-center">选用对象你在课堂{engNameMap[object_type]}列表中已<span className="color-orange-tip">加入题库</span>{engNameMap[object_type]}</p>
:
<p className="color-grey-6 mb25 edu-txt-center">选用对象题库中已设置成公共属性的所有{engNameMap[object_type]}</p>
}
{
nav_my != 'myself' && is_teacher == false ?
<div style={{"height":"230px"}} className="d_middle edu-txt-center">
<div>
<p className="font-16 mb20 color-grey-3">通过职业认证的教师才能访问公共题库</p>
<a className="white-btn edu-orangeback-btn" target="_blank" href="/account/certification">立即认证</a>
</div>
</div>
:
object_list && object_list.length == 0 && page == 1 ?
<div className="setImgW"><NoneData></NoneData></div>
:
<div className="edu-back-skyblue padding20" style={{"height":"230px", "overflowY": 'auto'}}>
<InfiniteScroll
threshold={10}
initialLoad={false}
pageStart={0}
loadMore={() => this.fetchAll(this.state.page + 1)}
hasMore={!loading && hasMore}
useWindow={false}
>
<Checkbox.Group style={{ width: '100%' }} onChange={this.onCheckBoxChange} value={checkBoxValues}>
{ object_list && object_list.map( item => {
/**
course_list_name: "C++程序设计"
id: 3889
name: "作业3:第四章 控制结构 "
username: "Coder"
*/
return (
<p className="clearfix mb7" key={item.id}>
<Checkbox className="fl" value={item.id} key={item.id}></Checkbox>
<span className={nav_my === 'myself' ? "fl with58" : "fl with63"}>
<label className="task-hide fl" title={item.name && item.name.length > 30 ? item.name : ""}
style={{"maxWidth": "100%"}}>
<a title={item.name}>{item.name}</a>
</label>
</span>
<span title={item.course_list_name && item.course_list_name.length > 10 && item.course_list_name}
className={nav_my === 'myself' ? "fl with30 color-grey-6 task-hide pl5 bankwidth" : "fl with30 color-grey-6 task-hide pl5 bankwidth2"}
><a title={item.course_list_name}>{item.course_list_name}</a></span>
{
nav_my === 'public' &&
<span className="fl with16 color-grey-6 task-hide pl10 bankwidth76"><a
title={item.username} style={{
width: "76px"
}}>{item.username}</a></span>
}
{nav_my === "myself" ?
(this.props.object_type && this.props.object_type === "normal" ?
<a style={{textAlign: "center"}} key={item.id} className="color-blue font-14 pl5"
onClick={() => this.getotiku(`/banks/normal/${item.id}/edit/personal?tab=0`)}
>编辑</a>
: this.props.object_type && this.props.object_type === "group" ?
<a style={{textAlign: "center"}} key={item.id} className="color-blue font-14 pl5"
onClick={() => this.getotiku(`/banks/group/${item.id}/edit/publicly?tab=0`)}
>编辑</a>
: this.props.object_type && this.props.object_type === "exercise" ?
<a style={{textAlign: "center"}} key={item.id} className="color-blue font-14 pl5"
onClick={() => this.getotiku(`/banks/exercise/${item.id}/edit/personal`)}
>编辑</a>
: this.props.object_type && this.props.object_type === "poll" ?
<a style={{textAlign: "center"}} key={item.id} className="color-blue font-14 pl5"
onClick={() => this.getotiku(`/banks/poll/${item.id}/edit/personal`)}
>编辑</a>
: "")
: ""
}
</p>
)
})}
</Checkbox.Group>
{/* <p className="clearfix mb7">
<Checkbox className="fl"></Checkbox>
<span className="fl with45"><label className="task-hide fl" style={{"maxWidth":"208px;"}}>毕业设计2018开题报告题报</label></span>
<span className={nav_my===1?"fl with50 color-grey-6 task-hide pl5":"fl with30 color-grey-6 task-hide pl5"}>数据库原理数据库原理</span>
{
nav_my===2 &&
<span className="fl with16 color-grey-6 task-hide pl10">胡莎莎胡莎莎</span>
}
</p> */}
</InfiniteScroll>
</div>
}
<p style={{height: "20px", lineHeight: "20px;", marginTop: '4px'}}>
{
isChecked !="" ? <span className="color-red">{isChecked}</span>:""
}
</p>
<div className="mt10 marginauto clearfix edu-txt-center">
<a onClick={this.closeSelectBank} className="pop_close task-btn mr30">取消</a>
{object_list && object_list.length == 0 && page == 1 ?
nav_my !='myself' && is_teacher == false ?
<a onClick={this.closeSelectBank} className="task-btn task-btn-orange">确定</a>
: <a onClick={this.closeSelectBank} className="task-btn task-btn-orange">确定</a>
:<a className="task-btn task-btn-orange" id="submit_send_shixun" onClick={this.onSave}>确定</a>
}
{/*<a className="task-btn task-btn-orange" id="submit_send_shixun" onClick={this.onSave}>确定</a>*/}
</div>
</Spin>
</div>
</Modal>:""}
</a>
)
}
}
export default UseBank;
@@ -0,0 +1,196 @@
import React, { Component } from 'react';
export const STATUS_UN_PUBLISH = "未发布"
// homework_status: ["提交中", "未开启补交"]
export const STATUS_SUBMIT = "提交中"
// export const STATUS_UN_PUBLISH = "未发布"
// homework_status: ["提交中"] 未发布 未开启补交
export function RouteHOC(options = {}) {
return function wrap(WrappedComponent) {
return class Wrapper extends Component {
constructor(props) {
super(props);
this.state = {
}
}
toCreateProject = () => {
let url = '/projects/new'
if (window.location.port == 3007) {
// window.location.href
url = '/testbdweb.educoder.net/projects/new'
}
window.open(
url,
'_blank' // <- This is what makes it open in a new window.
);
}
// common_homework group_homework
// 是否是分组作业
isGroup = () => {
return window.location.pathname.indexOf('group_homeworks') != -1
}
getModuleName = (isChinese) => {
const isGroup = this.isGroup()
if (isChinese) {
let chName = isGroup ? '分组作业' : '普通作业'
return chName;
}
const secondName = isGroup ? 'group_homeworks' : 'common_homeworks'
return secondName;
}
getModuleType = () => {
const isGroup = this.isGroup()
return isGroup ? 3 : 1
}
toDetailPage = (_courseId, workId, topicId) => {
if (typeof _courseId == "object") {
const topicId = _courseId.topicId
const workId = _courseId.workId
const courseId = _courseId.coursesId
this.props.history.push(`/courses/${courseId}/boards/${workId}/messages/${topicId}`)
} else {
this.props.history.push(`/courses/${_courseId}/boards/${workId}/messages/${topicId}`)
}
}
toEditPage = (_courseId, _workId) => {
const secondName = this.getModuleName()
if (typeof _courseId == "object") {
const workId = _courseId.workId
const courseId = _courseId.coursesId
this.props.history.push(`/courses/${courseId}/${secondName}/${_workId || workId}/edit`)
} else {
this.props.history.push(`/courses/${_courseId}/${secondName}/${_workId}/edit`)
}
}
toWorkDetailPage = (_courseId, _workId, _studentWorkId) => {
const secondName = this.getModuleName()
if (typeof _courseId == "object") {
const workId = _courseId.workId
const courseId = _courseId.coursesId
const studentWorkId = _courseId.studentWorkId
window.open(`/courses/${courseId}/${secondName}/${_workId || workId}/${_studentWorkId || studentWorkId}/appraise`);
} else {
window.open(`/courses/${_courseId}/${secondName}/${_workId}/${_studentWorkId}/appraise`);
}
}
toWorkDetailPage2 = (e, _courseId, _workId, _studentWorkId) => {
console.log("鼠标中键点击了")
console.log(_studentWorkId)
const secondName = this.getModuleName()
if (typeof _courseId == "object") {
const workId = _courseId.workId
const courseId = _courseId.coursesId
const studentWorkId = _courseId.studentWorkId
window.open(`/courses/${courseId}/${secondName}/${_workId || workId}/${_studentWorkId || studentWorkId}/appraise`);
} else {
window.open(`/courses/${_courseId}/${secondName}/${_workId}/${_studentWorkId}/appraise`);
}
}
toNewPage = (courseId) => {
const secondName = this.getModuleName()
this.props.history.push(`/courses/${courseId.coursesId}/${secondName}/${courseId.category_id}/new`)
}
toListPage = (_courseId, _workId) => {
const secondName = this.getModuleName()
if (typeof _courseId == "object") {
const workId = _courseId.workId
const courseId = _courseId.coursesId
this.props.history.push(`/courses/${courseId}/${secondName}/${_workId || workId}`)
} else {
this.props.history.push(`/courses/${_courseId}/${secondName}${_workId ? '/' + _workId : ''}`)
}
}
toWorkPostPage = (_courseId, _workId, isEdit, _studentWorkId) => {
const secondName = this.getModuleName()
if (typeof _courseId == "object") {
const workId = _courseId.workId
const courseId = _courseId.coursesId
const studentWorkId = _courseId.studentWorkId
this.props.history.push(`/courses/${courseId}/${secondName}/${_workId || workId}/${isEdit? `${_studentWorkId || studentWorkId}/post_edit` : 'post'}`)
} else {
this.props.history.push(`/courses/${_courseId}/${secondName}/${_workId}/${isEdit? `${_studentWorkId}/post_edit` : 'post'}`)
}
}
toWorkListPage = (_courseId, _workId) => {
const secondName = this.getModuleName()
if (typeof _courseId == "object") {
const workId = _courseId.workId
const courseId = _courseId.coursesId
this.props.history.push(`/courses/${courseId}/${secondName}/${_workId || workId}/list`)
} else {
this.props.history.push(`/courses/${_courseId}/${secondName}/${_workId}/list`)
}
}
toWorkAnswerPage = (_courseId, _workId) => {
const secondName = this.getModuleName()
if (typeof _courseId == "object") {
const workId = _courseId.workId
const courseId = _courseId.coursesId
this.props.history.push(`/courses/${courseId}/${secondName}/${workId}/answer`)
} else {
this.props.history.push(`/courses/${_courseId}/${secondName}/${_workId}/answer`)
}
}
toWorkQuestionPage = (_courseId, _workId) => {
const secondName = this.getModuleName()
if (typeof _courseId == "object") {
const workId = _workId || _courseId.workId
const courseId = _courseId.coursesId
this.props.history.push(`/courses/${courseId}/${secondName}/${workId}/question`)
} else {
this.props.history.push(`/courses/${_courseId}/${secondName}/${_workId}/question`)
}
}
toWorkSettingPage = (_courseId, _workId) => {
const secondName = this.getModuleName()
if (typeof _courseId == "object") {
const workId = _courseId.workId
const courseId = _courseId.coursesId
this.props.history.push(`/courses/${courseId}/${secondName}/${_workId || workId}/setting`)
} else {
this.props.history.push(`/courses/${_courseId}/${secondName}/${_workId}/setting`)
}
}
render() {
const { snackbarOpen} = this.state;
return (
<React.Fragment>
<WrappedComponent {...this.props}
toDetailPage={this.toDetailPage}
toEditPage={this.toEditPage}
toNewPage={this.toNewPage}
toListPage={this.toListPage}
toWorkDetailPage={this.toWorkDetailPage}
toWorkDetailPage2={this.toWorkDetailPage2}
toWorkPostPage={this.toWorkPostPage}
toWorkListPage={this.toWorkListPage}
toWorkAnswerPage={this.toWorkAnswerPage}
toWorkQuestionPage={this.toWorkQuestionPage}
toWorkSettingPage={this.toWorkSettingPage}
toCreateProject={this.toCreateProject}
isGroup={this.isGroup}
getModuleName={this.getModuleName}
getModuleType={this.getModuleType}
>
</WrappedComponent>
</React.Fragment>
)
}
}
}
}
@@ -0,0 +1,22 @@
import React,{Component} from "react";
export default function LeaderIcon(props = {}) {
let icon = null;
const { className, style } = props;
const _className = `font-8 blueFull Actionbtn ${className}`
if (props.small) {
icon = <div className={_className} style={{
height: '14px',
'line-height': '14px',
// width: '24px',
transform: 'scale(0.833)',
padding: '0px 5px',
'margin-top': '-2px',
'margin-left': '2px',
'vertical-align': 'middle', }}>组长</div>
} else {
icon = <div className={_className} style={{ height: '16px', 'line-height': '16px', transform: 'scale(0.833)'}}>组长</div>
}
return icon
}
@@ -0,0 +1,197 @@
import React,{Component} from "react";
import { Form, Select, Input, Button,Checkbox,Icon,message,Modal, Table, Divider, Tag,DatePicker,Radio,Tooltip} from "antd";
import {Link} from 'react-router-dom';
import { WordsBtn, getRandomcode } from 'educoder';
import axios from 'axios';
import PublishRightnow from '../PublishRightnow'
import AccessoryModal from "../../coursesPublic/AccessoryModal";
import DownloadMessageysl from "../../../modals/DownloadMessageysl";
const { Option} = Select;
const CheckboxGroup = Checkbox.Group;
const confirm = Modal.confirm;
let GraduationTasksnewtype=true;
const $ = window.$;
const Search = Input.Search;
const RadioGroup = Radio.Group;
class TabRightComponents extends Component{
constructor(props){
super(props)
this.publishModal = React.createRef();
this.endModal = React.createRef();
this.state={
accessoryVisible: false,
DownloadType:false,
DownloadMessageval:undefined,
}
}
componentDidMount() {
}
onToPublishClick = () => {
}
// 补交附件
Cancelvisible=()=>{
this.setState({
accessoryVisible:false
})
}
/// 确认是否下载
confirmysl(url){
axios.get(url + '&export=true' ).then((response) => {
if(response.data.status&&response.data.status===-1){
}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
})
}
}else {
this.props.slowDownload(getRandomcode(url));
// this.props.showNotification(`正在下载中`);
// window.open("/api"+url, '_blank');
}
}).catch((error) => {
console.log(error)
});
}
Downloadcal=()=>{
this.setState({
DownloadType:false,
DownloadMessageval:undefined
})
}
addAccessory=()=>{
this.setState({
accessoryVisible:true
})
}
setupdate = () => {
}
render(){
const dateFormat = 'YYYY-MM-DD HH:mm';
const { accessoryVisible } = this.state
let { work_statuses, publish_immediately, work_id
, end_immediately
} =this.props;
let courseId=this.props.match.params.coursesId;
let category_id=this.props.match.params.category_id;
let workId=this.props.match.params.workId;
const isGroup = this.props.isGroup()
const moduleName = !isGroup? "普通作业":"分组作业";
const moduleEngName = this.props.getModuleName()
const childModuleName = this.props.moduleName
const isAdmin = this.props.isAdmin()
const isSuperAdmin = this.props.isSuperAdmin()
let exportUrls = `/api/homework_commons/${workId}/works_list.zip`
const exportResultUrls = `/api/homework_commons/${workId}/works_list.xlsx`
return(
<React.Fragment>
{isAdmin ?
<React.Fragment>
<div style={{display: 'inline', float: 'right'}}>
<PublishRightnow ref={this.publishModal} showActionButton={false} {...this.props} checkBoxValues={[workId]}
isPublish={true} doWhenSuccess={this.props.doWhenSuccess} checkBeforePost={this.props.saveWorkSetting}
onToPublishClick={this.onToPublishClick}
></PublishRightnow>
<PublishRightnow ref={this.endModal} showActionButton={false} {...this.props} checkBoxValues={[workId]}
isPublish={false} doWhenSuccess={this.props.doWhenSuccess}></PublishRightnow>
</div>
<DownloadMessageysl
{...this.props}
value={this.state.DownloadMessageval}
modalCancel={this.Downloadcal}
modalsType={this.state.DownloadType}
/>
<style>{`
.drop_down_menu li a {
padding: 0px;
font-size: 14px;
color: #333;
}
.drop_down_menu {
width: 121px;
}
.drop_down_menu li {
overflow: visible;
width: 121px;
}
.drop_down_menu, .drop_down_normal {
padding-top: 10px;
padding-bottom: 8px;
}
`}</style>
{this.props.isAdmin()? <li className="li_line drop_down fr color-blue font-16 mt20" style={{"padding":"0 20px"}}>
导出<i className="iconfont icon-xiajiantou font-12 ml2"></i>
<ul className="drop_down_menu" style={{"right":"-34px","left":"unset","height":"auto"}}>
<li><a onClick={(url)=>this.confirmysl(exportResultUrls)} className="color-dark">导出成绩</a></li>
<li><a onClick={(url)=>this.confirmysl(exportUrls)} className="color-dark">导出作品附件</a></li>
</ul>
</li>:""}
{/* <a className={"fr color-blue font-16"} href={exportUrl}></a>
<a className={"fr color-blue font-16"} href={exportResultUrl}>导出成绩</a> */}
{/*<a className={"fr color-blue font-16"}>导出</a>*/}
{end_immediately && <a className={"fr color-blue font-16"} onClick={() => { this.endModal.current.open() } }>立即截止</a>}
{publish_immediately && <a className={"fr color-blue font-16"} onClick={() => { this.publishModal.current.open() } } >立即发布</a>}
{/*<a className={"fr color-blue font-16"}>项目在线质量检测</a>*/}
{isAdmin && <a className={"fr color-blue font-16"} onClick={() => this.props.toEditPage(this.props.match.params, workId)}>编辑作业</a>}
</React.Fragment> :
<React.Fragment>
{work_statuses && work_statuses.indexOf('提交作品') != -1 && <a className={"fr color-blue font-16"} href={"javascript:void(0)"}
onClick={() => { this.props.toWorkPostPage(this.props.match.params)}}
>提交作品</a>}
{work_statuses && work_statuses.indexOf('修改作品') != -1 && <a className={"fr color-blue font-16"} href={"javascript:void(0)"}
onClick={() => { this.props.toWorkPostPage(this.props.match.params, null, true, work_id)}}
>修改作品</a>}
{work_statuses && work_statuses.indexOf('补交附件') != -1 &&
<React.Fragment>
<AccessoryModal
{...this.props}
modalname={"补交附件"}
visible={accessoryVisible}
Cancelname={"取消"}
Savesname={"确认"}
Cancel={this.Cancelvisible}
setupdate={this.setupdate}
reviseAttachmentUrl={`/student_works/${work_id}/revise_attachment.json`}
/>
<a className={"fr color-blue font-16"} href={"javascript:void(0)"}
onClick={this.addAccessory}
>补交附件</a>
</React.Fragment>
}
</React.Fragment> }
</React.Fragment>
)
}
}
export default TabRightComponents;
@@ -0,0 +1,172 @@
import React,{Component} from "react";
import { Form, Select, Input, Button,Checkbox,Upload,Icon,message,Modal, Table, Divider, Tag,DatePicker,Radio,Tooltip} from "antd";
import {Link} from 'react-router-dom';
import locale from 'antd/lib/date-picker/locale/zh_CN';
import { WordsBtn, MarkdownToHtml } from 'educoder';
import axios from 'axios';
import Modals from '../../../modals/Modals';
import CoursesListType from '../../coursesPublic/CoursesListType';
import AccessoryModal from "../../coursesPublic/AccessoryModal";
import '../../css/Courses.css'
import CBreadcrumb from '../../common/CBreadcrumb'
const { Option} = Select;
const CheckboxGroup = Checkbox.Group;
const confirm = Modal.confirm;
let GraduationTasksnewtype=true;
const $ = window.$;
const Search = Input.Search;
const RadioGroup = Radio.Group;
class WorkDetailPageHeader extends Component{
constructor(props){
super(props)
this.state={
}
}
componentDidMount() {
}
goback = () => {
let workId=this.props.match.params.workId;
if ( window.location.pathname.indexOf('appraise') == -1) {
let category_id= this.props.category.category_id;
this.props.toListPage(this.props.match.params, category_id)
} else {
this.props.toWorkListPage(this.props.match.params, workId)
// this.props.match.params.category_id
}
// this.props.history.goBack()
}
// 补交附件
Cancelvisible=()=>{
this.setState({
accessoryVisible:false
})
}
addAccessory=()=>{
this.setState({
accessoryVisible:true
})
}
setupdate = () => {
this.props.getReviseAttachments && this.props.getReviseAttachments()
}
render(){
const dateFormat = 'YYYY-MM-DD HH:mm';
let {course_name, homework_name, search, page, loadingstate, homework_status, description, noTab
, view_answer, author_name, category, work_id, current_user
} =this.props;
let courseId=this.props.match.params.coursesId;
let category_id= category && category.category_id;
let category_name= category && category.category_name;
let workId=this.props.match.params.workId;
const studentWorkId = this.props.match.params.studentWorkId
const isGroup = this.props.isGroup()
const moduleName = !isGroup? "普通作业":"分组作业";
const moduleEngName = this.props.getModuleName()
const childModuleName = this.props.moduleName
return(
<React.Fragment>
<div>
<div className="newMain clearfix worklist1 workDetailPageHeader">
<div className={"educontent mt20"}>
<style>{`
.memoContent {
background: #fff;
}
.workDetailPageHeader .summaryname {
line-height:28px
}
`}</style>
<CBreadcrumb items={[
{ to: current_user&&current_user.first_category_url, name: course_name},
{ to: `/courses/${courseId}/${moduleEngName}/${category_id}`, name: category_name },
window.location.pathname.indexOf('appraise') == -1 ? { } : { to: `/courses/${courseId}/${moduleEngName}/${workId}/list`, name: '作业详情' },
// 1. 与上一条联动,当匿评他人作品时,TA人作品的作者真实姓名切换为“匿名”
window.location.pathname.indexOf('appraise') == -1 ? { name: '作业详情' } : { name: author_name },
// window.location.pathname.indexOf('appraise') == -1 ? { } : { to: `/courses/${courseId}/${moduleEngName}/${workId}/list`, name: '作品列表' },
// { name: childModuleName }
]}></CBreadcrumb>
<div className="clearfix mt20 mb20" >
<span className=" fl color-black summaryname">
{homework_name}
{/* <Link to={"/courses/"+courseId+"/graduation"+"/graduation_tasks/"}>{homework_name}</Link> */}
</span>
<CoursesListType
typelist={homework_status}
typesylename={"mt3"}
/>
{category && <a className="color-grey-6 fr font-16 ml30 mr30 lineh-25" onClick={this.goback}>返回</a>}
{this.props.update_atta &&
<React.Fragment>
{this.state.accessoryVisible===true? <AccessoryModal
{...this.props}
modalname={"补交附件"}
visible={this.state.accessoryVisible}
Cancelname={"取消"}
Savesname={"确认"}
Cancel={this.Cancelvisible}
setupdate={this.setupdate}
seeworks={undefined}
reviseAttachmentUrl={`/student_works/${work_id || studentWorkId}/revise_attachment.json`}
/>:""}
<a className={"fr color-blue font-16 "} href={"javascript:void(0)"}
onClick={this.addAccessory} style={{ 'marginTop': '-4px' }}
>补交附件</a>
</React.Fragment>
}
</div>
{ noTab !== true && <div className="stud-class-set bor-bottom-greyE">
<div className="mt10 clearfix edu-back-white poll_list pl5">
<Link
className={`${childModuleName == '作品列表' ? 'active' : '' } `}
to={`/courses/${courseId}/${moduleEngName}/${workId}/list`}>作品列表</Link>
<Link
className={`${childModuleName == '作业描述' ? 'active' : '' } `}
to={`/courses/${courseId}/${moduleEngName}/${workId}/question`}>作业描述</Link>
{view_answer == true && <Link
className={`${childModuleName == '参考答案' ? 'active' : '' } `}
to={`/courses/${courseId}/${moduleEngName}/${workId}/answer`}>参考答案</Link>}
<Link
className={`${childModuleName == '设置' ? 'active' : '' } `}
style={{paddingLeft:this.props.isAdmin()?'38px':'20px'}}
to={`/courses/${courseId}/${moduleEngName}/${workId}/setting`}>{this.props.isAdmin()?"设置":"得分规则"}</Link>
{ this.props.tabRightComponents }
</div>
</div> }
{/* 内容区 */}
{this.props.children}
</div>
</div>
</div>
</React.Fragment>
)
}
}
export default WorkDetailPageHeader;

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