Merge branch 'dev_aliyun' of https://bdgit.educoder.net/Hjqreturn/educoder into dev_aliyun

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
dev_cs_new
杨树明 6 years ago
commit 82c990e60d

@ -0,0 +1,529 @@
import React, {Component} from 'react';
import logo from './logo.svg';
import './App.css';
import {LocaleProvider} from 'antd'
import zhCN from 'antd/lib/locale-provider/zh_CN';
import {
BrowserRouter as Router,
Route,
Switch
} from 'react-router-dom';
import axios from 'axios';
import '@icedesign/base/dist/ICEDesignBase.css';
import '@icedesign/base/index.scss';
import LoginDialog from './modules/login/LoginDialog';
import Notcompletedysl from './modules/user/Notcompletedysl';
import Trialapplicationysl from './modules/login/Trialapplicationysl';
import Trialapplicationreview from './modules/user/Trialapplicationreview';
import Addcourses from "./modules/courses/coursesPublic/Addcourses";
import AccountProfile from"./modules/user/AccountProfile";
import Trialapplication from './modules/login/Trialapplication'
import NotFoundPage from './NotFoundPage'
import Loading from './Loading'
import Loadable from 'react-loadable';
import moment from 'moment'
import {MuiThemeProvider, createMuiTheme} from 'material-ui/styles';
// import './AppConfig'
import history from './history';
import {SnackbarHOC} from 'educoder'
import {initAxiosInterceptors} from './AppConfig'
// tpi需要这个来加载css
import {TPMIndexHOC} from './modules/tpm/TPMIndexHOC';
const theme = createMuiTheme({
palette: {
primary: {
main: '#4CACFF',
contrastText: 'rgba(255, 255, 255, 0.87)'
},
secondary: {main: '#4CACFF'}, // #11cb5f This is just green.A700 as hex.
},
});
//
// const Trialapplication= Loadable({
// loader: () =>import('./modules/login/Trialapplication'),
// loading:Loading,
// })
//登入
const EducoderLogin = Loadable({
loader: () => import('./modules/login/EducoderLogin'),
loading: Loading,
})
const TestIndex = Loadable({
loader: () => import('./modules/test'),
loading: Loading,
})
const IndexWrapperComponent = Loadable({
loader: () => import('./modules/page/IndexWrapper'),
loading: Loading,
})
const CommentComponent = Loadable({
loader: () => import('./modules/comment/CommentContainer'),
loading: Loading,
})
const TestMaterialDesignComponent = Loadable({
loader: () => import('./modules/test/md/TestMaterialDesign'),
loading: Loading,
})
const TestCodeMirrorComponent = Loadable({
loader: () => import('./modules/test/codemirror/TestCodeMirror'),
loading: Loading,
})
const TestComponent = Loadable({
loader: () => import('./modules/test/TestRC'),
loading: Loading,
})
const TestUrlQueryComponent = Loadable({
loader: () => import('./modules/test/urlquery/TestUrlQuery'),
loading: Loading,
})
const TPMIndexComponent = Loadable({
loader: () => import('./modules/tpm/TPMIndex'),
loading: Loading,
})
const TPMShixunsIndexComponent = Loadable({
loader: () => import('./modules/tpm/shixuns/ShixunsIndex'),
loading: Loading,
})
//实训课程(原实训路径)
const ShixunPaths = Loadable({
loader: () => import('./modules/paths/Index'),
loading: Loading,
})
//在线课堂
const CoursesIndex = Loadable({
loader: () => import('./modules/courses/Index'),
loading: Loading,
})
const SearchPage = Loadable({
loader: () => import('./search/SearchPage'),
loading: Loading,
})
//教学案例
const MoopCases = Loadable({
loader: () => import('./modules/moop_cases/index'),
loading: Loading,
})
// 课堂讨论
// const BoardIndex = Loadable({
// loader: () => import('./modules/courses/boards/BoardIndex'),
// loading:Loading,
// })
// //课堂普通作业&分组作业
// const CoursesWorkIndex = Loadable({
// loader: () => import('./modules/courses/busyWork/Index'),
// loading:Loading,
// })
//
// const TPMShixunchildIndexComponent = Loadable({
// loader: () => import('./modules/tpm/shixunchild/ShixunChildIndex'),
// loading: Loading,
// })
// const TPMshixunfork_listIndexComponent = Loadable({
// loader: () => import('./modules/tpm/shixunchild/Shixunfork_list'),
// loading: Loading,
// })
const ForumsIndexComponent = Loadable({
loader: () => import('./modules/forums/ForumsIndex'),
loading: Loading,
})
// trustie plus forum
// const TPForumsIndexComponent = Loadable({
// loader: () => import('./modules/tp-forums/TPForumsIndex'),
// loading: Loading,
// })
// const TestPageComponent = Loadable({
// loader: () => import('./modules/page/Index'),
// loading: Loading,
// })
//新建实训
const Newshixuns = Loadable({
loader: () => import('./modules/tpm/newshixuns/Newshixuns'),
loading: Loading,
})
//实训首页
const ShixunsHome = Loadable({
loader: () => import('./modules/home/shixunsHome'),
loading: Loading,
})
const CompatibilityPageLoadable = Loadable({
loader: () => import('./modules/common/CompatibilityPage'),
loading: Loading,
})
//403页面
const Shixunauthority = Loadable({
loader: () => import('./modules/403/Shixunauthority'),
loading: Loading,
})
//404页面
const Shixunnopage = Loadable({
loader: () => import('./modules/404/Shixunnopage'),
loading: Loading,
})
//500页面
const http500 = Loadable({
loader: () => import('./modules/500/http500'),
loading: Loading,
})
// 登录注册
const LoginRegisterPage = Loadable({
loader: () => import('./modules/user/LoginRegisterPage'),
loading: Loading,
})
const AccountPage = Loadable({
loader: () => import('./modules/user/AccountPage'),
loading: Loading,
})
// 个人主页
const UsersInfo = Loadable({
loader: () => import('./modules/user/usersInfo/Infos'),
loading: Loading,
})
// 兴趣页面
const Interestpage = Loadable({
loader: () => import('./modules/login/EducoderInteresse'),
loading: Loading,
})
//众包创新
const ProjectPackages=Loadable({
loader: () => import('./modules/projectPackages/ProjectPackageIndex'),
loading: Loading,
})
class App extends Component {
constructor(props) {
super(props)
// this.state = {
// isRenders:false,
// }
}
componentDidMount() {
// force an update if the URL changes
history.listen(() => {
this.forceUpdate()
const $ = window.$
// https://www.trustie.net/issues/21919 可能会有问题
$("html").animate({ scrollTop: $('html').scrollTop() - 0 })
});
initAxiosInterceptors(this.props)
//
// axios.interceptors.response.use((response) => {
// // console.log("response"+response);
// if(response!=undefined)
// // console.log("response"+response.data.statu);
// if (response&&response.data.status === 407) {
// this.setState({
// isRenders: true,
// })
// }
// return response;
// }, (error) => {
// //TODO 这里如果样式变了会出现css不加载的情况
// });
}
//修改登录方法
Modifyloginvalue=()=>{
this.setState({
isRender:false,
})
}
render() {
return (
<LocaleProvider locale={zhCN}>
<MuiThemeProvider theme={theme}>
<LoginDialog {...this.props} {...this.state} Modifyloginvalue={()=>this.Modifyloginvalue()}></LoginDialog>
<Notcompletedysl {...this.props} {...this.state}></Notcompletedysl>
<Trialapplicationysl {...this.props} {...this.state}></Trialapplicationysl>
<Trialapplicationreview {...this.props} {...this.state}></Trialapplicationreview>
<Addcourses {...this.props} {...this.state}/>
<AccountProfile {...this.props} {...this.state}/>
{/*{*/}
{/* isRender === true?*/}
{/* <LoginDialog></LoginDialog> : ""*/}
{/*}*/}
{/*{*/}
{/* isRenders === true?*/}
{/*<Trialapplication></Trialapplication>*/}
{/*:""*/}
{/*}*/}
<Router>
<Switch>
{/*<Route path="/login" component={LoginRegisterPage}/>*/}
{/*众包创新*/}
<Route path={"/crowdsourcings"} component={ProjectPackages}/>
{/*认证*/}
<Route path="/account" component={AccountPage}/>
{/*403*/}
<Route path="/403" component={Shixunauthority}/>
<Route path="/500" component={http500}/>
{/*404*/}
<Route path="/nopage" component={Shixunnopage}/>
<Route path="/compatibility" component={CompatibilityPageLoadable}/>
<Route
path="/login" component={EducoderLogin}
/>
<Route
path="/register" component={EducoderLogin}
/>
<Route path="/users/:username"
render={
(props) => (<UsersInfo {...this.props} {...props} {...this.state} />)
}></Route>
{/*<Route*/}
{/* path="/trialapplication" component={Trialapplication}*/}
{/*/>*/}
<Route
path="/changepassword" component={EducoderLogin}
/>
<Route
path="/interesse" component={Interestpage}
/>
<Route path="/shixuns/new" component={Newshixuns}>
</Route>
<Route path="/tasks/:stageId" component={IndexWrapperComponent}/>
<Route path="/shixuns/:shixunId" component={TPMIndexComponent}>
</Route>
{/*列表页*/}
<Route path="/shixuns" component={TPMShixunsIndexComponent}/>
{/* <Route path="/shixunchild" component={TPMShixunchildIndexComponent}>
</Route>
<Route path="/fork_list" component={TPMshixunfork_listIndexComponent}>
</Route> */}
{/*<Route path="/forums" component={ForumsIndexComponent}>*/}
{/*</Route>*/}
{/*实训课程(原实训路径)*/}
<Route path="/paths" component={ShixunPaths}></Route>
<Route path="/search"
render={
(props)=>(<SearchPage {...this.props} {...props} {...this.state}></SearchPage>)
}
></Route>
{/*课堂*/}
<Route path="/courses" component={CoursesIndex} {...this.props}></Route>
{/* 课堂讨论 */}
{/* <Route path="/board" component = {BoardIndex} {...this.props}></Route> */}
{/* <Route path="/tpforums" component={TPForumsIndexComponent}>
</Route> */}
{/* <Route path="/myshixuns/:shixunId/stages/:stageId" component={Index}/> */}
{/* 兴趣页面*/}
{/*<Route path="/interest" component={Interestpage}/>*/}
<Route path="/comment" component={CommentComponent}/>
<Route path="/testMaterial" component={TestMaterialDesignComponent}/>
<Route path="/test" component={TestIndex}/>
<Route path="/testCodeMirror" component={TestCodeMirrorComponent}/>
<Route path="/testRCComponent" component={TestComponent}/>
<Route path="/testUrlQuery" component={TestUrlQueryComponent}/>
{/* 教学案例 */}
<Route path="/moop_cases"render={
(props) => (<MoopCases {...this.props} {...props} {...this.state} />)
}/>
{/* <Route component={NotFoundPage}/> */}
{/*列表页*/}
{/*<Route component={TPMShixunsIndexComponent}/>*/}
{/*首页*/}
<Route exact path="/" component={ShixunsHome}/>
<Route component={Shixunnopage}/>
{/*<Route component={ShixunsHome}/>*/}
</Switch>
</Router>
</MuiThemeProvider>
</LocaleProvider>
);
}
}
// moment国际化设置为中文
moment.defineLocale('zh-cn', {
months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'),
monthsShort: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
weekdaysShort: '周日_周一_周二_周三_周四_周五_周六'.split('_'),
weekdaysMin: '日_一_二_三_四_五_六'.split('_'),
longDateFormat: {
LT: 'Ah点mm分',
LTS: 'Ah点m分s秒',
L: 'YYYY-MM-DD',
LL: 'YYYY年MMMD日',
LLL: 'YYYY年MMMD日Ah点mm分',
LLLL: 'YYYY年MMMD日ddddAh点mm分',
l: 'YYYY-MM-DD',
ll: 'YYYY年MMMD日',
lll: 'YYYY年MMMD日Ah点mm分',
llll: 'YYYY年MMMD日ddddAh点mm分'
},
meridiemParse: /凌晨|早上|上午|中午|下午|晚上/,
meridiemHour: function (hour, meridiem) {
if (hour === 12) {
hour = 0;
}
if (meridiem === '凌晨' || meridiem === '早上' ||
meridiem === '上午') {
return hour;
} else if (meridiem === '下午' || meridiem === '晚上') {
return hour + 12;
} else {
// '中午'
return hour >= 11 ? hour : hour + 12;
}
},
meridiem: function (hour, minute, isLower) {
var hm = hour * 100 + minute;
if (hm < 600) {
return '凌晨';
} else if (hm < 900) {
return '早上';
} else if (hm < 1130) {
return '上午';
} else if (hm < 1230) {
return '中午';
} else if (hm < 1800) {
return '下午';
} else {
return '晚上';
}
},
calendar: {
sameDay: function () {
return this.minutes() === 0 ? '[今天]Ah[点整]' : '[今天]LT';
},
nextDay: function () {
return this.minutes() === 0 ? '[明天]Ah[点整]' : '[明天]LT';
},
lastDay: function () {
return this.minutes() === 0 ? '[昨天]Ah[点整]' : '[昨天]LT';
},
nextWeek: function () {
var startOfWeek, prefix;
startOfWeek = moment().startOf('week');
prefix = this.unix() - startOfWeek.unix() >= 7 * 24 * 3600 ? '[下]' : '[本]';
return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm';
},
lastWeek: function () {
var startOfWeek, prefix;
startOfWeek = moment().startOf('week');
prefix = this.unix() < startOfWeek.unix() ? '[上]' : '[本]';
return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm';
},
sameElse: 'LL'
},
ordinalParse: /\d{1,2}(日|月|周)/,
ordinal: function (number, period) {
switch (period) {
case 'd':
case 'D':
case 'DDD':
return number + '日';
case 'M':
return number + '月';
case 'w':
case 'W':
return number + '周';
default:
return number;
}
},
relativeTime: {
future: '%s内',
past: '%s前',
s: '几秒',
m: '1分钟',
mm: '%d分钟',
h: '1小时',
hh: '%d小时',
d: '1天',
dd: '%d天',
M: '1个月',
MM: '%d个月',
y: '1年',
yy: '%d年'
},
week: {
// GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
dow: 1, // Monday is the first day of the week.
doy: 4 // The week that contains Jan 4th is the first week of the year.
}
});
export default SnackbarHOC()(App);

@ -190,7 +190,7 @@ class ApplicationController < ActionController::Base
def check_account
if !current_user.profile_completed?
info_url = '/account/profile'
tip_exception(402, info_url)
tip_exception(402, '')
end
end

@ -298,7 +298,13 @@ class CoursesController < ApplicationController
@applications = CourseMessage.unhandled_join_course_requests_by_course(@course).
joins("join users on course_messages.course_message_id=users.id").
where("LOWER(concat(users.lastname, users.firstname)) LIKE ?", "%#{search_str}%")
@teacher_list_size = @course.teachers.size
if @course.try(:id) != 1309 || current_user.admin? || current_user.try(:id) == 15582
teacher_list = @course.course_members.where("course_members.role in (1, 2, 3)")
else
teacher_list = @course.course_members.where("(course_members.role in (1, 3) or (course_members.user_id = #{current_user.id}
and course_members.role = 2))")
end
@teacher_list_size = teacher_list.size
@applications_size = CourseMessage.unhandled_join_course_requests_by_course(@course).size
@is_admin = @user_course_identity < Course::PROFESSOR
end
@ -924,16 +930,23 @@ class CoursesController < ApplicationController
course_message = CourseMessage.new(course_id: course.id, user_id: course.tea_id, status: 0,
course_message_id: current_user.id, course_message_type: "JoinCourseRequest",
viewed: false)
course_message.content = 2 if params[:professor].present? && params[:professor].to_i == 1
course_message.content = 3 if params[:assistant_professor].present? && params[:assistant_professor].to_i == 1
if params[:professor].present? && params[:professor].to_i == 1
course_message.content = 2
role = 9
message = "教师申请已提交,请等待审核"
else
course_message.content = 3
role = 7
message = "助教申请已提交,请等待审核"
end
course_message.save!
role = course_message.content == 2 ? '9' : '7' # 7:助教 9:教师
# role = course_message.content == 2 ? '9' : '7' # 7:助教 9:教师
ApplyTeacherRoleJoinCourseNotifyJob.perform_later(current_user.id, course.id, role)
message = "#{course_message.content == 2 ? '教师' : '助教'}申请已提交,请等待审核"
# message = "#{course_message.content == 2 ? '教师' : '助教'}申请已提交,请等待审核"
else
message = "#{existing_course_message.content == 2 ? '教师' : '助教'}申请已提交,请等待审核"
message = "#{existing_course_message.content == '2' ? '教师' : '助教'}申请已提交,请等待审核"
end
else
message = "您已是课堂成员"

@ -703,12 +703,14 @@ class ExercisesController < ApplicationController
end
if ex_status == 1 #如果试卷存在已发布的,或者是已截止的,那么则直接跳过
g_course = params[:group_ids] #表示是否传入分班参数,如果传入分班的参数,那么试卷的统一设置需修改
tiding_group_ids = g_course
if g_course
user_course_groups = @course.charge_group_ids(current_user)
if g_course.map(&:to_i).sort == user_course_groups.sort # 如果是设置为全部班级,则试卷不用分组,且试卷设定为统一设置,否则则分组设置
exercise.exercise_group_settings.destroy_all
ex_unified = true
e_time = ex_end_time
tiding_group_ids = []
else
ex_unified = false
g_course.each do |i|
@ -748,7 +750,7 @@ class ExercisesController < ApplicationController
if exercise.course_acts.size == 0
exercise.course_acts << CourseActivity.new(:user_id => exercise.user_id,:course_id => exercise.course_id)
end
ExercisePublishNotifyJob.perform_later(exercise.id, g_course)
ExercisePublishNotifyJob.perform_later(exercise.id, tiding_group_ids)
end
end
end
@ -1084,6 +1086,7 @@ class ExercisesController < ApplicationController
:subjective_score => subjective_score
}
@answer_committed_user.update_attributes(commit_option)
CommitExercsieNotifyJobJob.perform_later(@exercise.id, current_user.id)
normal_status(0,"试卷提交成功!")
end
rescue Exception => e

@ -38,7 +38,7 @@ class LibrariesController < ApplicationController
def create
library = current_user.libraries.new
Libraries::SaveService.call(library, current_user, save_params)
render_ok
render_ok({id: library.id})
rescue Libraries::SaveService::Error => ex
render_error(ex.message)
end
@ -47,7 +47,7 @@ class LibrariesController < ApplicationController
return render_forbidden unless library_manageable?(current_library)
Libraries::SaveService.call(current_library, current_user, save_params)
render_ok
render_ok({id: current_library.id})
rescue Libraries::SaveService::Error => ex
render_error(ex.message)
end

@ -155,7 +155,7 @@ class MemosController < ApplicationController
@reply.root_id = memo.root_id || memo.id
memo.children << @reply
m = Memo.find_by!(id: @reply.root_id)
m.increment!(:all_replies_count)
m.update_attributes!(all_replies_count: m.all_replies_count + 1)
rescue Exception => e
tip_exception("回复失败,原因:#{e}")
raise ActiveRecord::Rollback
@ -191,7 +191,7 @@ class MemosController < ApplicationController
tip_exception("话题名称不能为空") if params[:subject].blank?
tip_exception("话题内容不能为空") if params[:content].blank?
tip_exception("话题类型不能为空") if params[:forum_id].blank?
tip_exception("技术标签不能为空") if params[:tags].blank?
tip_exception("技术标签不能为空") if params[:forum_id].to_i == 5 && params[:tags].blank?
end
end

@ -265,9 +265,10 @@ class MyshixunsController < ApplicationController
# params[:evaluate] 实训评测时更新必须给的参数,需要依据该参数做性能统计,其它类型的更新可以跳过
# 自动保存的时候evaluate为0点评测的时候为1
if params[:evaluate] == 1
exec_time = game.challenge.try(:exec_time)
@sec_key = generate_identifier(EvaluateRecord, 12)
record = EvaluateRecord.create!(:user_id => current_user.id, :shixun_id => @myshixun.shixun_id, :game_id => game_id,
:identifier => @sec_key)
:identifier => @sec_key, :exec_time => exec_time)
uid_logger("-- game build: file update #{@sec_key}, record id is #{record.id}, time is **** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
end
unless @hide_code

@ -919,6 +919,7 @@ class PollsController < ApplicationController
:end_at => Time.now
}
poll_user_current.update_attributes(poll_user_params)
CommitPollNotifyJobJob.perform_later(@poll.id, current_user.id)
normal_status(0, "问卷提交成功!")
end
## 需添加发送消息的接口,稍后添加

@ -164,7 +164,7 @@ class ShixunsController < ApplicationController
@new_shixun = Shixun.new
@new_shixun.attributes = @shixun.attributes.dup.except("id","user_id","visits","gpid","status", "identifier", "averge_star",
"homepage_show","repo_name", "myshixuns_count", "challenges_count",
"can_copy")
"can_copy", "created_at", "updated_at")
@new_shixun.user_id = User.current.id
@new_shixun.averge_star = 5
@new_shixun.identifier = generate_identifier Shixun, 8
@ -782,8 +782,8 @@ class ShixunsController < ApplicationController
school_name = "%#{params[:school_name].to_s.strip}%"
if user_name.present? || school_name.present?
@users = User.joins(user_extension: :school).where("users.id not in #{member_ids} AND users.status = 1 AND
(LOWER(users.lastname) LIKE ? or users.phone like ?) AND LOWER(schools.name) LIKE
?", user_name, user_name, school_name)
(LOWER(concat(users.lastname, users.firstname)) LIKE ? or users.phone like ?)
AND LOWER(schools.name) LIKE ?", user_name, user_name, school_name)
else
@users = User.none
end

@ -360,7 +360,7 @@ class StudentWorksController < ApplicationController
Tiding.create(user_id: @work.user_id, trigger_user_id: User.current.id, container_id: new_score.id,
container_type: "StudentWorksScore", parent_container_id: @work.id,
parent_container_type: "HomeworkCommon", belong_container_id: @homework.course_id,
belong_container_type: "Course", viewed: 0, tiding_type: "HomeworkCommon", extra: new_score.reviewer_role)
belong_container_type: "Course", viewed: 0, tiding_type: new_score.reviewer_role == 3 ? "System" : "HomeworkCommon", extra: new_score.reviewer_role)
case new_score.reviewer_role
when 1 #教师评分:最后一个教师评分为最终评分

@ -9,6 +9,8 @@ class Users::PrivateMessagesController < Users::BaseController
query = "SELECT subquery.*, COUNT(*) message_count FROM (#{subquery}) subquery "\
"GROUP BY subquery.target_id ORDER BY subquery.send_time desc LIMIT #{limit_value} OFFSET #{offset_value}"
@messages = PrivateMessage.select('*').from("(#{query}) AS query").includes(target: :user_extension)
observed_user.private_messages.only_unread.update_all(status: 1)
end
def create

@ -1,5 +1,5 @@
module CourseDecorator
def can_visited?
is_public == 1 || User.current.admin? || User.current.member_of_course?(self)
is_public == 1 || User.current.admin_or_business? || User.current.member_of_course?(self)
end
end

@ -47,13 +47,13 @@ module TidingDecorator
# ================ 各种类消息内容方法 ================
def apply_user_authentication_content
return if trigger_user_id.zero?
t_user = trigger_user || User.find(1)
if tiding_type == 'Apply'
str1, str2 = if container.auth_type == 1
[trigger_user.show_real_name, trigger_user.ID_number]
[t_user.show_real_name, t_user.ID_number]
elsif container.auth_type == 2
ue = trigger_user.user_extension
ue = t_user.user_extension
[[ue.school&.name, ue.department&.name].join('_'), ue.identity_text]
end
I18n.t(locale_format(tiding_type, container.auth_type)) % [str1, str2]
@ -97,7 +97,7 @@ module TidingDecorator
elsif status == 2
I18n.t(locale_format(tiding_type, "#{status}_#{extra.nil?}"), reason: extra) % [name, second_name]
else
I18n.t(locale_format(tiding_type, status)) % [name, second_name]
I18n.t(locale_format(tiding_type, status), reason: extra) % [name, second_name]
end
end
@ -106,9 +106,9 @@ module TidingDecorator
if tiding_type == 'Apply'
I18n.t(locale_format(tiding_type)) % name
elsif status == 2
I18n.t(locale_format(tiding_type, "#{status}_#{extra.nil?}"), reason: extra) % name
I18n.t(locale_format(tiding_type, "#{status}_#{extra.nil?}"), name: name, reason: extra)
else
I18n.t(locale_format(tiding_type, status)) % name
I18n.t(locale_format(tiding_type, status), name: name, reason: extra)
end
end

@ -62,15 +62,18 @@ module ApplicationHelper
# shixun开启挑战对应的行为名及url
def task_operation_url current_myshixun, shixun
url = "/shixuns/#{shixun.identifier}/shixun_exec"
name =
if current_myshixun.blank?
shixun.status == 0 ? "模拟实战" : "开启挑战"
elsif current_myshixun.status == 1
"查看实战"
if current_myshixun.blank?
name = shixun.status == 0 ? "模拟实战" : "开启挑战"
url = "/shixuns/#{shixun.identifier}/shixun_exec"
else
identifier = current_myshixun.current_task(current_myshixun.games).try(:identifier)
if current_myshixun.status == 1
name = "查看实战"
else
"继续挑战"
name = "继续挑战"
end
url = identifier
end
[name, url]
end

@ -0,0 +1,26 @@
class CommitExercsieNotifyJobJob < ApplicationJob
queue_as :notify
def perform(exercise_id, user_id)
exercise = Exercise.find_by(id: exercise_id)
user = User.find_by(id: user_id)
return if [exercise, user].any?(&:blank?)
course = exercise.course
attrs = %i[user_id trigger_user_id container_id container_type parent_container_id parent_container_type
belong_container_id belong_container_type tiding_type viewed status created_at updated_at]
same_attrs = {
trigger_user_id: user.id,
container_id: exercise.id, container_type: 'Exercise',
parent_container_id: exercise.id, parent_container_type: 'CommitExercise',
belong_container_id: course.id, belong_container_type: 'Course',
tiding_type: 'Exercise', viewed: 0, status: 0
}
Tiding.bulk_insert(*attrs) do |worker|
course.course_member(user).member_teachers.each do |teacher|
worker.add same_attrs.merge(user_id: teacher.user_id)
end
end
end
end

@ -0,0 +1,26 @@
class CommitPollNotifyJobJob < ApplicationJob
queue_as :notify
def perform(poll_id, user_id)
poll = Poll.find_by(id: poll_id)
user = User.find_by(id: user_id)
return if [poll, user].any?(&:blank?)
course = poll.course
attrs = %i[user_id trigger_user_id container_id container_type parent_container_id parent_container_type
belong_container_id belong_container_type tiding_type viewed status created_at updated_at]
same_attrs = {
trigger_user_id: user.id,
container_id: poll.id, container_type: 'Poll',
parent_container_id: poll.id, parent_container_type: 'CommitPoll',
belong_container_id: course.id, belong_container_type: 'Course',
tiding_type: 'Poll', viewed: 0, status: 0
}
Tiding.bulk_insert(*attrs) do |worker|
course.course_member(user).member_teachers.each do |teacher|
worker.add same_attrs.merge(user_id: teacher.user_id)
end
end
end
end

@ -19,11 +19,20 @@ class StudentWorkScoreAppealNotifyJob < ApplicationJob
belong_container_id: course.id, belong_container_type: 'Course',
tiding_type: 'HomeworkCommon', viewed: 0, status: 0
}
student_same_attrs = {
trigger_user_id: 0,
container_id: appeal.id, container_type: 'StudentWorksScoresAppeal',
parent_container_id: score.student_work_id, parent_container_type: 'StudentWork',
belong_container_id: course.id, belong_container_type: 'Course',
tiding_type: 'System', viewed: 0, status: 0
}
Tiding.bulk_insert(*attrs) do |worker|
course.course_member(user).member_teachers.each do |teacher|
worker.add same_attrs.merge(user_id: teacher.user_id)
end
worker.add same_attrs.merge(user_id: score.user_id)
worker.add student_same_attrs.merge(user_id: score.user_id)
end
end
end

@ -3,8 +3,18 @@
class ApplyUserAuthentication < ApplicationRecord
belongs_to :user
has_many :tidings, :as => :container, :dependent => :destroy
scope :real_name_auth, -> { where(auth_type: 1) }
scope :professional_auth, -> { where(auth_type: 2) }
scope :processing, -> { where(status: 0) }
scope :passed, -> { where(status: 1) }
after_create :send_tiding
private
def send_tiding
self.tidings << Tiding.new(:user_id => '1', :status=> 0, :trigger_user_id => user_id, :belong_container_id => 1, :belong_container_type =>'User', :tiding_type => "Apply")
end
end

@ -11,7 +11,7 @@ class CourseMessage < ApplicationRecord
def pass!
update!(status: :PASSED)
send_deal_tiding
send_deal_tiding(1)
end
def application_user
@ -20,16 +20,16 @@ class CourseMessage < ApplicationRecord
def reject!
update!(status: :REJECTED)
send_deal_tiding
send_deal_tiding(2)
end
private
def send_deal_tiding
def send_deal_tiding deal_status
# 发送申请处理结果消息
Tiding.create!(
user_id: course_message_id, trigger_user: User.current, container_id: course_id, container_type: 'DealCourse',
belong_container: course, extra: content.to_i == 2 ? '7' : '9', tiding_type: 'System', status: status == :PASSED ? 1 : 2
belong_container: course, extra: content.to_i == 2 ? '9' : '7', tiding_type: 'System', status: deal_status
)
# 将申请消息置为已处理
Tiding.where(trigger_user_id: user_id, container_id: course_id, container_type: 'JoinCourse', status: 0).update_all(status: 1)

@ -11,6 +11,7 @@ class JournalsForMessage < ApplicationRecord
scope :parent_comment, -> { where(m_parent_id: nil)}
scope :search_by_jour_type, lambda{|type,ids| where(jour_type:type,jour_id: ids)}
has_many :tidings, as: :container, dependent: :destroy
# "jour_type", # 留言所属类型
# "jour_id", # 留言所属类型的id

@ -43,7 +43,8 @@ module Searchable::Subject
author_school_name: user.school_name,
visits_count: visits,
stage_count: stages_count,
stage_shixuns_count: stage_shixuns_count
stage_shixuns_count: stage_shixuns_count,
shixuns_count: shixuns_count
}
end

@ -17,7 +17,12 @@ class StudentGraduationTopic < ApplicationRecord
scope :is_refused, -> {where(status: 2)}
scope :is_accepted, -> {where(status: 1)}
scope :is_accepting, -> {where(status: 0)}
after_create :send_tiding
def send_tiding
self.tidings << Tiding.new(:user_id => self.graduation_topic.tea_id, :trigger_user_id => self.user_id, :parent_container_id => self.graduation_topic_id, :parent_container_type => "GraduationTopic",
:belong_container_id => self.graduation_topic.course_id, :belong_container_type => "Course", :viewed => 0, :status => 0, :tiding_type => "GraduationTopic")
end
# 学生名称
def name

@ -132,6 +132,10 @@ class User < ApplicationRecord
# 项目
has_many :applied_projects, dependent: :destroy
# 教学案例
has_many :libraries, dependent: :destroy
# Groups and active users
scope :active, lambda { where(status: STATUS_ACTIVE) }

@ -56,7 +56,7 @@ class Libraries::SaveService < ApplicationService
destroy_ids = old_attachment_id - attachment_ids
library.attachments.where(id: destroy_ids).delete_all
Attachment.where(id: attachment_ids, author_id: user.id).update_all(container: library)
Attachment.where(id: attachment_ids, author_id: user.id).update_all(container_type: library.class.to_s, container_id: library.id)
end
def library_params

@ -39,15 +39,10 @@ elsif @type == "mp3" || @type == "mp4"
json.file_url attachment_show_users_path(:file_name => file, :path => @original_path)
end
end
# json.user_file do
# json.array! @user_picture do |file|
# json.file_url attachment_show_users_path(:file_name => file, :path => @user_path, :time => Time.now.to_i)
# end
# end
if @type == "mp4"
json.user_file [{"file_url": "http://120.27.231.56:48080/attachments/download/378172/456.mp4"}]
else
json.user_file [{"file_url": "http://120.27.231.56:48080/attachments/download/378175/654058514.mp3"}]
json.user_file do
json.array! @user_picture do |file|
json.file_url attachment_show_users_path(:file_name => file, :path => @user_path, :time => Time.now.to_i)
end
end
json.answer_file do
json.array! @answer_picture do |file|

@ -1,4 +1,4 @@
json.(@subject, :id, :name, :description, :learning_notes, :stages_count, :stage_shixuns_count)
json.(@subject, :id, :name, :description, :learning_notes, :stages_count, :stage_shixuns_count, :shixuns_count)
json.challenge_choose_count @subject.subject_challenge_choose_count
json.challenges_count @subject.subject_challenge_count

@ -4,13 +4,28 @@ json.content tiding.content
json.identifier tiding.identifier
json.auth_type tiding.container_type == 'ApplyUserAuthentication' ? tiding.container.auth_type : nil
json.homework_type tiding.container_type == 'HomeworkCommon' ? tiding.container.homework_type : nil
homework_type = nil
if tiding.container_type == 'HomeworkCommon'
homework_type = tiding.container.homework_type rescue nil
end
if homework_type.blank? && tiding.parent_container_type == 'HomeworkCommon'
homework_type = tiding.parent_container.homework_type rescue nil
end
json.homework_type homework_type
json.time tiding.how_long_time
json.new_tiding tiding.unread?(@onclick_time)
json.trigger_user do
json.partial! 'users/user_simple', user: tiding.trigger_user_id.zero? ? User.find(1) : tiding.trigger_user
if tiding.trigger_user_id.zero?
json.id 0
json.name "系统"
json.login ""
json.image_url "educoder/systemLogo.png"
else
json.partial! 'users/user_simple', user: tiding.trigger_user
end
end
json.attachments tiding.attachments, partial: 'attachments/attachment_small', as: :attachment

@ -38,8 +38,8 @@
Apply_end: "申请添加单位:%s"
System:
"1_end": "你提交的添加单位申请:%{name},审核已通过"
"2_reason_end": "你提交的添加单位申请:%{name},审核未通过<br/><span>原因:%{reason}</span>"
"2_no_reason_end": "你提交的添加单位申请:%{name},审核未通过"
"2_false_end": "你提交的添加单位申请:%{name},审核未通过<br/><span>原因:%{reason}</span>"
"2_true_end": "你提交的添加单位申请:%{name},审核未通过"
"3_end": "你提交的添加单位申请:%{name},已被更改为:%{reason}"
ApplyAction:
ApplyShixun:
@ -57,11 +57,11 @@
"1_end": "你提交的试用授权申请,审核已通过"
"2_end": "你提交的试用授权申请,审核未通过<br/><span>原因:%{reason}</span>"
Apply_end: "提交了试用授权申请"
Course_end: "成功创建了课堂:%s"
Course_end: "创建了课堂:%s"
Course:
Delete_end: "你删除了课堂:%s"
Shixun_end: "成功创建了实训:%s"
Subject_end: "成功创建了实训课程:%s"
Shixun_end: "创建了实训:%s"
Subject_end: "创建了实训课程:%s"
ArchiveCourse_end: "你的课堂已经归档:%s"
JournalsForMessage:
Mentioned_end: "@了你:%s"
@ -164,7 +164,7 @@
NearlyEnd_end: "试卷的截止时间快到啦:%s"
CommitExercise_end: "提交了试卷答题:%s"
ExerciseScore_end: "评阅了你的试卷:%s"
StudentGraduationTopic_end: "选择毕设选题:%s"
StudentGraduationTopic_end: "申请选择毕设选题:%s"
DealStudentTopicSelect:
1_end: "你提交的选题申请:%s审核已通过"
2_end: "你提交的选题申请:%s审核未通过"
@ -195,14 +195,14 @@
ChallengeWorkScore_end: "调整了你的作品分数:%s"
StudentWorksScoresAppeal:
UserAppealResult:
1_end: "同意了你提交的匿评申诉申请:%s"
2_end: "拒绝了你提交的匿评申诉:%s"
1_end: "你提交的匿评申诉申请:%s,审核已通过"
2_end: "你提交的匿评申诉:%s,审核未通过"
AppealResult:
1_end: "同意了他人对你的匿评申诉申请:%s"
2_end: "拒绝了他人对你的匿评申诉:%s"
1_end: "别人对你的匿评发起的申诉申请:%s审核已通过"
2_end: "别人对你的匿评发起的申诉申请:%s审核未通过"
StudentWork:
Apply_end: "发起了匿评申诉申请:%s"
HomeworkCommon_end: "有人对你的匿评发起了申诉:%s"
System_end: "有人对你的匿评发起了申诉:%s"
Department_end: "你选填的二级单位:%s%s因不符合规范,已被系统删除.请重新选择"
Library:
Apply_end: "申请发布教学案例:%s"

@ -1,7 +0,0 @@
class AddIndexToUser < ActiveRecord::Migration[5.2]
def change
# add_index :users, :login, unique: true
# add_index :users, :mail, unique: true
# add_index :users, :phone, unique: true
end
end

@ -1,10 +1,10 @@
class AddPraisesCountToLibraries < ActiveRecord::Migration[5.2]
def change
add_column :libraries, :praises_count, :integer, :default => 0
Library.find_each do |library|
praises_count = library.praise_treads.count
library.update_column(:praises_count, praises_count)
end
# add_column :libraries, :praises_count, :integer, :default => 0
#
# Library.find_each do |library|
# praises_count = library.praise_treads.count
# library.update_column(:praises_count, praises_count)
# end
end
end

@ -0,0 +1,7 @@
class SyncIndexToUsers < ActiveRecord::Migration[5.2]
def change
add_index :users, :login, unique: true
add_index :users, :mail, unique: true
add_index :users, :phone, unique: true
end
end

@ -0,0 +1,124 @@
class ModifyKeContentsForMarkdown < ActiveRecord::Migration[5.2]
include ActionView::Helpers::SanitizeHelper
def change
# def ke_transform_to_md content
# sanitize(content, tags: %w(img a span), attributes: %w(src href target style))
# .gsub(/^(\n)/, "").gsub(/(\n)+/, "<br />").gsub(/$(\n)/, "")
# .gsub(/(\n)+/, "<br />").gsub("\t", "&nbsp;&nbsp;&nbsp;&nbsp;")
# end
#
# # 课程讨论区
# MessageDetail.find_each do |m|
# content = ke_transform_to_md m.content
# puts("content:#{content}")
# m.update_column(:content, content)
# end
#
# # 试卷的标题
# ExerciseQuestion.find_each do |eq|
# question_title = ke_transform_to_md eq.question_title
# puts("question_title:#{question_title}")
# eq.update_column(:question_title, question_title)
# end
#
# # 试卷的答案
# ExerciseStandardAnswer.find_each do |esa|
# answer_text = ke_transform_to_md esa.answer_text
# puts("answer_text: #{answer_text}")
# esa.update_column(:answer_text, answer_text)
# end
#
# # 试卷题库的问题标题
# ExerciseBankQuestion.find_each do |ebq|
# question_title = ke_transform_to_md ebq.question_title
# puts("#question_title: #{question_title}")
# ebq.update_column(:question_title, question_title)
# end
#
# # 试卷答案
# ExerciseBankStandardAnswer.find_each do |ebsa|
# answer_text = ke_transform_to_md ebsa.answer_text
# puts("#answer_text: #{answer_text}")
# ebsa.update_column(:answer_text, answer_text)
# end
#
# # 问卷的标题
# PollQuestion.find_each do |pq|
# question_title = ke_transform_to_md pq.question_title
# puts("question_title:#{question_title}")
# pq.update_column(:question_title, question_title)
# end
#
# # 学生的作品
# StudentWork.find_each do |sw|
# description = ke_transform_to_md sw.description
# puts("#description: #{description}")
# sw.update_column(:description, description)
# end
#
# # 作业
# HomeworkCommon.find_each do |hc|
# description = ke_transform_to_md hc.description
# reference_answer = ke_transform_to_md hc.reference_answer
# explanation = ke_transform_to_md hc.explanation
# hc.update_attributes(description: description, reference_answer: reference_answer, explanation: explanation)
# end
#
# # 作业题库
# HomeworkBank.find_each do |hb|
# description = ke_transform_to_md hb.description
# reference_answer = ke_transform_to_md hb.reference_answer
# hb.update_attributes(description: description, reference_answer: reference_answer)
# end
#
# # 课堂评论
# JournalsForMessage.find_each do |jfm|
# notes = ke_transform_to_md jfm.notes
# puts("#notes: #{notes}")
# jfm.update_column(:notes, notes)
# end
#
# # 毕业任务
# GraduationTask.find_each do |gt|
# description = ke_transform_to_md gt.description
# puts("#description: #{description}")
# gt.update_column(:description, description)
# end
#
# # 毕设选题
# GraduationTopic.find_each do |gt|
# description = ke_transform_to_md gt.description
# puts("#description: #{description}")
# gt.update_column(:description, description)
# end
#
# # 毕设作品
# GraduationWork.find_each do |gw|
# description = ke_transform_to_md gw.description
# puts("#description: #{description}")
# gw.update_column(:description, description)
# end
#
# # 毕设任务题库
# GtaskBank.find_each do |gb|
# description = ke_transform_to_md gb.description
# puts("#description: #{description}")
# gb.update_column(:description, description)
# end
#
# # 毕设选题题库
# GtopicBank.find_each do |gb|
# description = ke_transform_to_md gb.description
# puts("#description: #{description}")
# gb.update_column(:description, description)
# end
#
# # 交流问答
# Memo.find_each do |m|
# content = ke_transform_to_md m.content
# puts("#description: #{content}")
# m.update_column(:content, content)
# end
end
end

@ -0,0 +1,6 @@
class MigrateAnonymousTidingType < ActiveRecord::Migration[5.2]
def change
Tiding.where(container_type: "StudentWorksScore", extra: "3").update_all(tiding_type: "System", trigger_user_id: 0)
Tiding.where(container_type: "StudentWorksScoresAppeal", parent_container_type: "StudentWork", tiding_type: "HomeworkCommon").update_all(tiding_type: "System", trigger_user_id: 0)
end
end

@ -0,0 +1,7 @@
class AddExecTimeToEvaluateRecords < ActiveRecord::Migration[5.2]
def change
remove_columns :evaluate_records, :exec_time if User.first.attributes.include?("exec_time")
add_column :evaluate_records, :exec_time, :integer
end
end

@ -29,7 +29,7 @@ const env = getClientEnvironment(publicUrl);
module.exports = {
// You may want 'eval' instead if you prefer to see the compiled output in DevTools.
// See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.s
devtool: "eval", // 开启调试
devtool: "source-map", // 开启调试
// These are the "entry points" to our application.
// This means they will be the "root" imports that are included in JS bundle.
// The first two entry points enable "hot" CSS and auto-refreshes for JS.

@ -54,4 +54,8 @@ html, body {
/* md多空格 */
.markdown-body p {
white-space: pre-wrap;
}
/* resize */
.editormd .CodeMirror {
border-right: none !important;
}

@ -218,6 +218,12 @@ const UsersInfo = Loadable({
loading: Loading,
})
// 教学案例
const MoopCases = Loadable({
loader: () => import('./modules/moop_cases/index'),
loading: Loading,
})
// 兴趣页面
const Interestpage = Loadable({
loader: () => import('./modules/login/EducoderInteresse'),
@ -351,14 +357,19 @@ class App extends Component {
{/*课堂*/}
<Route path="/courses" component={CoursesIndex} {...this.props}></Route>
{/* <Route path="/forums" component={ForumsIndexComponent}>
</Route> */}
{/* 教学案例 */}
<Route path="/moop_cases"render={
(props) => (<MoopCases {...this.props} {...props} {...this.state} />)
}/>
<Route path="/forums"
render={
(props)=>(<ForumsIndexComponent {...this.props} {...props} {...this.state}></ForumsIndexComponent>)
}
>
</Route>
<Route path="/comment" component={CommentComponent}/>
<Route path="/testMaterial" component={TestMaterialDesignComponent}/>
<Route path="/test" component={TestIndex}/>

@ -59,4 +59,6 @@ export function toPath(path) {
}
// export default queryString
export function getTaskUrlById(id) {
return `/tasks/${id}`
}

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import {Link} from 'react-router-dom'
const map={"blue":"blueFull","greyBack":"greyBack","grey":"greyWidthFixed","green":"greenBack",
const map={"blue":"blueFull","greyBack":"greyBack","grey":"greyWidthFixed","green":"greenBack",'greyLine':"greyLine",
'colorBlue': 'colorBlue', // 蓝字白底
}
class ActionBtn extends Component {

@ -3,7 +3,8 @@ import { from } from '_array-flatten@2.1.2@array-flatten';
// export { default as OrderStateUtil } from '../routes/Order/components/OrderStateUtil';
export { getImageUrl as getImageUrl, getUrl as getUrl, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
, getUploadActionUrl as getUploadActionUrl, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth } from './UrlTool';
, getUploadActionUrl as getUploadActionUrl, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth
, getTaskUrlById as getTaskUrlById } from './UrlTool';
export { default as queryString } from './UrlTool2';
export { SnackbarHOC as SnackbarHOC } from './SnackbarHOC';

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

@ -23,6 +23,7 @@ import CommentItemMDEditor from './CommentItemMDEditor';
import './Comment.css'
import Modals from '../modals/Modals'
import { InputNumber } from 'antd'
/*
-------------------------- 样式相关
@ -470,7 +471,8 @@ class Comments extends Component {
onGoldRewardDialogOkBtnClick() {
console.log('onGoldRewardDialogOkBtnClick')
const { goldRewardInput } = this.state;
if (!goldRewardInput || goldRewardInput === '0' || goldRewardInput.indexOf('-') !== -1) {
// || goldRewardInput.indexOf('-') !== -1
if (!goldRewardInput || goldRewardInput === '0' ) {
this.setState({ goldRewardInputError: true})
return;
} else {
@ -478,8 +480,13 @@ class Comments extends Component {
this.props.rewardCode(this.comment, this.childComment, goldRewardInput)
}
}
onGoldRewardInputChange(event) {
this.setState({ goldRewardInput: event.target.value, goldRewardInputError: false });
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;
@ -538,11 +545,14 @@ class Comments extends Component {
<DialogTitle id="alert-dialog-title">{"奖励设置"}</DialogTitle>
<DialogContent>
<FormControl { ...goldRewardInputErrorObj } aria-describedby="name-error-text">
<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>
</FormControl> */}
{/*<DialogContentText id="alert-dialog-description" style={{textAlign: 'center'}}> </DialogContentText>*/}
</DialogContent>

@ -9,6 +9,7 @@ import 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'
@ -38,16 +39,22 @@ class RewardDialog extends Component {
onGoldRewardDialogOkBtnClick() {
console.log('onGoldRewardDialogOkBtnClick')
const { goldRewardInput } = this.state;
if (!goldRewardInput || goldRewardInput === '0' || goldRewardInput.indexOf('-') !== -1) {
this.setState({ goldRewardInputError: true})
if (!goldRewardInput || goldRewardInput === '0' || goldRewardInput < 0) {
this.props.showNotification('奖励金币不能为空或负数')
// this.setState({ goldRewardInputError: true})
return;
} else {
this.props.setRewardDialogVisible( false )
this.props.rewardCode(goldRewardInput)
}
}
onGoldRewardInputChange(event) {
this.setState({ goldRewardInput: event.target.value, goldRewardInputError: false });
onGoldRewardInputChange(value) {
// || event.target.value
const number = parseInt(value || 0, 10);
if (Number.isNaN(number)) {
return;
}
this.setState({ goldRewardInput: number , goldRewardInputError: false });
}
render() {
@ -58,17 +65,26 @@ class RewardDialog extends Component {
return (
<Dialog
open={goldRewardDialogOpen}
disableEscapeKeyDown={true}
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>
{/* <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>
{ 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>

@ -1,449 +1,449 @@
import React,{ Component } from "react";
import {
Form, Input, InputNumber, Switch, Radio,
Slider, Button, Upload, Icon, Rate, Checkbox, message,
Row, Col, Select, Modal, Divider
} 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;
// 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: 60
}
}
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 || [],
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: 60 - parseInt(data.subject.length) })
}
}
})
.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,
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,
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) => {
let fileList = info.fileList;
this.setState({ fileList: appendFileSizeToUploadFileAll(fileList)
});
}
onAttachmentRemove = (file) => {
confirm({
// title: '确定要删除这个附件吗?',
title: '是否确认删除?',
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: 60 - parseInt(e.target.value.length)
})
}
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) {
message.error('文件大小必须小于150MB!');
}
return isLt150M;
},
};
const isAdmin = this.props.isAdmin()
const courseId=this.props.match.params.coursesId;
const boardId = this.props.match.params.boardId
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.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.props.history.goBack()}>
返回
</a>
</p>
{/* notRequired */}
<Form {...formItemLayout} onSubmit={this.handleSubmit}>
<div className="formBlock" style={{paddingBottom: '0px', position: 'relative'}}>
{ isAdmin &&
<React.Fragment>
{getFieldDecorator('sticky', {
valuePropName: 'checked',
})(
isAdmin && <Checkbox style={{ right: '22px',
top: '28px',
position: 'absolute'
}}>置顶</Checkbox>
)}
{/* checkbox 有个边距样式 .ant-checkbox-wrapper + span, */}
<span style={{ "padding-left": 0, "padding-right": 0 }}></span>
</React.Fragment>
}
<Form.Item
label="标题"
className="topicTitle "
>
{getFieldDecorator('subject', {
rules: [{
required: true, message: '请输入标题',
}, {
max: 60, message: '最大限制为60个字符',
}],
})(
<Input placeholder="请输入帖子标题最大限制60个字符" className="searchViewAfter" maxLength="60"
onInput={this.changeTitle} addonAfter={String(title_num)} />
)}
</Form.Item>
<Form.Item
label=""
style={{ display: 'inline-block' }}
>
{getFieldDecorator('select_board_id', {
// initialValue: '3779',
})(
<Select style={{ width: 230 }}
dropdownRender={menu => (
<div>
{menu}
<Divider style={{ margin: '4px 0' }} />
<div style={{ padding: '8px', cursor: 'pointer' }} onMouseDown={() => this.refs['addDirModal'].open()}>
<Icon type="plus" /> 添加目录
</div>
</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);
import React,{ Component } from "react";
import {
Form, Input, InputNumber, Switch, Radio,
Slider, Button, Upload, Icon, Rate, Checkbox, message,
Row, Col, Select, Modal, Divider
} 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;
// 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: 60
}
}
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 || [],
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: 60 - parseInt(data.subject.length) })
}
}
})
.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,
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,
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) => {
let fileList = info.fileList;
this.setState({ fileList: appendFileSizeToUploadFileAll(fileList)
});
}
onAttachmentRemove = (file) => {
confirm({
// title: '确定要删除这个附件吗?',
title: '是否确认删除?',
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: 60 - parseInt(e.target.value.length)
})
}
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) {
message.error('文件大小必须小于150MB!');
}
return isLt150M;
},
};
const isAdmin = this.props.isAdmin()
const courseId=this.props.match.params.coursesId;
const boardId = this.props.match.params.boardId
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.props.history.goBack()}>
返回
</a>
</p>
{/* notRequired */}
<Form {...formItemLayout} onSubmit={this.handleSubmit}>
<div className="formBlock" style={{paddingBottom: '0px', position: 'relative'}}>
{ isAdmin &&
<React.Fragment>
{getFieldDecorator('sticky', {
valuePropName: 'checked',
})(
isAdmin && <Checkbox style={{ right: '22px',
top: '28px',
position: 'absolute'
}}>置顶</Checkbox>
)}
{/* checkbox 有个边距样式 .ant-checkbox-wrapper + span, */}
<span style={{ "padding-left": 0, "padding-right": 0 }}></span>
</React.Fragment>
}
<Form.Item
label="标题"
className="topicTitle "
>
{getFieldDecorator('subject', {
rules: [{
required: true, message: '请输入标题',
}, {
max: 60, message: '最大限制为60个字符',
}],
})(
<Input placeholder="请输入帖子标题最大限制60个字符" className="searchViewAfter" maxLength="60"
onInput={this.changeTitle} addonAfter={String(title_num)} />
)}
</Form.Item>
<Form.Item
label=""
style={{ display: 'inline-block' }}
>
{getFieldDecorator('select_board_id', {
// initialValue: '3779',
})(
<Select style={{ width: 230 }}
dropdownRender={menu => (
<div>
{menu}
<Divider style={{ margin: '4px 0' }} />
<div style={{ padding: '8px', cursor: 'pointer' }} onMouseDown={() => this.refs['addDirModal'].open()}>
<Icon type="plus" /> 添加目录
</div>
</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);

File diff suppressed because it is too large Load Diff

@ -223,7 +223,7 @@ class CommonWorkDetailIndex extends Component{
}
`}</style>
{current_user && <CBreadcrumb items={[
{ to: current_user.first_category_url , name: course_name},
{ 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人作品的作者真实姓名切换为“匿名”

@ -90,7 +90,7 @@ class WorkDetailPageHeader extends Component{
}
`}</style>
<CBreadcrumb items={[
{ to: current_user.first_category_url, name: course_name},
{ 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人作品的作者真实姓名切换为“匿名”

@ -683,8 +683,9 @@ a.white-btn.use_scope-btn:hover{
}
.colorBlue {
padding: 0px 7px;
background-color: #fff;
color: #4CACFF;
color: #4CACFF!important;
border: 1px solid #4CACFF;
}
.greyBack{
@ -701,6 +702,12 @@ a.white-btn.use_scope-btn:hover{
.Actionbtn.middle {
padding: 0px 18px;
}
.greyLine{
background: #fff;
border:1px solid #eaeaea;
color: #999!important;
padding:0px 10px;
}
.colorFF6800{

@ -41,7 +41,7 @@
}
.stu_table table{
line-height: 1;
line-height: 1.2;
}
.stu_table .classesName{
display: block;

File diff suppressed because it is too large Load Diff

@ -124,7 +124,7 @@ class Exercisestatisticalresult extends Component {
}
`}
</style>
<div className={"educontent mb20" } >
<div className={"educontent mb30" } >
{data&&data.course_groups.length===0?"":<div className="stud-class-set">
<div className="clearfix edu-back-white">
@ -154,7 +154,7 @@ class Exercisestatisticalresult extends Component {
</div>}
<div className="stud-class-set">
<div className="clearfix edu-back-white poll_list">
<div className="clearfix edu-back-white exerciseTable ">
<Exercisestatisticalresulttabel
data={data}
/>
@ -198,16 +198,73 @@ class Exercisestatisticalresult extends Component {
.padtop24{
padding-top: 24px;
}
.exerciseTable .shixunreporttitle{
padding:0px!important;
}
.exerciseTable .ant-table-thead > tr > th,.exerciseTable .ant-table-tbody > tr > td{
padding:16px 20px!important;
}
.exerciseTable .editormd-html-preview{
width:100%!important;
}
`}
</style>
{/* 从子组件将样式提出来,不然会循环多次 */}
<style>{`
// .ant-table-thead > tr > th{
// text-align: center;
// }
.tasknamebox{
width: 50px;
height: 24px;
border: 1px solid rgba(221,23,23,1);
border-radius: 12px;
color: rgba(221,23,23,1);
display: inline-block;
line-height: 24px;
text-align: center;
}
.tasknameboxs{
color: #666666 !important;
background:rgba(237,237,237,1) !important;
}
.ant-table-tbody > tr > td{
font-size:14px;
}
.task-hide{
max-width: 345px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
// .ant-table-tbody > tr{
// height:64px;
// text-align: center;
// }
.ant-progress-success-bg, .ant-progress-bg{
background-color: #29BD8B !important;
}
.ant-table-thead > tr > th:nth-child(1){
text-align: left;
}
.ant-table-tbody > tr > td:nth-child(1){
text-align: left;
}
.Exermubu .ant-table-tbody > tr:nth-last-child(1) > td{
border:1px solid transparent;
}
.Exermubu .editormd-html-preview, .Exermubu .editormd-preview-container {
background: transparent;
}
`}
</style>
{data&&data.commit_results.map((item,key)=>{
return(
<div className="stud-class-set mb20" key={key}>
<div className="stud-class-set mb20 exerciseTable" key={key}>
<div className="clearfix edu-back-white poll_list" style={{padding: '20px'}}>
<div className="font-16 shixunreporttitle fl" >
<div className="font-16 shixunreporttitle fl" style={{width:"1050px"}}>
<span className="CACFF">{item.ques_position+"."}{item.ques_type===0?"单选":item.ques_type===1?"多选":item.ques_type===2?"判断":item.ques_type===3?"填空":item.ques_type===4?"主观":item.ques_type===5?"实训":""}</span>
<div>
{/*Q{item.ques_position}*/}
@ -224,14 +281,15 @@ class Exercisestatisticalresult extends Component {
item.ques_details.map((ite,k)=>{
return(
<div className={"mt20"}>
<div className="clearfix edu-back-white poll_list" style={{padding: '0px 20px'}}>
<div className="font-16 shixunreporttitle fl padding20">
<div>
<div className="clearfix edu-back-white poll_list mb10" style={{padding: '0px 20px'}}>
<div className="font-16 shixunreporttitle fl pt20 pb20">
<span>{ite.challenge_position}{ite.challenge_name}</span>
</div>
<div className="fr shixunreporttitles padtop24">正确率<span style={{color:'#FF6800'}}>{ite.challenge_percent}%</span></div>
<div className="fr shixunreporttitles">正确率<span style={{color:'#FF6800'}}> {ite.challenge_percent}%</span></div>
</div>
<Exercisetablesmubu
tableNum={`${key}${k}`}
data={ite.challenge_details}
type={item.ques_type}
effictive_counts={item.effictive_counts}
@ -241,7 +299,7 @@ class Exercisestatisticalresult extends Component {
)
}):
<Exercisetablesmubu
tableNum={key}
tableNum={key}
data={item.ques_details}
type={item.ques_type}
effictive_counts={item.effictive_counts}
@ -252,24 +310,20 @@ class Exercisestatisticalresult extends Component {
})}
</div>
<div className="mb40 edu-txt-center padding20-30"
style={
{
display: data&&data===undefined?1:data&&data.questions_count.length< 10 ? 'none' : 'block'
}
}>
<Pagination
showQuickJumper
defaultCurrent={1}
pageSize={limit}
total={data&&data.questions_count}
current={page}
onChange={this.PaginationCourse}
/>
</div>
{
data && data.questions_count > 10 &&
<div className="mb40 edu-txt-center padding20-30">
<Pagination
showQuickJumper
defaultCurrent={1}
pageSize={limit}
total={ data && data.questions_count }
current={page}
onChange={this.PaginationCourse}
/>
</div>
}
</div>

@ -110,53 +110,7 @@ class Exercisetablesmubus extends Component {
return (
<div>
<style>{`
// .ant-table-thead > tr > th{
// text-align: center;
// }
.tasknamebox{
width: 50px;
height: 24px;
border: 1px solid rgba(221,23,23,1);
border-radius: 12px;
color: rgba(221,23,23,1);
display: inline-block;
line-height: 24px;
text-align: center;
}
.tasknameboxs{
color: #666666 !important;
background:rgba(237,237,237,1) !important;
}
.ant-table-tbody > tr > td{
font-size:14px;
}
.task-hide{
max-width: 345px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
// .ant-table-tbody > tr{
// height:64px;
// text-align: center;
// }
.ant-progress-success-bg, .ant-progress-bg{
background-color: #29BD8B !important;
}
.ant-table-thead > tr > th:nth-child(1){
text-align: left;
}
.ant-table-tbody > tr > td:nth-child(1){
text-align: left;
}
.Exermubu .ant-table-tbody > tr:nth-last-child(1) > td{
border:1px solid transparent;
}
`}
</style>
{datas===undefined?"":<Table
className={"Exermubu"}
dataSource={datas}

@ -105,7 +105,7 @@ class GraduateTopicDetail extends Component{
<div className="newMain">
<div className="educontent mt10 mb50">
<p className="clearfix mb15 lineh-20">
<WordsBtn style="grey" className="fl" to={current_user.first_category_url}>{tableData && tableData.course_name}</WordsBtn>
<WordsBtn style="grey" className="fl" to={current_user&&current_user.first_category_url}>{tableData && tableData.course_name}</WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn style="grey" className="fl" to={`/courses/${tableData.course_id}/graduation_topics/${tableData.graduation_id}`}>{tableData.graduation_name}</WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>

@ -323,7 +323,7 @@ class GraduateTopicNew extends Component{
`}</style>
<div className="edu-class-container edu-position courseForm">
<p className="clearfix mb20 mt10">
<WordsBtn style="grey" className="fl" to={current_user.first_category_url}>{course_name}</WordsBtn>
<WordsBtn style="grey" className="fl" to={current_user&&current_user.first_category_url}>{course_name}</WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn style="grey" className="fl" to={`/courses/${coursesId}/graduation_topics/${left_banner_id}`}>{left_banner_name}</WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>

@ -603,7 +603,9 @@ class studentsList extends Component{
combineArray = this.state.application_list
}
const isAdminOrTeacher = this.props.isAdminOrTeacher()
const isSuperAdmin = this.props.isSuperAdmin()
const hasGraduationModule = this.hasGraduationModule()
const coursesId = this.props.match.params.coursesId
return(
<React.Fragment>
@ -624,7 +626,10 @@ class studentsList extends Component{
></AddAdminModal>
<Titlesearchsection
title={"教师列表"}
title={<React.Fragment>
<span>教师列表</span>
{!isSuperAdmin && coursesId == '1309' && <span style={{color: '#848484', fontSize: '14px', marginLeft: '10px'}}>(示例课堂部分成员不可见)</span>}
</React.Fragment>}
searchValue={ searchValue }
onInputSearchChange={this.onInputSearchChange}
showSearchInput={total_count >= 10}
@ -641,6 +646,7 @@ class studentsList extends Component{
<Menu.Item key="1">已审批({total_count})</Menu.Item>
<Menu.Item key="2">待审批({apply_size})</Menu.Item>
</Menu>
{/* */}
</div> :
(!!total_count ? <ColorCountText count={total_count} name="个教师"></ColorCountText> : '')
}

@ -1,6 +1,6 @@
import React,{ Component } from "react";
import CoursesListType from '../coursesPublic/CoursesListType';
import { WordsBtn } from 'educoder';
import { WordsBtn, getTaskUrlById } from 'educoder';
import {Tooltip,message,Modal} from 'antd';
import {Link} from 'react-router-dom';
import axios from 'axios';
@ -56,16 +56,17 @@ class ShixunhomeWorkItem extends Component{
startbtn:true,
})
let url= list+".json";
// const w=window.open('about:blank');
axios.get(url).then((response) => {
if(response.status===200){
if(response.data.status===-2){
this.setState({
startbtn:false,
shixunsreplace:true,
hidestartshixunsreplacevalue:response.data.message+".json"
})
// w.close()
}else if(response.data.status===-1){
console.log(response)
}else if(response.data.status===-3){
@ -74,13 +75,15 @@ class ShixunhomeWorkItem extends Component{
startshixunCombattype:true,
startbtn:false
})
// w.close()
}else{
if(response.data.status!=401&&response.data.status!=403){
const w=window.open('about:blank');
w.location.href= "/tasks/"+response.data.game_identifier
}
this.setState({
startbtn:false
})
// if(response.data.status!=401&&response.data.status!=403){
// w.location.href= "/tasks/"+response.data.game_identifier
// }
window.location.href = "/tasks/"+response.data.game_identifier
}
}
@ -88,6 +91,8 @@ class ShixunhomeWorkItem extends Component{
this.setState({
startbtn:false
})
// w.close()
});
}
@ -388,9 +393,15 @@ class ShixunhomeWorkItem extends Component{
{this.props.isStudent===true?this.props.course_identity===5?
<WordsBtn style="blue" className="colorblue font-16 mr20 fr mt10">
{startbtn===false?<a className="btn colorblue" onClick={()=>this.taskoperationId(discussMessage.task_operation[1])}>
{discussMessage.task_operation[0]}
</a>:<a className="btn colorblue" ></a>}
{startbtn===false?
(discussMessage.task_operation[0] == '继续挑战' || discussMessage.task_operation[0] == '查看实战' ?
<a className="btn colorblue" href={getTaskUrlById(discussMessage.task_operation[1])}>
{discussMessage.task_operation[0]}
</a>
:
<a className="btn colorblue" onClick={()=>this.taskoperationId(discussMessage.task_operation[1])}>
{discussMessage.task_operation[0]}
</a>):<a className="btn colorblue" ></a>}
</WordsBtn>:"":""
}

@ -42,6 +42,7 @@ import './ForumsIndex.css'
import './RightSection.css'
import { SnackbarHOC, getUrl } from 'educoder'
import { CNotificationHOC } from '../courses/common/CNotificationHOC'
let _url_origin = getUrl()
@ -104,7 +105,7 @@ class ForumsIndex extends Component {
componentDidMount() {
window.document.title = '讨论区'
window.document.title = '交流问答'
}
@ -248,7 +249,7 @@ class ForumsIndex extends Component {
}
}
export default SnackbarHOC() ( TPMIndexHOC ( ForumsIndex ) );
export default CNotificationHOC() (SnackbarHOC() ( TPMIndexHOC ( ForumsIndex ) ));
/*
:

@ -14,7 +14,7 @@ import moment from 'moment'
import Comments from '../comment/Comments'
import update from 'immutability-helper'
import Tooltip from 'material-ui/Tooltip';
// import Tooltip from 'material-ui/Tooltip';
import RewardDialog from '../common/RewardDialog';
import {ImageLayerOfCommentHOC} from '../page/layers/ImageLayerOfCommentHOC'
@ -23,6 +23,7 @@ import MemoDetailKEEditor from './MemoDetailKEEditor'
import MemoDetailMDEditor from './MemoDetailMDEditor'
import { bytesToSize } from 'educoder'
import { Tooltip } from 'antd'
const $ = window.$
function urlStringify(params) {
let noParams = true;
@ -65,7 +66,7 @@ class MemoDetail extends Component {
const memo = response.data.memo
if (response.data.status === -1) {
setTimeout(() => {
this.props.showSnackbar('帖子不存在!')
this.props.showNotification('帖子不存在!')
}, 300)
this.props.history.push(`/forums`)
return;
@ -127,11 +128,11 @@ class MemoDetail extends Component {
const status = response.data.status
if (status === 0) {
this.props.showSnackbar('删除成功');
this.props.showNotification('删除成功');
this.props.history.push(`/forums`)
} else if (status === -1) {
this.props.showSnackbar('帖子已被删除');
this.props.showNotification('帖子已被删除');
this.props.history.push(`/forums`)
}
}).catch((error) => {
@ -180,18 +181,16 @@ class MemoDetail extends Component {
})
}
renderAttachment() {
const { memo } = this.props;
const { memo, attachments_list } = this.props;
const attachments = []
memo.attachment_url.forEach((item, index) => {
attachments_list.forEach((item, index) => {
const ar = item.url.split('/')
const fileName = item.filename || ar[ar.length - 1]
let filesize = 0
if (item.filesize) {
filesize = bytesToSize(item.filesize)
}
const fileName = item.title
let filesize = item.filesize
attachments.push(
<p className="clearfix" key={index} >
<a href={item.url} className="color-green clearfix notefileDownload">
<a href={item.url} className="color-green clearfix notefileDownload" target="_blank">
<i className="iconfont icon-xiazai color-green ml5 fl"></i><span className="fl">{fileName}{filesize? ` ${filesize}` : ''}</span>
</a>
</p>
@ -215,9 +214,9 @@ class MemoDetail extends Component {
}
}
replyComment = (commentContent, id, editor) => {
const { showSnackbar } = this.props;
const { showNotification } = this.props;
if (!commentContent || commentContent.length === 0) {
showSnackbar('必须填写内容!')
showNotification('必须填写内容!')
return;
}
@ -377,7 +376,7 @@ class MemoDetail extends Component {
})
}
rewardCode = (parrentComment, childComment, amount) => {
const { showSnackbar } = this.props;
const { showNotification } = this.props;
const { comments } = this.state;
let handleComment = parrentComment
if (childComment) {
@ -421,12 +420,12 @@ class MemoDetail extends Component {
}
}).catch((error) => {
console.log(error)
showSnackbar('奖励失败,请联系系统管理员!')
showNotification('奖励失败,请联系系统管理员!')
})
}
hiddenComment = (item, childCommentId) => {
const id = item.id
const { showSnackbar } = this.props;
const { showNotification } = this.props;
const user = this._getUser();
const url = `/memos/${id}/hidden.json`
const { comments } = this.state;
@ -441,7 +440,7 @@ class MemoDetail extends Component {
}
).then((response) => {
if (response.data.status === -1) {
showSnackbar(response.data.message)
showNotification(response.data.message)
return;
}
if (response.data.status === 0) {
@ -600,7 +599,7 @@ class MemoDetail extends Component {
.then((response) => {
const status = response.data.status
if (status === 0) {
this.props.showSnackbar( memo.sticky ? '取消置顶成功' : '置顶成功');
this.props.showNotification( memo.sticky ? '取消置顶成功' : '置顶成功');
memo.sticky = memo.sticky ? false : true
this.setState({
memo: Object.assign({}, memo)
@ -635,9 +634,9 @@ class MemoDetail extends Component {
this.props.initForumState({
memo: newMemo
})
this.props.showSnackbar( '奖励成功' );
this.props.showNotification( '奖励成功' );
} else {
this.props.showSnackbar( '奖励失败,请联系系统管理员!' );
this.props.showNotification( '奖励失败,请联系系统管理员!' );
}
}).catch((error) => {
console.log(error)
@ -687,43 +686,56 @@ class MemoDetail extends Component {
<RewardDialog goldRewardDialogOpen={goldRewardDialogOpen}
setRewardDialogVisible={this.setRewardDialogVisible}
rewardCode={this.rewardCodeMemo}
{...this.props}
></RewardDialog>
<div className="clearfix">
<div id="forum_list" className="forum_table mh650">
<div className="padding40-30 bor-bottom-greyE">
<div className="font-16 mb5 cdefault clearfix pr pr35">
<span className="noteDetailTitle">{memo.subject}</span>
{ memo.sticky && <span className="btn-cir btn-cir-red ml10 mt10">置顶</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 && (_current_user.admin === true || _current_user.user_id === author_info.user_id) &&
<div className="edu-position-hidebox" style={{position: 'absolute', right: '12px',top:'4px'}}>
<a href="javascript:void(0);"><i className="fa fa-bars font-16"></i></a>
<ul className="edu-position-hide undis">
{ _current_user.admin === true &&
( 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> )
}
<li><Link to={`/forums/${memo.id}/edit`}>&nbsp;&nbsp;</Link></li>
<li>
<a href="javascript:void(0)" onClick={() =>
window.delete_confirm_box_2_react(`onMemoDelete`, '您确定要删除吗?' , memo)}>
&nbsp;&nbsp;</a>
</li>
</ul>
</div>
}
<Link className={`task-hide fr return_btn color-grey-6 mt2 ${ _current_user && (_current_user.admin === true
|| _current_user.user_id === author_info.user_id) ? '': 'no_mr'} `} to="/forums"
style={{ marginRight: '10px'}}
>
返回
</Link>
<div className="font-16 mb5 cdefault clearfix pr pr35" style={{display: 'flex', alignItems: 'center'}}>
{/* overflowHidden1 */}
<span className="noteDetailTitle " style={{maxWidth: '634px'}}>{memo.subject + memo.subject}</span>
{ memo.sticky && <span className="btn-cir btn-cir-red ml10 "
style={{ height: '20px', alignSelf: 'flex-start', marginTop: '10px' }}
>置顶</span>}
{ !!memo.reward &&
<Tooltip title={`获得平台奖励金币:${memo.reward}`}>
<span className="color-orange font-14 ml15"
style={{ height: '20px', alignSelf: 'flex-start', marginTop: '1px' }}
>
<i className="iconfont icon-gift mr5"></i>
<span style={{ 'vertical-align': 'sub' }}>{memo.reward}</span>
</span>
</Tooltip>
}
<div style={{ flex: 1, alignSelf: 'flex-start' }}>
{ _current_user && (_current_user.admin === true || _current_user.user_id === author_info.user_id) &&
<div className="edu-position-hidebox" style={{position: 'absolute', right: '12px',top:'4px'}}>
<a href="javascript:void(0);"><i className="fa fa-bars font-16"></i></a>
<ul className="edu-position-hide undis">
{ _current_user.admin === true &&
( 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> )
}
<li><Link to={`/forums/${memo.id}/edit`}>&nbsp;&nbsp;</Link></li>
<li>
<a href="javascript:void(0)" onClick={() =>
window.delete_confirm_box_2_react(`onMemoDelete`, '您确定要删除吗?' , memo)}>
&nbsp;&nbsp;</a>
</li>
</ul>
</div>
}
<Link className={`task-hide fr return_btn color-grey-6 mt2 ${ _current_user && (_current_user.admin === true
|| _current_user.user_id === author_info.user_id) ? '': 'no_mr'} `} to="/forums"
style={{ marginRight: '10px'}}
>
返回
</Link>
</div>
</div>
<div className="color-grey-9 clearfix">
<span className="fl">{moment(memo.time).fromNow()} 发布</span>
@ -774,7 +786,7 @@ class MemoDetail extends Component {
</p>
</div>
{ memo.attachment_url && !!memo.attachment_url.length &&
{ this.props.attachments_list &&
<div>
{this.renderAttachment()}
</div>
@ -804,7 +816,7 @@ class MemoDetail extends Component {
></Comments>
{ hasMoreComments ?
<div className="memoMore">
<div className="memoMore" style={{ cursor: 'default' }}>
<a onClick={this.moreMemos}>查看更多评论</a>
<div className="writeCommentBtn" onClick={this.showCommentInput}>写评论</div>
</div>

@ -17,16 +17,22 @@ import PostItem from './PostItem'
import ForumsNavTab from './ForumsNavTab'
import { queryString } from 'educoder'
import { queryString, ThemeContext } from 'educoder'
class MemoList extends Component {
render() {
const { match, history, currentPage, memo_count ,memo_list, renderMemoList, onPaginationChange } = this.props
let theme = this.context;
return (
<React.Fragment>
<div id="forum_list" className="forum_table">
<style>{`
.forum_table_item .item_name:hover {
color: ${theme.foreground_select}
}
`}</style>
<div className="mh650 edu-back-white">
{!memo_list || memo_list.length === 0 ?
<div className="edu-tab-con-box clearfix edu-txt-center">
@ -49,5 +55,6 @@ class MemoList extends Component {
);
}
}
MemoList.contextType = ThemeContext;
export default ( MemoList );

@ -7,17 +7,17 @@ import PropTypes from 'prop-types';
import classNames from 'classnames'
import { Select,Icon } from 'antd';
import { Select,Icon, Upload, Button } from 'antd';
// demo http://react-component.github.io/upload/examples/simple.html
import Upload from 'rc-upload';
// import Upload from 'rc-upload';
import axios from 'axios'
import 'antd/lib/select/style/index.css'
import TPMMDEditor from '../tpm/challengesnew/TPMMDEditor'
import { getUrl } from 'educoder'
import { getUrl, getUploadActionUrl, appendFileSizeToUploadFileAll, appendFileSizeToUploadFile } from 'educoder'
const Option = Select.Option;
const $ = window.$;
@ -169,14 +169,16 @@ class MemoNew extends Component {
repertoires: [],
currentSelectRepertoiresIndex: -1,
repertoiresTagMap: {}
repertoiresTagMap: {},
fileList: []
}
}
onCommit() {
const { memoSubject, memoRepertoire, memoLanguage, currentMemoId, memoType } = this.state;
const { showSnackbar } = this.props;
const { showNotification } = this.props;
if (!memoSubject) {
showSnackbar('请先输入话题名称')
showNotification('请先输入话题名称')
return
}
let mdVal;
@ -184,17 +186,17 @@ class MemoNew extends Component {
mdVal = this.mdRef.current.getValue()
} catch (e) {
showSnackbar('编辑器还未加载完毕,请稍后')
showNotification('编辑器还未加载完毕,请稍后')
return
}
if (!mdVal) {
showSnackbar('请先输入话题内容')
showNotification('请先输入话题内容')
return
}
// !memoRepertoire ||
if (memoType === 5 && ( !memoLanguage || memoLanguage.length === 0 )) {
showSnackbar('请先选择技术标签')
showNotification('请先选择技术标签')
return
}
/*
@ -207,19 +209,21 @@ class MemoNew extends Component {
// collect attachments
const $ = window.$;
const attachmentsMap = {};
const attachmentIds = []
$('#attachments_fields .attachment').each(( index, item ) => {
const filename = $(item).find('.upload_filename').val();
// $($('#attachments_fields .attachment')[0]).find('input:nth-child(6)').val()
const token = $(item).find('input:nth-child(7)').val()
const attachment_id = parseInt($(item).children().last().val())
attachmentsMap[index] = {
filename,
token,
attachment_id
}
attachmentIds.push(attachment_id)
const attachmentIds = this.state.fileList.map(item => {
return item.response ? item.response.id : item.id
})
// $('#attachments_fields .attachment').each(( index, item ) => {
// const filename = $(item).find('.upload_filename').val();
// // $($('#attachments_fields .attachment')[0]).find('input:nth-child(6)').val()
// const token = $(item).find('input:nth-child(7)').val()
// const attachment_id = parseInt($(item).children().last().val())
// attachmentsMap[index] = {
// filename,
// token,
// attachment_id
// }
// attachmentIds.push(attachment_id)
// })
if (currentMemoId) {
@ -253,7 +257,7 @@ class MemoNew extends Component {
// language: memoLanguage.join(languageSeparator),
//
// },
attachments: attachmentsMap
attachment_ids: attachmentsMap
}, {
// withCredentials: true,
})
@ -263,7 +267,7 @@ class MemoNew extends Component {
window.$("html,body").animate({"scrollTop":0})
this.props.history.push(`/forums/${currentMemoId}`)
} else {
this.props.showSnackbar(message)
this.props.showNotification(message)
}
}).catch((error) => {
console.log(error)
@ -283,7 +287,7 @@ class MemoNew extends Component {
// repertoire_name: memoRepertoire,
// },
attachments: attachmentsMap
attachment_ids: attachmentsMap
}, {
// withCredentials: true,
})
@ -293,7 +297,7 @@ class MemoNew extends Component {
window.$("html,body").animate({"scrollTop":0})
this.props.history.push(`/forums/${memo_id}`)
} else {
this.props.showSnackbar(message)
this.props.showNotification(message)
}
}).catch((error) => {
console.log(error)
@ -349,7 +353,7 @@ class MemoNew extends Component {
if (tag_list) {
// this.setState({...response.data})
const { content, forum_id, id, repertoire_name, subject,
current_user, tag_list, attachments_url, memo_tags } = response.data;
current_user, tag_list, attachments_url, memo_tags, attachments } = response.data;
this.initMD(content);
// this.onRepertoiresChange(repertoire_name)
// tag -> memo_tags
@ -360,7 +364,18 @@ class MemoNew extends Component {
return item.id + ""
})
}
const fileList = attachments.map(item => {
return {
id: item.id,
uid: item.id,
name: appendFileSizeToUploadFile(item),
url: item.url,
filesize: item.filesize,
status: 'done'
}
})
this.setState({
fileList,
currentMemoId: memoId,
memoSubject: subject,
memoType: forum_id,
@ -466,7 +481,7 @@ class MemoNew extends Component {
onTagChange(value) {
if (value && value.length > 3) {
this.props.showSnackbar(`最多选择3个技术标签`)
this.props.showNotification(`最多选择3个技术标签`)
return;
}
@ -531,6 +546,55 @@ class MemoNew extends Component {
)
})
return attachments;
}
handleChange = (info) => {
let fileList = info.fileList;
this.setState({
fileList: appendFileSizeToUploadFileAll(fileList)
});
}
onAttachmentRemove = (file) => {
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);
});
}
render() {
const { match, history } = this.props
@ -538,8 +602,28 @@ class MemoNew extends Component {
// repertoires, repertoiresTagMap, currentSelectRepertoiresIndex, memoRepertoire,
tag_list,
memoSubject, memoType,
memoLanguage, attachments_url } = this.state;
memoLanguage, attachments_url, fileList } = this.state;
const memoId = match.params.memoId;
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) {
// message.error('文件大小必须小于150MB!');
}
return isLt150M;
},
};
return (
<div >
<div className="pt20 pl20 pr20 pb20 bor-bottom-greyE clearfix" style={{background: '#fff'}}>
@ -592,13 +676,13 @@ class MemoNew extends Component {
<form className="newForm">
{/* <form className="newForm">
<span id={`attachments_fields`} className="attachments_fields"
xmlns="http://www.w3.org/1999/html">
{ attachments_url && !!attachments_url.length &&
this.renderAttachment()
}
</span>
</span>
<span className="add_attachment">
<input className="file_selector" data-are-you-sure="您确定要删除吗?"
data-delete-all-files="您确定要删除所有文件吗" data-description-placeholder="可选的描述"
@ -610,19 +694,39 @@ class MemoNew extends Component {
onChange={()=>{debugger;window.addInputFiles( window.$('.file_selector')[0] ) }}
style={{'display':'none'}} type="file">
</input>
</span>
</form>
</span>
</form>*/}
<style>{`
.memo_upload.upload_1 {
margin-left: 36px;
}
.memo_upload.upload_1 .ant-upload-list {
margin-left: 30px;
}
.memo_upload.upload_1 .ant-upload-list-item-info .anticon-paper-clip {
top: 4px;
}
`}</style>
{/*<Upload {...this.uploaderProps} ref="inner"><a>开始上传</a></Upload>*/}
<Upload {...uploadProps} className="upload_1 memo_upload" >
<Button className="uploadBtn">
<Icon type="upload" /> 上传附件
</Button>
(单个文件150M以内)
</Upload>
{/* 请求status 422 */}
<div className="df uploadBtn">
{/* <Icon type="upload" ></Icon> */}
{/* <div className="df uploadBtn">
<a href="javascript:void(0);" className="fl" onClick={()=>window.$('#_file').click()}
data-tip-down="请选择文件上传">
<i className="fa fa-upload mr5 color-blue"></i>
<span className="color-blue"> <Icon type="upload" > 上传附件</Icon></span><span style={{color: '#CDCDCD', fontSize: "14px"}}>(50M)</span>
<span className="color-blue"> 上传附件
</span><span style={{color: '#CDCDCD', fontSize: "14px"}}>(50M)</span>
</a>
</div>
</div> */}
</div>
</div>
{/* TODOTODO 这里重复的html代码太多如果有其他页面有类似需求需要封装*/}

@ -27,7 +27,7 @@
cursor: pointer;
}
#forum_list .return_btn.no_mr {
margin-right: -16px;
margin-right: -24px !important;
}
div#forum_list>div {
background: #fff;

@ -7,10 +7,12 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import { getImageUrl, toPath } from 'educoder';
import { getImageUrl, toPath, ThemeContext } from 'educoder';
import moment from 'moment';
import { Tooltip } from 'antd'
class PostItem extends Component {
_toTenThousand(num) {
@ -31,17 +33,19 @@ class PostItem extends Component {
<div className="fl pr" style={{flex: 1}}>
<p className="font-16 clearfix" style={{ lineHeight: 2 }}>
{/* target="_blank" */}
<a href={`/forums/${memo.id}`} target="_blank" title={memo.subject}
className="clearfix task-hide item_name fl" style={{maxWidth: '750px'}} >
<a href={`/forums/${memo.id}`} target="_blank" title={memo.subject && memo.subject.length > 46 ? memo.subject : ''}
className="clearfix task-hide item_name fl" style={{maxWidth: '600px'}} >
{memo.subject}
</a>
{ memo.sticky && <span className="btn-top btn-cir-orange mt6 ml5 fl">置顶</span> }
{ memo.reward &&
<span className=" ml10 fl color-orange03 fl" data-tip-down={`获得平台奖励金币:${memo.reward}`}>
<i className="iconfont icon-gift font-16 mr5 fl"></i><span className="fl mt3 font-14">{memo.reward}</span>
</span>
<Tooltip title={`获得平台奖励金币:${memo.reward}`}>
<span className=" ml10 fl color-orange03 fl" >
<i className="iconfont icon-gift font-16 mr5 fl"></i><span className="fl mt3 font-14">{memo.reward}</span>
</span>
</Tooltip>
}
</p>
@ -101,4 +105,6 @@ class PostItem extends Component {
);
}
}
PostItem.contextType = ThemeContext;
export default PostItem

@ -126,7 +126,7 @@ return function wrap(WrappedComponent) {
}
const orderTypeMap = {
'hottest': 'replies_count',
'newest': 'updated_at', // 'created_at',
'newest': 'updated_at', // 'created_at',
}
const _search = this.props.history.location.search;
const parsed = queryString.parse(_search);
@ -276,7 +276,7 @@ return function wrap(WrappedComponent) {
const status = response.data.status
if (status === 0) {
this.props.showSnackbar('删除成功');
this.props.showNotification('删除成功');
// 刷新列表
this.fetchMemos();
}

@ -68,7 +68,7 @@ class MemoShixun extends Component {
let currentPage = parseInt( arg_currentPage ? arg_currentPage : (parsed.page || 1) )
const paramsObject = {
page: currentPage - 1
page: currentPage // - 1 从1开始
}
let searchValue = arg_searchValue != undefined ? arg_searchValue : this.props.searchValue

@ -53,7 +53,7 @@ class Leftdialogue extends Component{
<span className="trangle"></span>
<ImageLayerHook parentSel={`#Leftdialogue${this.props.keys}`} parentSel={`#Leftdialogue${this.props.keys}`}>
</ImageLayerHook>
<div className="sms break_word" id={`Leftdialogue${this.props.keys}`} dangerouslySetInnerHTML={{__html: markdownToHTML(this.props.objeysl.content).replace(/▁/g, "▁▁▁")}}></div>
<div className="sms break_word markdown-body" id={`Leftdialogue${this.props.keys}`} dangerouslySetInnerHTML={{__html: markdownToHTML(this.props.objeysl.content).replace(/▁/g, "▁▁▁")}}></div>
<div className="edu-txt-right mt5">
<a className="color-grey-c" onClick={()=>this.mydelete(this.props.objeysl.sender.id,this.props.objeysl.id)} >删除</a>
</div>

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import "../css/messagemy.css"
import {getImageUrl} from 'educoder';
import {getImageUrl,markdownToHTML} from 'educoder';
import { Modal,Input,Icon,Tooltip,Spin} from 'antd';
import axios from 'axios';
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
@ -656,7 +656,7 @@ class MessagChat extends Component{
</p>
<p className="color-grey-6 lineh-20 justify break_word task-hide-2 markdown-body" style={{wordBreak:"break-word"}} id="MakedownHTML" dangerouslySetInnerHTML={{__html:item.content}}>
<p className="color-grey-6 lineh-20 justify break_word task-hide-2 markdown-body" style={{wordBreak:"break-word"}} id="MakedownHTML" dangerouslySetInnerHTML={{__html: markdownToHTML(item.content).replace(/▁/g, "▁▁▁")}} >
</p>
</div>

@ -4,7 +4,7 @@ import {
Pagination,
} from "antd";
import axios from 'axios';
import {getImageUrl} from 'educoder';
import {getImageUrl,markdownToHTML} from 'educoder';
import "../css/messagemy.css"
//消息页面
class MessagSub extends Component{
@ -127,6 +127,7 @@ class MessagSub extends Component{
// 账号管理页-认证信息
return window.open("/account/certification")
}
return ;
case "CancelUserAuthentication" :
// 账号管理页-认证信息
return window.open("/account/certification")
@ -142,6 +143,7 @@ class MessagSub extends Component{
// 账号管理页/account/profile
return window.open("/account/profile")
}
return ;
case "ApplyAddSchools" :
if(item.tiding_type==="Apply"){
// 单位审批
@ -151,6 +153,7 @@ class MessagSub extends Component{
// 账号管理页
return window.open("/account/profile")
}
return ;
case "ApplyAction" :
switch (item.parent_container_type) {
case "ApplyShixun" :
@ -181,12 +184,19 @@ class MessagSub extends Component{
return window.open("/account/profile")
}
}
return ;
case 'JoinCourse' :
// 课堂详情页 :id =
return window.open(`/courses/${item.belong_container_id}/shixun_homeworks/${item.container_id}`)
return window.open(`/courses/${item.belong_container_id}/teachers`)
case 'StudentJoinCourse':
// 课堂详情页 :id = container_id
return window.open(`/courses/${item.belong_container_id}/shixun_homeworks/${item.container_id}`)
if(item.tiding_type === 'Apply') {
return window.open(`/courses/${item.belong_container_id}/teachers`);
}
if(item.tiding_type === 'System'){
//教学案例详情 :id = container_id
return window.open(`/courses/${item.belong_container_id}/students`);
}
case 'DealCourse':
// 课堂详情页 :id = container_id
return window.open(`/courses/${item.belong_container_id}/shixun_homeworks/${item.container_id}`)
@ -195,6 +205,10 @@ class MessagSub extends Component{
return window.open(`/courses/${item.belong_container_id}/shixun_homeworks/${item.container_id}`)
case 'Course' :
// 课堂详情页 :id = container_id
if(item.tiding_type==="Delete"){
return;
}
return window.open(`/courses/${item.belong_container_id}/shixun_homeworks/${item.container_id}`)
case 'ArchiveCourse' :
// 课堂详情页 :id = container_id
@ -214,16 +228,17 @@ class MessagSub extends Component{
//学生作业页 homework = parent_container_id
if(item.homework_type==="normal"){
//普通作业
return window.open(`/courses/${item.belong_container_id}/common_homeworks/${item.parent_container_id}/list`)
return window.open(`/courses/${item.belong_container_id}/common_homeworks/${item.parent_container_id}/question`)
}
if(item.homework_type==="group"){
//分组作业
return window.open(`/courses/${item.belong_container_id}/group_homeworks/${item.parent_container_id}/list`)
return window.open(`/courses/${item.belong_container_id}/group_homeworks/${item.parent_container_id}/question`)
}
if(item.homework_type==="practice"){
//实训作业
return window.open(`/courses/${item.belong_container_id}/shixun_homeworks/${item.parent_container_id}/list?tab=0`)
return window.open(`/courses/${item.belong_container_id}/shixun_homeworks/${item.parent_container_id}/list?tab=1`)
}
return "";
case "GraduationTopic" :
// 毕业目标页 parent_container_id
return window.open(`/courses/${item.belong_container_id}/graduation_topics/${item.parent_container_id}/detail`)
@ -231,20 +246,24 @@ class MessagSub extends Component{
//学生作业页
if(item.homework_type==="normal"){
//普通作业
return window.open(`/courses/${item.belong_container_id}/common_homeworks/${item.parent_container_id}/list`)
return window.open(`/courses/${item.belong_container_id}/common_homeworks/${item.parent_container_id}/question`)
}
if(item.homework_type==="group"){
//分组作业
return window.open(`/courses/${item.belong_container_id}/group_homeworks/${item.parent_container_id}/list`)
return window.open(`/courses/${item.belong_container_id}/group_homeworks/${item.parent_container_id}/question`)
}
if(item.homework_type==="practice"){
//实训作业
return window.open(`/courses/${item.belong_container_id}/shixun_homeworks/${item.parent_container_id}/list?tab=0`)
return window.open(`/courses/${item.belong_container_id}/shixun_homeworks/${item.parent_container_id}/list?tab=1`)
}
return "";
}
case "Memo" :
// 交流问答页 :id = parent_container_id
return window.open(`/forums/${item.parent_container_id}`)
return window.open(`/forums/${item.parent_container_id}`);
case "Message" :
// 交流问答页 :id = parent_container_id
return window.open(`/forums/`);
case "Watcher" :
// 用户个人中心页 :id = item.trigger_user.login
return window.open(`/users/${item.trigger_user.login}/courses`)
@ -281,10 +300,12 @@ class MessagSub extends Component{
switch (item.parent_container_type) {
case "CommitExercise":
// 课堂-学生试卷详情 :id = container_id :user_id = trigger_user.id
return window.open(` /courses/${item.belong_container_id}/exercises/${item.container_id}/student_exercise_list?tab=0`)
return window.open(` /courses/${item.belong_container_id}/exercises/${item.container_id}/student_exercise_list?tab=0`);
//记得跳评阅页面
case "ExerciseScore":
// 课堂-学生试卷详情 :id = container_id :user_id = trigger_user.id
return window.open(` /courses/${item.belong_container_id}/exercises/${item.container_id}/student_exercise_list?tab=0`)
return window.open(` /courses/${item.belong_container_id}/exercises/${item.container_id}/student_exercise_list?tab=0`);
//记得跳评阅页面
default :
// 课堂-试卷列表详情 :id = container_id
return window.open(`/courses/${item.belong_container_id}/exercises/${item.container_id}`)
@ -365,43 +386,46 @@ class MessagSub extends Component{
//课堂-作业 :id = container_id
if(item.homework_type==="normal"){
//普通作业
return window.open(`/courses/${item.belong_container_id}/common_homeworks/${item.container_id}/list`)
return window.open(`/courses/${item.belong_container_id}/common_homeworks/${item.parent_container_id}/${item.container_id}/appraise`)
}
if(item.homework_type==="group"){
//分组作业
return window.open(`/courses/${item.belong_container_id}/group_homeworks/${item.container_id}/list`)
//分组作业/courses/1208/group_homeworks/22373/1219130/appraise
return window.open(`/courses/${item.belong_container_id}/group_homeworks/${item.parent_container_id}/${item.container_id}/appraise`)
}
if(item.homework_type==="practice"){
//实训作业
return window.open(`/courses/${item.belong_container_id}/shixun_homeworks/${item.container_id}/list?tab=0`)
return window.open(`/courses/${item.belong_container_id}/shixun_homeworks/${item.parent_container_id}/list?tab=0`)
}
case "StudentWorksScore" :
//课堂-作业 :id = parent_container_id
if(item.homework_type==="normal"){
//普通作业
return window.open(`/courses/${item.belong_container_id}/common_homeworks/${item.parent_container_id}/list`)
}
if(item.homework_type==="group"){
//分组作业
return window.open(`/courses/${item.belong_container_id}/group_homeworks/${item.parent_container_id}/list`)
}
if(item.homework_type==="practice"){
//实训作业
return window.open(`/courses/${item.belong_container_id}/shixun_homeworks/${item.parent_container_id}/list?tab=0`)
}
// if(item.homework_type==="normal"){
// //普通作业
// return window.open(`/courses/${item.belong_container_id}/common_homeworks/${item.parent_container_id}/list`)
// }
// if(item.homework_type==="group"){
// //分组作业
// return window.open(`/courses/${item.belong_container_id}/group_homeworks/${item.parent_container_id}/list`)
// }
// if(item.homework_type==="practice"){
// //实训作业
// return window.open(`/courses/${item.belong_container_id}/shixun_homeworks/${item.parent_container_id}/list?tab=0`)
// }
return window.open(`/courses/${item.belong_container_id}/common_homeworks/${item.trigger_user.id}/${item.parent_container_id}/appraise`);
case "StudentWorksScoresAppeal" :
if(item.homework_type==="normal"){
//普通作业
return window.open(`courses/${item.belong_container_id}/common_homeworks/${item.parent_container_id}/list`)
}
if(item.homework_type==="group"){
//分组作业
return window.open(`/courses/${item.belong_container_id}/group_homeworks/${item.parent_container_id}/list`)
}
if(item.homework_type==="practice"){
//实训作业
return window.open(`/courses/${item.belong_container_id}/shixun_homeworks/${item.parent_container_id}/list?tab=0`)
}
// if(item.homework_type==="normal"){
// //普通作业
// return window.open(`courses/${item.belong_container_id}/common_homeworks/${item.parent_container_id}/list`)
// }
// if(item.homework_type==="group"){
// //分组作业
// return window.open(`/courses/${item.belong_container_id}/group_homeworks/${item.parent_container_id}/list`)
// }
// if(item.homework_type==="practice"){
// //实训作业
// return window.open(`/courses/${item.belong_container_id}/shixun_homeworks/${item.parent_container_id}/list?tab=0`)
// }
return window.open(`/courses/${item.belong_container_id}/common_homeworks/${item.trigger_user.id}/${item.parent_container_id}/appraise`);
case "ChallengeWorkScore" :
return '';
case "SendMessage" :
@ -409,7 +433,7 @@ class MessagSub extends Component{
return window.open(`/managements/mirror_repository`)
case "Journal" :
//项目Issue页 :id = parent_container_id
return window.open(`/projects/${item.parent_container_id}/issues?remote=true`)
return window.open(`/issues/${item.parent_container_id}`);
case "Issue" :
//项目Issue页 :id = container_id
return window.open(`/issues/${item.container_id}`)
@ -429,6 +453,9 @@ class MessagSub extends Component{
return window.open(`/moop_cases/${item.container_id}`)
}
case "ProjectPackage" :
if(item.tiding_type==="Destroyed"){
return ;
}
if(item.tiding_type==="Destroyed_end"){
return ;
}else {
@ -464,11 +491,11 @@ class MessagSub extends Component{
<li className={typeysl&&typeysl==="project_package"?"active":""}><a onClick={(s,i)=>this.getdata("project_package",1)}>众包提醒</a></li>
<li className={typeysl&&typeysl==="interactive"?"active":""}><a onClick={(s,i)=>this.getdata("interactive",1)}>互动提醒</a></li>
<li className={typeysl&&typeysl==="interaction"?"active":""}><a onClick={(s,i)=>this.getdata("interaction",1)}>互动提醒</a></li>
<li className={typeysl&&typeysl==="apply"?"active":""}><a onClick={(s,i)=>this.getdata("apply",1)}>审核</a></li>
<li className={typeysl&&typeysl==="system"?"active":""}><a onClick={(s,i)=>this.getdata("system",1)}>通知</a></li>
<li className={typeysl&&typeysl==="notice"?"active":""}><a onClick={(s,i)=>this.getdata("notice",1)}>通知</a></li>
@ -478,10 +505,10 @@ class MessagSub extends Component{
<div className="bor-top-greyE mycenter">
{/*这里可以进行数据处理*/}
<div className="myw100baifenbi">
<Spin size="large" className="myw100baifenbi" spinning={isSpin}>
<Spin size="large" className="myw100baifenbi mt10" spinning={isSpin}>
{
data===undefined?
data===undefined?"":data.length===0?
<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>
@ -497,6 +524,11 @@ class MessagSub extends Component{
if(item.parent_container_type==="Principal"){
boolps=false;
}
if(item.parent_container_type==="HomeworkCommon"){
if(item.homework_type===null||item.homework_type===undefined){
boolps=false;
}
}
}
// if(item.container_type==="HomeworkCommon"){
// if(item.parent_container_type==="AnonymousAppeal"){
@ -507,7 +539,16 @@ class MessagSub extends Component{
if(item.tiding_type==="Destroyed_end"){
boolps=false;
}
if(item.tiding_type==="Destroyed"){
boolps=false;
}
}
if(item.container_type==="Course"){
if(item.tiding_type==="Delete"){
boolps=false;
}
}
return(
<div className="pl25 ridinglist edu-back-white" key={key}>
<div className={boolps===true?"ridinglist-sub clearfix df tiding_item":"ridinglist-subs clearfix df tiding_item"} onClick={()=>this.gettourl(item)}>
@ -534,7 +575,7 @@ class MessagSub extends Component{
`
}
</style>
<p className="color-grey-6 break_word_firefox yslspansk markdown-body" style={{wordBreak: "break-word"}} dangerouslySetInnerHTML={{__html:item.content}}></p>
<p className="color-grey-6 break_word_firefox yslspansk markdown-body mt10" style={{wordBreak: "break-word"}} dangerouslySetInnerHTML={{__html: markdownToHTML(item.content).replace(/▁/g, "▁▁▁")}} ></p>
</div>
<span className={item.new_tiding===true?"new-point fr mr40 mt22":""}></span>

@ -5,7 +5,7 @@ import {
} from "antd";
import axios from 'axios';
import moment from 'moment';
import {getImageUrl} from 'educoder';
import {getImageUrl,markdownToHTML} from 'educoder';
import "../css/messagemy.css"
import WriteaprivateletterModal from '../messagemodal/WriteaprivateletterModal';
//私信页面
@ -160,14 +160,13 @@ class MessagePrivate extends Component{
<Spin size="large" className="myw100baifenbi" spinning={isSpin}>
{
data===undefined?
data===undefined?"":data.length===0?
<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>
:data.map((item,key)=>{
return(
<div className="private-item clearfix df" key={key} onClick={()=>this.smyJump(3,item.target.id)}>
<a className="fl mr10 private_message_a" onMouseDown={()=>this.myCome(item)}>
<img onMouseDown={()=>this.myCome(item)} src={getImageUrl("images/"+item.target.image_url)} className="radius myimgw48 myimgh48"/>
@ -179,12 +178,11 @@ class MessagePrivate extends Component{
<span className="color-grey-c mr20">[{item.message_count}{"条"}]</span>
<span className="color-grey-c">{moment(item.send_time).fromNow()}</span>
</p>
<span className="color-grey-6 break_word_firefox markdown-body" dangerouslySetInnerHTML={{__html:item.content}}></span>
<div className="color-grey-6 break_word_firefox markdown-body mt10" dangerouslySetInnerHTML={{__html: markdownToHTML(item.content).replace(/▁/g, "▁▁▁")}} ></div>
</div>
{item.unread === true ?<span className="new-point fr mt22"></span>:""}
</div>
)
})}
</Spin>

@ -205,11 +205,11 @@ class Messagerouting extends Component{
<ul className="edu-back-white">
<li className={routing ===1?"nav pr active":"nav pr"}>
<a onClick={(value,child)=>this.SwitchonClick(1,this.child)}>消息</a>
{unread_message_count===0?"":unread_message_count>0?<span className="new-info">{unread_message_count}</span>:""}
{unread_tiding_count===0?"":unread_tiding_count>0?<span className="new-info">{unread_tiding_count}</span>:""}
</li>
<li className={routing ===2?"nav pr active":routing ===3?"nav pr active":"nav pr"}>
<a onClick={(value,child)=>this.SwitchonClick(2,this.child)}>私信</a>
{unread_tiding_count===0?"":unread_tiding_count>0?<span className="new-info">{unread_tiding_count}</span>:""}
{unread_message_count===0?"":unread_message_count>0?<span className="new-info">{unread_message_count}</span>:""}
</li>
</ul>
</div>

@ -53,7 +53,7 @@ class Rightdialogue extends Component{
<span className="trangle"></span>
<ImageLayerHook parentSel={`#Rightdialogue${this.props.keys}`} parentSel={`#Rightdialogue${this.props.keys}`}>
</ImageLayerHook>
<div className="sms break_word" id={`Rightdialogue${this.props.keys}`} dangerouslySetInnerHTML={{__html: markdownToHTML(this.props.objeysl.content).replace(/▁/g, "▁▁▁")}}></div>
<div className="sms break_word markdown-body" id={`Rightdialogue${this.props.keys}`} dangerouslySetInnerHTML={{__html: markdownToHTML(this.props.objeysl.content).replace(/▁/g, "▁▁▁")}}></div>
<div className="edu-txt-left mt5">
<a className="color-grey-c" onClick={()=>this.mydelete(this.props.objeysl.sender.id,this.props.objeysl.id)}
>删除</a>

@ -0,0 +1,165 @@
import React,{ Component } from "react";
import './css/moopCases.css'
import '../courses/css/Courses.css'
import { getImageUrl , MarkdownToHtml , ActionBtn } from 'educoder';
import Tags from './CaseTags'
import axios from 'axios';
import Modals from '../modals/Modals'
class CaseDetail extends Component{
constructor(props){
super(props);
this.state={
modalsType:"",
modalsTopval:"",
modalsBottomval:"",
modalCancel:"",
}
}
componentDidMount =()=>{
let caseID = this.props.match.params.caseID;
this.props.getDetail(caseID);
}
// 是否删除
delCases=()=>{
this.setState({
modalsType:true,
modalsTopval:"是否确认删除?",
modalsBottomval:""
})
}
// 取消删除
cancelDelClasses=()=>{
this.setState({
modalsType:false,
modalsTopval:"",
modalsBottomval:""
})
}
// 确定删除
sureDelClasses=()=>{
let caseID = this.props.match.params.caseID;
let url =`/libraries/${caseID}.json`;
axios.delete(url).then((result)=>{
if(result){
this.props.showNotification("删除成功");
this.props.history.push("/moop_cases");
}
}).catch((error)=>{
console.log(error);
})
}
render(){
let { CaseDetail , praise_count , creator , operation , user_praised , tags , attachments }=this.props
let {
modalsType,
modalsTopval,
modalsBottomval,
} = this.state;
return(
<div className="educontent mt10 mb50">
<Modals
modalsType={modalsType}
modalsTopval={modalsTopval}
modalsBottomval={modalsBottomval}
modalCancel={this.cancelDelClasses}
modalSave={this.sureDelClasses}
>
</Modals>
<p className="mt10 mb20 clearfix lineh-20">
<a href="/moop_cases" className="color-grey-9">教学案例</a> &gt; <span className="color-grey-3">{ CaseDetail && CaseDetail.title}</span>
</p>
<p className="lineh-25 mb20 clearfix">
<span className="font-22 fl mr10 task-hide" style={{maxWidth:"800px"}}>
{ CaseDetail && CaseDetail.title}
</span>
<span className="mt5 fl">
<Tags tags={tags}></Tags>
{
CaseDetail && CaseDetail.status == "pending" && <span class="edu-filter-btn fl cdefault edu-activity-green ml10">草稿</span>
}
</span>
<a href="/moop_cases" className="fr color-grey-9 mt5">返回</a>
</p>
<div className="edu-back-white">
<div className="padding30">
<div className="df">
<a href="/users/moop"><img alt="82274?1563067098" className="radius mr15 mt3" height="50" src={getImageUrl(`images/${creator && creator.image_url}`)} width="50" /></a>
<div className="flex1">
<li className="clearfix mb5">
<span className="font-16 fl">{creator && creator.name}</span>
{
operation && operation.can_deletable ? <ActionBtn style="greyLine" onClick={this.delCases} className="fr">删除</ActionBtn>:""
}
{
operation && operation.can_editable ? <ActionBtn style="colorBlue" to={`/moop_cases/${this.props.match.params.caseID}/edit`} className="fr mr20">编辑</ActionBtn>:""
}
</li>
<li className="clearfix">
<span className="fl color-grey-9 mr20">{creator && creator.school_name}</span>
<span className="fr">
<span className="fl color-grey-9 mr30">编码<span className="color-grey-6">{CaseDetail && CaseDetail.uuid}</span></span>
<span className="fl color-grey-9">发布时间<span className="color-grey-6">{CaseDetail && CaseDetail.published_at}</span></span>
</span>
</li>
</div>
</div>
<div>
<span className="fl color-grey-9">作者</span>{CaseDetail && CaseDetail.author_name}/{CaseDetail && CaseDetail.author_school_name}
</div>
<style>
{`
.setMDStyle .editormd-html-preview{
width:100%!important;
}
`}
</style>
<div class="mt20 setMDStyle">
{ CaseDetail && CaseDetail.content && <MarkdownToHtml content={CaseDetail.content} selector="casesDetail" style={{width:"100%!important"}}></MarkdownToHtml>}
</div>
{ attachments &&
<div className="mt10">
{
attachments.map((item,key)=>{
return(
<p>
<a className="color-grey">
<i className="font-14 color-green iconfont icon-fujian mr8"></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>
</p>
)
})
}
</div>
}
<div class="mt40">
{
user_praised ?
<p className="pointsBtn pointedBtn">
<span>已赞</span>
<span>{praise_count}</span>
</p>
:
<p onClick = {()=>this.props.praisePoint(this.props.match.params.caseID)} className="pointsBtn">
<span><i class="iconfont icon-dianzan"></i></span>
<span>{praise_count}</span>
</p>
}
</div>
</div>
</div>
</div>
)
}
}
export default CaseDetail;

@ -0,0 +1,48 @@
import React,{ Component } from "react";
import './css/moopCases.css'
import '../courses/css/Courses.css'
import { getUrl } from 'educoder';
import Tags from './CaseTags'
class CaseItem extends Component{
constructor(props){
super(props);
}
render(){
let { libraries } = this.props;
console.log(libraries)
return(
<div className="library-list-container">
{
libraries && libraries.map((item,key)=>{
return(
<li className="library_list_item">
<img alt={item.id} className="mr15 mt3 radius4" height="90" src={getUrl(`${item.cover_url}`)} width="120" />
<div className="flex1">
<p className="clearfix mb25 lineh-40">
<a href={`/moop_cases/${item.id}`} className="task-hide font-22 library_l_name">{item.title}</a>
<span className="fl mt10"><Tags tags={item.tags}></Tags></span>
</p>
<p className="clearfix lineh-20">
<span className="color-grey-3 mr10">{item.author_name}</span>
<span className="color-grey-3 mr20">{item.author_school_name}</span>
<span className="color-grey-c fr">
<span className="color-grey-c mr20"><span className=" item-group-icon mr5"><i className="fa fa-eye"></i></span>{item.visited_count} </span>
<span className="color-grey-c mr20"><span className=" item-group-icon mr5"><i className="fa fa-thumbs-o-up"></i></span>{item.praise_count} </span>
<span className="color-grey-c"><span className=" item-group-icon mr5"><i className="fa fa-download"></i></span>{item.download_count} </span>
</span>
</p>
</div>
</li>
)
})
}
</div>
)
}
}
export default CaseItem;

@ -0,0 +1,134 @@
import React,{ Component } from "react";
import { Input , Spin , Pagination } from "antd";
import './css/moopCases.css'
import '../courses/css/Courses.css'
import { ActionBtn } from 'educoder';
import axios from 'axios'
import NoneData from '../courses/coursesPublic/NoneData'
import mainImg from '../../images/moop_cases/teach_ex.jpg'
import CaseItem from './CaseItem'
const Search = Input.Search;
class CaseList extends Component{
constructor(props){
super(props);
this.state={
type:0,
search:undefined,
page:1,
pageSize:20,
libraries:undefined,
totalCount:undefined
}
}
componentDidMount = () =>{
let { type , search , page , pageSize } = this.state;
this.InitList(type,search,page,pageSize);
}
// 列表
InitList = (type,search,page,pageSize) =>{
let url = `/libraries.json`;
axios.get(url,{params:{
type:type == 0 ? undefined : "mine",
keyword:search,
page,
per_page:pageSize
}}).then((result)=>{
if(result){
this.setState({
libraries:result.data.libraries,
totalCount:result.data.count
})
}
}).catch((error)=>{
console.log(error);
})
}
// tab切换
changeType = (type) =>{
this.setState({
type,
page:1
})
let { search , page , pageSize } = this.state;
this.InitList(type,search,page,pageSize);
}
// 输入搜索内容
inputStudent=(e)=>{
this.setState({
search:e.target.value
})
}
// 搜索
searchInfo = () =>{
let { type , search , pageSize } = this.state;
this.InitList( type , search , 1 , pageSize );
}
// 切换分页
onChangePage =(pageNumber)=>{
this.setState({
page:pageNumber
})
let { type , search , pageSize } = this.state;
this.InitList( type , search , pageNumber , pageSize );
}
render(){
let { type , search ,libraries , totalCount ,pageSize ,page } = this.state;
return(
<React.Fragment>
<img src={mainImg} width="100%" />
<div className="educontent">
<div className="edu-back-white mb30 mt30">
<p className="padding20-30 clearfix bor-bottom-greyE">
<span className="font-18 fl color-grey-3">教学案例</span>
<ActionBtn style="colorBlue" className="fr" onClick={() => this.addQuestion(null, Q_TYPE_SINGLE)}>发布案例</ActionBtn>
</p>
<div className="clearfix pl30 pr30">
<ul className="fl library_nav mt25">
<li className={type == 0 ? "active" :""} onClick={()=>this.changeType(0)}>
<a href="javascript:void(0)">全部</a>
</li>
<li className={type == 1 ? "active" :""} onClick={()=>this.changeType(1)}>
<a href="javascript:void(0)">我的</a>
</li>
</ul>
<div className="fr mt16 mb16 searchView"style={{width:"300px"}}>
<Search
value={search}
placeholder="输入教学案例标题、作者、单位进行检索"
onInput={this.inputStudent}
onSearch={this.searchInfo}
></Search>
</div>
</div>
</div>
{
libraries && libraries.length > 0 && <CaseItem {...this.props} {...this.state} libraries={libraries}></CaseItem>
}
{
libraries && libraries.length == 0 && <div className="mb50"><NoneData></NoneData></div>
}
{
totalCount && totalCount > pageSize &&
<div className="mb50 edu-txt-center clearfix">
<Pagination defaultCurrent={page} current={page} pageSize={pageSize} showQuickJumper onChange={this.onChangePage} total={totalCount}></Pagination>
</div>
}
</div>
</React.Fragment>
)
}
}
export default CaseList

@ -0,0 +1,433 @@
import React,{ Component } from "react";
import './css/moopCases.css'
import '../courses/css/Courses.css'
import { Form , Input , Upload , Button , Icon , message , Tooltip } from "antd";
import { getImageUrl , MarkdownToHtml , ActionBtn , appendFileSizeToUploadFile , appendFileSizeToUploadFileAll , getUrl , getUploadActionUrl } from 'educoder';
import Tags from './CaseTags'
import axios from 'axios';
import TPMMDEditor from '../tpm/challengesnew/TPMMDEditor';
import _ from 'lodash'
const { Dragger } = Upload;
function beforeUpload(file) {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
message.error('You can only upload JPG/PNG file!');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('Image must smaller than 2MB!');
}
return isJpgOrPng && isLt2M;
}
function getBase64(img, callback) {
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result));
reader.readAsDataURL(img);
}
const $ = window.$;
class CaseNew extends Component{
constructor(props){
super(props);
this.DescMdRef = React.createRef();
this.state={
casesTags:[],
contentFileList:[],
filesID:[],
imageUrl:undefined,
loading: false,
checkTag:false,
checkFile:false,
coverID:undefined
}
}
// 上传附件-删除确认框
onAttachmentRemove = (file, stateName) => {
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.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);
});
}
// 上传附件-change
handleContentUploadChange = (info) => {
let contentFileList = info.fileList;
this.setState({ contentFileList: appendFileSizeToUploadFileAll(contentFileList)});
let list = appendFileSizeToUploadFileAll(contentFileList);
let arr = list.map(item=>{
return ( item.response && item.response.id )
})
this.setState({
filesID:arr,
checkFile:arr.length > 0 ? false : true
})
console.log("fujian");
console.log(list.map(item=>{
return ( item.response && item.response.id )
}));
}
// 上传封面图-change
handleChange = (info) => {
if (info.file.status === 'uploading') {
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
// Get this url from response in real world.
getBase64(info.file.originFileObj, imageUrl =>
this.setState({
imageUrl,
loading: false
}),
);
console.log(info.file);
this.setState({
coverID:info.file.response && info.file.response.id
})
}
};
// 编辑时加载数据
componentDidMount=()=>{
if(this.props.match.params.caseID){
this.InitEditData();
}
}
componentDidUpdate=(prevState)=>{
if(this.props.CaseDetail && prevState.CaseDetail != this.props.CaseDetail){
this.props.form.setFieldsValue({
caseTitle:this.props.CaseDetail.title,
userName:this.props.CaseDetail.author_name,
userUnit:this.props.CaseDetail.author_school_name,
})
this.setState({
contentFileList:this.props.CaseDetail.attachments.map(item => {
return {
id: item.id,
uid: item.id,
name: appendFileSizeToUploadFile(item),
url: item.url,
filesize: item.filesize,
status: 'done'
}
}),
coverID:this.props.cover && this.props.cover.id,
imageUrl:this.props.CaseDetail.cover && getImageUrl(this.props.CaseDetail.cover.url),
casesTags:this.props.tags.map(item=>{
return (item.id);
})
})
}
}
InitEditData=()=>{
let caseID = this.props.match.params.caseID;
this.props.getDetail(caseID);
}
// 申请提交和保存
handleSubmit = (type) => {
let caseID = this.props.match.params.caseID;
console.log(type);
this.props.form.validateFieldsAndScroll((err, values) => {
let { casesTags , filesID } = this.state;
if(casesTags.length == 0){
$("html").animate({ scrollTop: $("#tagFormItem").offset().top - 100 });
this.setState({
checkTag:true
})
return;
}
if(filesID.length == 0){
$("html").animate({ scrollTop: $("#fileFormItem").offset().top - 100 });
this.setState({
checkFile:true
})
return;
}
//const mdContnet = this.DescMdRef.current.getValue().trim();
console.log(values);
let url = caseID ? `/libraries/${caseID}.json`: `/libraries.json`;
if(caseID){
axios.put((url),{
title:values.caseTitle,
author_name:values.userName,
author_school_name:values.userUnit,
content:values.description,
attachment_ids:this.state.filesID,
tag_ids:this.state.casesTags,
cover_id:this.state.coverID,
publish:type == 'save' ? false : true
}).then((result)=>{
if(result){
this.props.showNotification(type == 'save' ? `案例保存成功!`: `提交成功!`);
this.props.history.push(type == 'save' ? `/moop_cases/${result.data.id}` : `/moop_cases/${result.data.id}/publish_success`);
}
}).catch((error)=>{
console.log(error);
})
}else{
axios.post((url),{
title:values.caseTitle,
author_name:values.userName,
author_school_name:values.userUnit,
content:values.description,
attachment_ids:this.state.filesID,
tag_ids:this.state.casesTags,
cover_id:this.state.coverID,
publish:type == 'save' ? false : true
}).then((result)=>{
if(result){
this.props.showNotification(type == 'save' ? `案例保存成功!`: `提交成功!`);
this.props.history.push(type == 'save' ? `/moop_cases/${result.data.id}` : `/moop_cases/${result.data.id}/publish_success`);
}
}).catch((error)=>{
console.log(error);
})
}
})
}
// 选择标签
changeType=(type)=>{
let tags = [];
if(this.state.casesTags.indexOf(type) > -1){
tags = this.state.casesTags.filter(item => item != type);
}else{
tags = this.state.casesTags.concat(type);
}
const tagUniqed = _.uniq(tags);
this.setState({
casesTags: tagUniqed,
checkTag:tags.length > 0 ? false : true
})
}
render(){
let { caseID } = this.props.match.params;
let { CaseDetail } = this.props;
let { casesTags , contentFileList , imageUrl , checkTag , checkFile } = this.state;
const {getFieldDecorator} = this.props.form;
// 上传附件点击事件
const uploadProps = {
width: 600,
multiple: true,
fileList:contentFileList,
action: `${getUploadActionUrl()}`,
onChange: this.handleContentUploadChange,
onRemove: (file) => this.onAttachmentRemove(file, 'contentFileList'),
beforeUpload: (file) => {
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
message.error('文件大小必须小于150MB!');
}
return isLt150M;
}
};
// 上传封面图-html
const uploadButton = (
<div>
<Icon className='font-36 color-grey-c' type={this.state.loading ? 'loading' : 'plus'} />
</div>
);
// 上传封面图点击事件
const uploadCover = {
listType:"picture-card",
className:"avatar-uploader",
showUploadList:false,
action:`${getUploadActionUrl()}`,
onChange:this.handleChange,
}
return(
<div className="educontent mt10 mb50">
<style>
{
`
.newCases .ant-col.ant-form-item-label{
float:left;
margin-right:20px;
height:35px;
line-height:35px;
}
.newCaseUpload{
width: 100%;
background: #F2F9FF;
justify-content: center;
align-items: center;
display: -webkit-flex;
text-align: center;
height: 120px;
border-radius: 4px;
border: 1px dashed #4cacff;
}
.newCases .ant-form-item{
margin-bottom:20px!important ;
}
.newCases .ant-col.ant-form-item-control-wrapper{
position:relative;
}
.newCases .ant-form-explain{
position:absolute;
bottom:-18px;
left:76px;
padding-left: 7px;
}
.newCases .resetLeft .ant-form-explain{
left:0px;
}
.newCases .resetBottom .ant-form-explain{
bottom:2px;
}
`
}
</style>
<p className="mt10 mb20 clearfix lineh-20">
<a href="/moop_cases" className="color-grey-9">教学案例</a> &gt; <span className="color-grey-3">{ caseID ? "" : "" }</span>
</p>
<p class="lineh-25 font-22 mb20">上传教学案例</p>
<Form onSubmit={this.handleSubmit} className={"newCases"}>
<div className="padding30 edu-back-white">
<Form.Item label="标题">
{getFieldDecorator('caseTitle', {
rules: [{required: true, message: "案例标题不能为空"}],
})(
<Input placeholder="例如:软件工程教学案例" className="greyInput winput-300-35 mr20 fl"/>
)}
<span className="color-grey-c font-12 fl">简明扼要介绍文档/视频所包含的主要的内容</span>
</Form.Item>
<div className="clearfix">
<Form.Item label="作者" className="fl with22">
{getFieldDecorator('userName', {
rules: [{required: true, message: "请输入作者姓名"}],
})(
<Input placeholder="请输入姓名" className="greyInput winput-120-35 mr20 fl winput150"/>
)}
</Form.Item>
<Form.Item className="fl resetLeft">
{getFieldDecorator('userUnit', {
rules: [{required: true, message: "请输入作者单位名称"}],
})(
<Input placeholder="请输入作者单位名称" className="greyInput winput-300-35 mr20 fl"/>
)}
</Form.Item>
</div>
<div className={checkTag==true ? "clearfix mb20 pr has-error" : "clearfix mb20"} id="tagFormItem">
<span className="upload_Title must">标签</span>
<ul className="fl libraries_tab">
<li className={ casesTags.indexOf(1) > -1 ? "active" :"" } onClick={()=>this.changeType(1)}>获奖案例</li>
<li className={ casesTags.indexOf(2) > -1 ? "active" :"" } onClick={()=>this.changeType(2)}>入库案例</li>
</ul>
{
checkTag && <div class="ant-form-explain">请选择标签</div>
}
</div>
<Form.Item label="描述" className="resetBottom" style={{marginBottom:"0px"}}>
{getFieldDecorator('description', {
rules: [{
required: true, message: '请输入描述内容'
}],
})(
<TPMMDEditor ref={this.DescMdRef} placeholder="请添加描述" mdID={'caseContentMD'} refreshTimeout={1500}
watch={true} className="caseMessageMD" initValue={CaseDetail && CaseDetail.content}></TPMMDEditor>
)}
</Form.Item>
<div className={checkFile == true ? "clearfix mb20 pr has-error" : "clearfix mb20"} id="fileFormItem" style={{marginLeft:"76px"}}>
<Dragger {...uploadProps} className="librariesField upload_1">
<p className="ant-upload-text color-blue font-18 mb20">上传附件</p>
<p className="ant-upload-text color-grey-c">从我的电脑选择要上传的文档按住CTRL可以上传多份文档单个文件最大限制150MB</p>
</Dragger>
{
checkFile && <div style={{left:"0px",bottom:"-21px"}} class="ant-form-explain">请先上传附件</div>
}
</div>
<p className="lineh-25 mt20 mb10 clearfix">
<span className="upload_Title" style={{marginRight:"12px"}}>封面图</span><span class="color-grey-c fl lineh-35">120*90 px</span>
</p>
<div style={{marginLeft:"76px"}} className="uploadImage">
<Upload {...uploadCover}>
{ imageUrl ?
<Tooltip title="重新上传">
<img src={imageUrl} alt="avatar" style={{ width: '100%' }} />
</Tooltip>
:
<Tooltip title="上传图片">
{uploadButton}
</Tooltip>
}
</Upload>
</div>
</div>
<div className="padding30 bor-top-greyE edu-back-white">
<li className="lineh-25 color-grey-6 font-18 mb20">审核说明</li>
<ul className="font-16">
<li>平台管理员将对每天新上传的文档进行审核审核通过的文档将公开显示否则将私有化或移除</li>
</ul>
</div>
<div className="padding30 bor-top-greyE edu-back-white">
<li className="lineh-25 color-grey-6 font-18 mb20">温馨提示</li>
<ul className="font-16 lineh-30">
<li>1.请勿上传已设置加密口令的文档资源</li>
<li>2.可以上传符合教学案例标准的文档资料
<a className="color-blue" target="_blank" href="https://www.educoder.net/courses/1309/boards/5909/messages/34799">案例入库标准</a>
<a target="_blank" className="color-blue" href="https://www.educoder.net/courses/1309/boards/5909/messages/34798">案例使用说明书</a>100MB</li>
<li>3.请确保上传内容无侵权或违反国家关于互联网政策的不良行为</li>
<li>4.请使用ChromeFirefoxSafariIE11及以上版本浏览器</li>
</ul>
</div>
<Form.Item>
<div className="clearfix mt30 mb30">
{
!caseID && <Button type="primary" onClick={()=>this.handleSubmit("submit")} className="defalutSubmitbtn fl mr20">申请发布</Button>
}
<a className="defalutCancelbtn fl" onClick={()=>this.handleSubmit("save")}>保存</ a>
</div>
</Form.Item>
</Form>
</div>
)
}
}
const WrappedCoursesNewApp = Form.create({name: 'CaseNew'})(CaseNew);
export default WrappedCoursesNewApp;

@ -0,0 +1,26 @@
import React,{ Component } from "react";
import './css/moopCases.css'
import '../courses/css/Courses.css'
class CaseTags extends Component{
constructor(props){
super(props);
}
render(){
let { tags } = this.props;
return(
<React.Fragment>
{
tags && tags.map((item,key)=>{
return(
<span key={key} className={item.name == "获奖案例" ? "edu-filter-btn fl cdefault edu-activity-red ml10" : "edu-filter-btn fl cdefault edu-activity-blue ml10"}>{item.name}</span>
)
})
}
</React.Fragment>
)
}
}
export default CaseTags;

@ -0,0 +1,28 @@
import React,{ Component } from "react";
import './css/moopCases.css'
import '../courses/css/Courses.css'
import { getImageUrl } from 'educoder';
import success from '../../images/moop_cases/success.png'
class Success extends Component{
constructor(props){
super(props);
}
render(){
return(
<div className="educontent edu-back-white mt20 successPage">
<div>
<img src={success} width="100" className="mb30" />
<p className="lineh-30 ed-txt-center font-24 color-grey-3 font-bd">恭喜!</p>
<p className="lineh-30 ed-txt-center font-24 color-grey-3 font-bd mb20">提交成功</p>
<p className="lineh-30 ed-txt-center font-16 color-grey-9 font-bd mb20">平台正在审核您的申请审核结果将以平台消息的形式通知您</p>
<li className="inline">
<a href={`/moop_cases/${this.props.match.params.caseID}`} className="white-btn edu-blueline-btn changebtn mr20 fl">查看已上传文档</a>
<a href="/moop_cases/new" className="white-btn edu-blueback-btn changebtn fl">继续上传</a>
</li>
</div>
</div>
)
}
}
export default Success;

@ -0,0 +1,155 @@
.winput-300-35{
width: 300px;
height: 35px;
padding: 5px;
box-sizing: border-box;
}
.library_nav li {
float: left;
cursor: pointer;
margin-right: 30px;
position: relative;
color: #05101A;
height: 40px;
line-height: 20px;
font-size: 16px;
}
.library_nav li.active a, .library_nav li:hover a{
color: #4cacff!important;
}
.library_list {
margin-bottom: 30px;
}
.library_list_item:hover {
box-shadow: 0px 4px 8px rgba(158,158,158,0.16);
}
.library_list_item {
background: #fff;
padding: 20px 30px;
margin-bottom: 30px;
display: flex;
}
.library_list_item .library_l_name {
max-width: 600px;
float: left;
}
.edu-activity-red {
background-color: #FC2B6A;
color: #fff!important;
cursor: pointer;
border: 1px solid #FC2B6A;
line-height: 17px;
}
.edu-activity-green {
background-color: green;
color: #fff!important;
cursor: pointer;
border: 1px solid green;
line-height: 17px;
}
.edu-activity-blue {
background-color: #4CACFF;
color: #fff!important;
cursor: pointer;
border: 1px solid #4CACFF;
line-height: 17px;
}
.pointsBtn {
width: 70px;
height: 70px;
background-color: #4cacff;
border-radius: 50%;
color: #fff;
text-align: center;
margin: 0 auto;
-webkit-box-sizing: border-box;
box-sizing: border-box;
padding: 2px 0;
cursor: pointer;
line-height: 22px;
padding-top: 12px;
}
.pointedBtn{
background: #BCD1E3;
cursor: default
}
.pointsBtn span{
display: block;
}
.upload_Title {
position: relative;
margin-right: 20px;
float: left;
line-height: 35px;
font-size: 16px;
width: 56px;
color:rgba(0, 0, 0, 0.85);
text-align: center
}
.upload_Title.must:before {
display: inline-block;
margin-right: 4px;
color: #f5222d;
font-size: 14px;
font-family: SimSun, sans-serif;
line-height: 1;
content: '*';
}
.upload_Title:after{
content: ':';
position: relative;
top: -0.5px;
margin: 0 8px 0 2px;
}
.libraries_tab li {
width: 120px;
height: 35px;
line-height: 33px;
border-radius: 18px;
border: 1px solid #4C98FF;
color: #4C98FF;
cursor: pointer;
margin-right: 30px;
float: left;
text-align: center;
}
.libraries_tab li.active {
background: #4C98FF;
color: #fff;
}
.librariesField .ant-upload{
width: 100%;
background: #F2F9FF;
justify-content: center;
align-items: center;
display: -webkit-flex;
text-align: center;
height: 120px!important;
border-radius: 4px;
border: 1px dashed #4cacff!important;
display: block;
cursor: pointer;
}
.uploadImage .ant-upload.ant-upload-select-picture-card{
width:120px;
height: 90px;
}
.uploadImage .ant-upload.ant-upload-select-picture-card > .ant-upload{
padding:0px!important;
}
.successPage {
justify-content: center;
align-items: center;
display: -webkit-flex;
height: 570px;
text-align: center;
margin-bottom: 50px;
}
.changebtn {
width:166px;
font-size: 16px;
height: 45px;
line-height: 45px;
}

@ -0,0 +1,130 @@
import React,{ Component } from "react";
import './css/moopCases.css'
import '../courses/css/Courses.css'
import { SnackbarHOC } from 'educoder';
import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
import { CNotificationHOC } from '../courses/common/CNotificationHOC'
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import Loading from '../../Loading';
import Loadable from 'react-loadable';
import axios from 'axios';
const CaseList = Loadable({
loader: () => import('./CaseList'),
loading:Loading,
})
const CaseDetail = Loadable({
loader: () => import('./CaseDetail'),
loading:Loading,
})
const CaseNew = Loadable({
loader: () => import('./CaseNew'),
loading:Loading,
})
const CaseSuccess = Loadable({
loader: () => import('./Success'),
loading:Loading,
})
class Index extends Component{
constructor(props){
super(props);
this.state={
praise_count:0,
CaseDetail:undefined,
cover:undefined,
creator:undefined,
operation:undefined,
tags:undefined,
attachments:undefined,
user_praised:true,
}
}
// 获取案例详情
getDetail = (caseID) =>{
let url=`/libraries/${caseID}.json`
axios.get(url).then((result)=>{
if(result){
this.setState({
CaseDetail:result.data,
praise_count:result.data.praise_count,
cover:result.data.cover,
creator:result.data.creator,
operation:result.data.operation,
user_praised:result.data.operation.user_praised,
tags:result.data.tags,
attachments:result.data.attachments
})
}
}).catch((error)=>{
console.log(error);
})
}
// 点赞
praisePoint=(caseID)=>{
let { praise_count }=this.state;
let url =`/praise_tread/like.json`;
axios.post(url,{
object_id:caseID,
object_type:"library"
}).then((result)=>{
if(result){
this.setState({
praise_count: parseInt(praise_count)+1,
user_praised:true
})
}
}).catch((error)=>{
console.log(error);
})
}
render(){
return(
<div className="newMain">
<Switch {...this.props}>
<Route exact path="/moop_cases"
render={
(props) => (<CaseList {...this.props} {...props} {...this.state} />)
}
></Route>
<Route exact path="/moop_cases/new"
render={
(props) => (<CaseNew {...this.props} {...props} {...this.state} />)
}
></Route>
<Route exact path="/moop_cases/:caseID"
render={
(props) => (<CaseDetail {...this.props} {...props} {...this.state} getDetail={this.getDetail} praisePoint ={this.praisePoint}/>)
}
></Route>
<Route exact path="/moop_cases/:caseID/edit"
render={
(props) => (<CaseNew {...this.props} {...props} {...this.state} getDetail={this.getDetail} />)
}
></Route>
<Route exact path="/moop_cases/:caseID/publish_success"
render={
(props) => (<CaseSuccess {...this.props} {...props} {...this.state} getDetail={this.getDetail} />)
}
></Route>
</Switch>
</div>
)
}
}
export default CNotificationHOC() ( SnackbarHOC() ( TPMIndexHOC(Index) ));

@ -195,7 +195,7 @@ class DetailTop extends Component{
detailInfoList &&
<ul className="fl color-grey-eb pathInfo">
{ detailInfoList.stages_count!=0 ? <li><span>章节</span><span className="ml5">{detailInfoList.stages_count}</span></li> : ""}
{ detailInfoList.stage_shixuns_count!=0 ? <li><span>实训</span><span className="ml5">{ detailInfoList.stage_shixuns_count}</span></li> : ""}
{ detailInfoList.shixuns_count!=0 ? <li><span>实训</span><span className="ml5">{ detailInfoList.shixuns_count}</span></li> : ""}
{ detailInfoList.challenge_choose_count!=0 ? <li><span>选择题任务</span><span>{detailInfoList.challenge_choose_count}</span></li> : ""}
{ detailInfoList.challenges_count!=0 ? <li><span>实践任务</span><span>{detailInfoList.challenges_count}</span></li> : ""}
{ detailInfoList.subject_score!=0 ? <li><span>经验值</span><span>{detailInfoList.subject_score}</span></li> : ""}

@ -409,7 +409,7 @@ class PackageIndexNEITaskDetails extends Component {
return(
<div className="ysldivhomediv1s homehove" key={key}>
{item.status==="bidding_won"?<img src={gouxuan} className="yslgouxuanimg"/>:""}
<a href={`/users/${item.login}`}><img className="div1imgs" src={getImageUrl("images/"+item.image_url)}/></a>
<a className={item.status==="bidding_won"?"":"mt20"} href={`/users/${item.login}`}><img className="div1imgs" src={getImageUrl("images/"+item.image_url)}/></a>
<div className="textall mt10" title={item.name}> <p className="ptext">{item.name}</p></div>
{this.props.current_user&&this.props.current_user.login!=item.login?<a className="ContacttheTAs fl none" target="_blank" href={`/messages/${this.props.current_user&&this.props.current_user.login}/message_detail?target_ids=${item.id}`}>
<img alt="头像" className="mr5" src={require('./newstwo.png')}/>联系TA

@ -1313,7 +1313,7 @@ export default class TPMsettings extends Component {
</div>
<div className="task-popup-submit clearfix">
<a onClick={this.hideoperateshixuns} className="task-btn fl">取消</a>
{delType===2?<a className="task-btn task-btn-orange fr" onClick={this.shixunsdel}>确定</a>:<a className="task-btn task-btn-orange fr" onClick={this.shixunsclose}></a>}
{delType===1?<a className="task-btn task-btn-orange fr" onClick={this.shixunsdel}>确定</a>:<a className="task-btn task-btn-orange fr" onClick={this.shixunsclose}></a>}
</div>
</Modal>
@ -1391,13 +1391,13 @@ export default class TPMsettings extends Component {
})
}
</Select>
<p
className="edu-txt-left font-12"
style={{display:operateauthority?"block":'none'}}
>
列表中没有
<a className="color-blue" onClick={this.post_apply}>申请新建</a>
</p>
{/*<p*/}
{/*className="edu-txt-left font-12"*/}
{/*style={{display:operateauthority?"block":'none'}}*/}
{/*>*/}
{/*列表中没有?*/}
{/*<a className="color-blue" onClick={this.post_apply}>申请新建</a>*/}
{/*</p>*/}
<Modal
keyboard={false}

@ -323,7 +323,7 @@ class Challenges extends Component {
</Tooltip>
</p>
<div className="justify break_full_word new_li markdown-body"
<div className="justify break_full_word new_li "
id="challenge_editorMd_description">
<p id="ReactMarkdown" style={{overflow:'hidden'}}>
{ChallengesDataList === undefined ? "" :ChallengesDataList&&ChallengesDataList.description===null?"":

@ -126,7 +126,7 @@ class AccountBasic extends Component {
// 输入昵称时change剩余的字数
changeNickName=(e)=>{
let num= 10 - parseInt(e.target.value.length);
let num= 20 - parseInt(e.target.value.length);
this.setState({
nameLength:num < 0 ? 0 : num
})
@ -393,6 +393,13 @@ class AccountBasic extends Component {
this.props.showNotification("请先选择正确的单位或者学校!");
}
}
checkNameLength = (rule, value, callback) => {
if (value && value.length <= 10) {
callback();
return;
}
callback('请输入您的姓名最大限制10个字符');
}
render() {
let{
@ -491,8 +498,8 @@ class AccountBasic extends Component {
message: '请输入您的昵称',
}],
})(
<Input placeholder="请输入您的昵称,10个字以内" onInput={this.changeNickName} maxLength="10" suffix ={
<span className="color-grey-6 font-13">{String(nameLength)}/10</span>
<Input placeholder="请输入您的昵称,最大限制20个字符" onInput={this.changeNickName} maxLength="20" suffix ={
<span className="color-grey-6 font-13">{String(nameLength)}/20</span>
}></Input>
)}
</Form.Item>
@ -505,10 +512,11 @@ class AccountBasic extends Component {
rules: [{
// initialValue: this.state.cityDefaultValue,
required: true,
message: '请输入您的姓名',
message: '请输入您的姓名最大限制10个字符',
validator: this.checkNameLength
}],
})(
<Input placeholder="请输入您的姓名" disabled={!showRealName && this.state.forDisable == true } suffix={
<Input placeholder="请输入您的姓名最大限制10个字符" disabled={!showRealName && this.state.forDisable == true } suffix={
<i className={showRealName?"iconfont icon-xianshi font-18 color-blue":"iconfont icon-yincang font-18 color-blue"}
onClick={()=>this.showOrHide(showRealName)}></i>
}></Input>

@ -13,7 +13,7 @@ class AccountNav extends Component {
this.props.history.push(`/account/secure`)
}
render() {
let { basicInfo } = this.props
let { basicInfo, current_user } = this.props
// console.log(this.props);
const path = window.location.pathname
const isBasic = path.indexOf('profile') != -1 || path == "/account"
@ -78,7 +78,7 @@ class AccountNav extends Component {
<React.Fragment>
<div className="accountInfo">
<AccountImg src={basicInfo.avatar_url} {...this.props}></AccountImg>
<span className="name">{basicInfo.name}</span>
<span className="name">{basicInfo.name || (current_user ? current_user.login : '')}</span>
<span className="role">{basicInfo.technical_title}</span>
</div>

@ -164,11 +164,14 @@ class InfosPath extends Component{
<a href="javascript:void(0)" className="square-img"><img alt="Subject12" src={getImageUrl(`${item.image_url}`)}/></a>
<div className="square-main">
<p className="task-hide">
<a href="javascript:void(0)" className="justify color-grey-name">{item.name}</a>
<a href="javascript:void(0)" className="justify color-grey-name "
>{item.name}</a>
</p>
<div className="mt10">
<p className="color-grey-6 clearfix">
<a href="javascript:void(0)" className="fl color-grey-9">{item.owner_name}</a>
<a href="javascript:void(0)" className="fl color-grey-9 overflowHidden1"
title={item.owner_name.length > 12 ? item.owner_name : ''} style={{ maxWidth: '198px'}}
>{item.owner_name}</a>
<span className="fr squareIconSpan">
<Tooltip placement='bottom' title="访问量">
<i className="iconfont icon-liulanyan fl mr5"></i>

@ -286,10 +286,10 @@ class SearchPage extends Component{
</span>
</span>}
{!!item.stage_shixuns_count && <span className="ml20 ziticor">
{!!item.shixuns_count && <span className="ml20 ziticor">
{/* <i className="iconfont icon-shixunguanqia ziticor"></i> */}
<span>实训数:
<span className="ml10 ziticor">{item.stage_shixuns_count}</span>
<span className="ml10 ziticor">{item.shixuns_count}</span>
</span>
</span>}
{type == 'subject' && !!item.visits_count && <span className="ml20 ziticor">

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe CommitExercsieNotifyJobJob, type: :job do
pending "add some examples to (or delete) #{__FILE__}"
end

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