Merge branch 'dev_aliyun' into develop

dev_oauth
cxt 6 years ago
commit fd0f663427

@ -26,6 +26,12 @@ class Users::BaseController < ApplicationController
render_forbidden
end
def require_teacher!
return if current_user.admin_or_business? || observed_user.is_teacher?
render_forbidden
end
def require_auth_teacher!
return if current_user.admin_or_business? || observed_user.certification_teacher?

@ -1,5 +1,6 @@
class Users::VideosController < Users::BaseController
before_action :private_user_resources!, :check_account
before_action :require_teacher!
before_action :require_auth_teacher!, except: [:index, :review]
helper_method :current_video

@ -13,8 +13,8 @@ module AliyunVod::Service::Base
if response.status != 200
message =
case result['Code']
when 'InvalidFileName.Extension' then '不支持的文件格式'
when 'IllegalCharacters' then '文件名称包含非法字符'
when 'InvalidFileName.Extension' then '不支持的视频格式'
when 'IllegalCharacters' then '视频名称包含非法字符'
else raise AliyunVod::Error, result['Message']
end
raise AliyunVod::Error, message if message.present?

@ -21,7 +21,14 @@ class Videos::CreateAuthService < ApplicationService
private
def title
@_title ||= params.delete(:title).to_s.strip
@_title ||= begin
str = params.delete(:title).to_s.strip
if str.length > 30
"#{str[0, 15]}...#{str.reverse[0,10].reverse}"
else
str
end
end
end
def filename

@ -13,6 +13,7 @@ class Videos::DispatchCallbackService < ApplicationService
case params['EventType']
when 'FileUploadComplete' then # 视频上传完成
video.file_url = params['FileUrl']
video.filesize = params['Size']
video.upload_success
video.save!
when 'SnapshotComplete' then # 封面截图完成

@ -549,6 +549,7 @@ a:hover.task-btn-orange{background: #459BE6;}
a.user_bluebg_btn{background-color:#4CACFF;color: #fff;}
a.user_orangebg_btn{background-color:#FF6800;color: #fff;}
a.user_greybg_btn{background-color:#747A7F;color: #fff;}
/*.user_white_btn{border: 1px solid #ffffff;color: #ffffff!important;}*/
.pointer{cursor: pointer}

@ -0,0 +1,5 @@
class AddFilesizeToVideos < ActiveRecord::Migration[5.2]
def change
add_column :videos, :filesize, :integer, default: 0
end
end

@ -61,13 +61,13 @@
"rc-select": "^8.0.12",
"rc-tree": "^1.7.11",
"rc-upload": "^2.5.1",
"react": "^16.8.0",
"react": "^16.9.0",
"react-beautiful-dnd": "^10.0.4",
"react-codemirror": "^1.0.0",
"react-codemirror2": "^6.0.0",
"react-content-loader": "^3.1.1",
"react-dev-utils": "^5.0.0",
"react-dom": "^16.8.0",
"react-dom": "^16.9.0",
"react-hot-loader": "^4.0.0",
"react-infinite-scroller": "^1.2.4",
"react-loadable": "^5.3.1",

@ -112,8 +112,25 @@ em.vertical-line{display: inline-block;width: 2px;background: #999;height: 10px}
.smallSquare:nth-child(3n+0){margin-right: 0px;}
.partimg{height: 180px;width: 100%;border-radius: 6px 6px 0px 0px;}
/*块状列表上面的绿色标签*/
.tag-green{position: absolute;left: 0px;top:20px;}
.tag-green .tag-name{display: block;width: auto;background-image: url("/images/educoder/tag1.png");background-size: 100% 100%;padding: 0px 8px;color: #fff;float: left;}
.tag-green{
position: absolute;
left: 15px;
bottom: 95px;}
.tag-green .tag-name{display: block;width: auto;
/*background-image: url("/images/educoder/tag1.png");*/
background: #000000;
border: 1px solid #fff;
border-radius: 3px;
font-size: 14px;
background-size: 100% 100%;padding: 0px 8px;color: #fff;float: left;}
.tag-orange{position: absolute;right: 0px;top:20px;}
.tag-orange .tag-name{display: block;width: auto;background-color:#FF6800;
background-size: 100% 100%;padding: 0px 8px;color: #fff;float: left;
height: 28px;
line-height: 28px;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
/*发送至弹框里的下拉框*/
.downSelectOption{position: relative;height: 35px;}
.downSelectOption .showOption{background-color: #F4F4F4;border: 1px solid #EAEAEA;width: 100%;padding: 5px 40px 5px 5px;outline: none;height: 100%;box-sizing: border-box;}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -64,3 +64,13 @@ html, body {
/* 某些情况下被cm盖住了 */
z-index: 99;
}
/* antd扩展 */
.formItemInline.ant-form-item {
display: flex;
}
.formItemInline .ant-form-item-control-wrapper {
flex: 1;
}

@ -217,6 +217,10 @@ const UsersInfo = Loadable({
loader: () => import('./modules/user/usersInfo/Infos'),
loading: Loading,
})
const InfosIndex = Loadable({
loader: () => import('./modules/user/usersInfo/InfosIndex'),
loading: Loading,
})
// 教学案例
const MoopCases = Loadable({
@ -327,7 +331,10 @@ class App extends Component {
/>
<Route path="/users/:username"
render={
(props) => (<UsersInfo {...this.props} {...props} {...this.state} />)
(props) => {
return (<InfosIndex {...this.props} {...props} {...this.state} />)
}
}></Route>
<Route

@ -69,7 +69,7 @@ export function initAxiosInterceptors(props) {
// proxy = 'http://localhost:3000'
// }
// ---------------------------------------------
if (config.url.indexOf(proxy) != -1) {
if (config.url.indexOf(proxy) != -1 || config.url.indexOf(':') != -1) {
return config
}
requestProxy(config)

@ -0,0 +1,33 @@
import React,{ Component } from "react";
class AttachmentsList extends Component{
constructor(props){
super(props);
}
render(){
let { attachments } = this.props;
return(
<React.Fragment>
{
attachments.map((item,key)=>{
return(
<p key={key}>
<a className="color-grey">
<i className="font-14 color-green iconfont icon-fujian mr8"></i>
</a>
{
item.is_pdf && item.is_pdf == true ?
<a href={item.url} className="mr12" length="58" target="_blank">{item.title}</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>
)
})
}
</React.Fragment>
)
}
}
export default AttachmentsList;

@ -0,0 +1,237 @@
import React, { useState, useEffect, memo } from 'react';
import { getUrl2, isDev } from 'educoder'
import axios from 'axios'
const $ = window.$
let _url_origin = getUrl2()
let _path = isDev() ? 'public' : 'build'
let uploader
let _testHost = '' ; '192.168.2.63:3001/api'
const login = 'innov'
function createUploader () {
uploader = new window.AliyunUpload.Vod({
timeout: $('#timeout').val() || 60000,
partSize: $('#partSize').val() || 1048576,
parallel: $('#parallel').val() || 5,
retryCount: $('#retryCount').val() || 3,
retryDuration: $('#retryDuration').val() || 2,
region: $('#region').val() || 'ap-southeast-1',
userId: $('#userId').val() || 1829848226361863, //, // 1303984639806000,
// 添加文件成功
addFileSuccess: function (uploadInfo) {
console.log('addFileSuccess')
$('#authUpload').attr('disabled', false)
$('#resumeUpload').attr('disabled', false)
$('#status').text('添加文件成功, 等待上传...')
console.log("addFileSuccess: " + uploadInfo.file.name)
$('#pauseUpload').attr('disabled', false)
uploader.startUpload()
},
// 开始上传
onUploadstarted: function (uploadInfo) {
// 如果是 UploadAuth 上传方式, 需要调用 uploader.setUploadAuthAndAddress 方法
// 如果是 UploadAuth 上传方式, 需要根据 uploadInfo.videoId是否有值调用点播的不同接口获取uploadauth和uploadAddress
// 如果 uploadInfo.videoId 有值,调用刷新视频上传凭证接口,否则调用创建视频上传凭证接口
// 注意: 这里是测试 demo 所以直接调用了获取 UploadAuth 的测试接口, 用户在使用时需要判断 uploadInfo.videoId 存在与否从而调用 openApi
// 如果 uploadInfo.videoId 存在, 调用 刷新视频上传凭证接口(https://help.aliyun.com/document_detail/55408.html)
// 如果 uploadInfo.videoId 不存在,调用 获取视频上传地址和凭证接口(https://help.aliyun.com/document_detail/55407.html)
if (!uploadInfo.videoId) {
// var createUrl = 'https://demo-vod.cn-shanghai.aliyuncs.com/voddemo/CreateUploadVideo?Title=testvod1&FileName=aa.mp4&BusinessType=vodai&TerminalType=pc&DeviceModel=iPhone9,2&UUID=59ECA-4193-4695-94DD-7E1247288&AppVersion=1.0.0&VideoId=5bfcc7864fc14b96972842172207c9e6'
// $.get(createUrl, function (data) {
// var uploadAuth = data.UploadAuth
// var uploadAddress = data.UploadAddress
// var videoId = data.VideoId
// uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress,videoId)
// }, 'json')
var createUrl = `${_testHost}/users/${login}/video_auths.json?debug=true`
axios.post(createUrl, {
title: 'testvod1',
file_name: 'aa.mp4'
}).then((response) => {
// if (response.data.status == )
const data = response.data.data
var uploadAuth = data.UploadAuth
var uploadAddress = data.UploadAddress
var videoId = data.VideoId
uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress, videoId)
}).catch((error) => {
console.log(error)
})
$('#status').text('文件开始上传...')
console.log("onUploadStarted:" + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object)
} else {
// 如果videoId有值根据videoId刷新上传凭证
// https://help.aliyun.com/document_detail/55408.html?spm=a2c4g.11186623.6.630.BoYYcY
// var refreshUrl = 'https://demo-vod.cn-shanghai.aliyuncs.com/voddemo/RefreshUploadVideo?BusinessType=vodai&TerminalType=pc&DeviceModel=iPhone9,2&UUID=59ECA-4193-4695-94DD-7E1247288&AppVersion=1.0.0&Title=haha1&FileName=xxx.mp4&VideoId=' + uploadInfo.videoId
// $.get(refreshUrl, function (data) {
// var uploadAuth = data.UploadAuth
// var uploadAddress = data.UploadAddress
// var videoId = data.VideoId
// uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress,videoId)
// }, 'json')
var refreshUrl = `${_testHost}/users/${login}/video_auths.json?debug=true`
axios.put(refreshUrl, {
video_id: uploadInfo.videoId,
}).then((response) => {
const data = response.data.data
var uploadAuth = data.UploadAuth
var uploadAddress = data.UploadAddress
var videoId = data.VideoId
uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress, videoId)
}).catch((error) => {
console.log(error)
})
}
},
// 文件上传成功
onUploadSucceed: function (uploadInfo) {
console.log("onUploadSucceed: " + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object)
$('#status').text('文件上传成功!')
},
// 文件上传失败
onUploadFailed: function (uploadInfo, code, message) {
console.log("onUploadFailed: file:" + uploadInfo.file.name + ",code:" + code + ", message:" + message)
$('#status').text('文件上传失败!')
},
// 取消文件上传
onUploadCanceled: function (uploadInfo, code, message) {
console.log("Canceled file: " + uploadInfo.file.name + ", code: " + code + ", message:" + message)
$('#status').text('文件上传已暂停!')
},
// 文件上传进度,单位:字节, 可以在这个函数中拿到上传进度并显示在页面上
onUploadProgress: function (uploadInfo, totalSize, progress) {
console.log("onUploadProgress:file:" + uploadInfo.file.name + ", fileSize:" + totalSize + ", percent:" + Math.ceil(progress * 100) + "%")
var progressPercent = Math.ceil(progress * 100)
$('#auth-progress').text(progressPercent)
$('#status').text('文件上传中...')
},
// 上传凭证超时
onUploadTokenExpired: function (uploadInfo) {
// 上传大文件超时, 如果是上传方式一即根据 UploadAuth 上传时
// 需要根据 uploadInfo.videoId 调用刷新视频上传凭证接口(https://help.aliyun.com/document_detail/55408.html)重新获取 UploadAuth
// 然后调用 resumeUploadWithAuth 方法, 这里是测试接口, 所以我直接获取了 UploadAuth
$('#status').text('文件上传超时!')
// let refreshUrl = 'https://demo-vod.cn-shanghai.aliyuncs.com/voddemo/RefreshUploadVideo?BusinessType=vodai&TerminalType=pc&DeviceModel=iPhone9,2&UUID=59ECA-4193-4695-94DD-7E1247288&AppVersion=1.0.0&Title=haha1&FileName=xxx.mp4&VideoId=' + uploadInfo.videoId
// $.get(refreshUrl, function (data) {
// var uploadAuth = data.UploadAuth
// uploader.resumeUploadWithAuth(uploadAuth)
// console.log('upload expired and resume upload with uploadauth ' + uploadAuth)
// }, 'json')
var refreshUrl = `${_testHost}/users/${login}/video_auths.json?debug=true`
axios.put(refreshUrl, {
video_id: uploadInfo.videoId,
}).then((response) => {
const data = response.data.data
var uploadAuth = data.UploadAuth
uploader.resumeUploadWithAuth(uploadAuth)
}).catch((error) => {
console.log(error)
})
},
// 全部文件上传结束
onUploadEnd: function (uploadInfo) {
$('#status').text('文件上传完毕!')
console.log("onUploadEnd: uploaded all the files")
}
})
return uploader
}
function AliyunUploader(props) {
useEffect(() => {
if (window.AliyunUpload && window.AliyunUpload.Vod) {
} else {
$.getScript(
`${_url_origin}/react/${_path}/js/aliyun-upload/lib/es6-promise.min.js`,
(data, textStatus, jqxhr) => {
$.getScript(
`${_url_origin}/react/${_path}/js/aliyun-upload/lib/aliyun-oss-sdk-5.3.1.min.js`,
(data, textStatus, jqxhr) => {
$.getScript(
`${_url_origin}/react/${_path}/js/aliyun-upload/aliyun-upload-sdk-1.5.0.min.js`,
(data, textStatus, jqxhr) => {
});
});
});
}
$('#fileUpload').on('change', function (e) {
var file = e.target.files[0]
if (!file) {
alert("请先选择需要上传的文件!")
return
}
var Title = file.name
var userData = '{"Vod":{}}'
if (uploader) {
uploader.stopUpload()
$('#auth-progress').text('0')
$('#status').text("")
}
if (!uploader) {
uploader = createUploader()
}
// 首先调用 uploader.addFile(event.target.files[i], null, null, null, userData)
console.log(uploader)
let result = uploader.addFile(file, null, null, null, userData)
$('#authUpload').attr('disabled', false)
// $('#pauseUpload').attr('disabled', true)
// $('#resumeUpload').attr('disabled', true)
})
return () => {
$('#fileUpload').off('change')
}
}, [])
let { source, id, className, type } = props;
function onStop() {
$('#resumeUpload').attr('disabled', false)
// $('#pauseUpload').attr('disabled', true)
uploader.stopUpload()
}
function onResume() {
// $('#resumeUpload').attr('disabled', true)
// $('#pauseUpload').attr('disabled', false)
uploader.startUpload()
}
return(
<React.Fragment>
<div>
<input type="file" id="fileUpload"></input>
<label class="status">上传状态: <span id="status"></span></label>
</div>
<div class="upload-type">
上传方式一, 使用 UploadAuth 上传:
{/* <button id="authUpload" disabled="true"></button>
<button id="pauseUpload" disabled="true" onClick={onStop}>暂停</button>
<button id="resumeUpload" disabled="true" onClick={onResume}>恢复上传</button> */}
<button id="authUpload" >开始上传</button>
<button id="pauseUpload" onClick={onStop}>暂停</button>
<button id="resumeUpload" onClick={onResume}>恢复上传</button>
<span class="progress">上传进度: <i id="auth-progress">0</i> %</span>
<span></span>
</div>
</React.Fragment>
)
}
export default AliyunUploader

@ -0,0 +1,188 @@
import React, { useState, useEffect, memo } from 'react';
import { getUrl2, isDev } from 'educoder'
const $ = window.$
let _url_origin = getUrl2()
let _path = _isDev ? 'public' : 'build'
let uploader
function createUploader () {
uploader = new AliyunUpload.Vod({
timeout: $('#timeout').val() || 60000,
partSize: $('#partSize').val() || 1048576,
parallel: $('#parallel').val() || 5,
retryCount: $('#retryCount').val() || 3,
retryDuration: $('#retryDuration').val() || 2,
region: $('#region').val() || 'ap-southeast-1',
userId: $('#userId').val() || 1202060945918292, // 1303984639806000,
// 添加文件成功
addFileSuccess: function (uploadInfo) {
console.log('addFileSuccess')
$('#authUpload').attr('disabled', false)
$('#resumeUpload').attr('disabled', false)
$('#status').text('添加文件成功, 等待上传...')
console.log("addFileSuccess: " + uploadInfo.file.name)
$('#pauseUpload').attr('disabled', false)
uploader.startUpload()
},
// 开始上传
onUploadstarted: function (uploadInfo) {
// 如果是 UploadAuth 上传方式, 需要调用 uploader.setUploadAuthAndAddress 方法
// 如果是 UploadAuth 上传方式, 需要根据 uploadInfo.videoId是否有值调用点播的不同接口获取uploadauth和uploadAddress
// 如果 uploadInfo.videoId 有值,调用刷新视频上传凭证接口,否则调用创建视频上传凭证接口
// 注意: 这里是测试 demo 所以直接调用了获取 UploadAuth 的测试接口, 用户在使用时需要判断 uploadInfo.videoId 存在与否从而调用 openApi
// 如果 uploadInfo.videoId 存在, 调用 刷新视频上传凭证接口(https://help.aliyun.com/document_detail/55408.html)
// 如果 uploadInfo.videoId 不存在,调用 获取视频上传地址和凭证接口(https://help.aliyun.com/document_detail/55407.html)
if (!uploadInfo.videoId) {
var createUrl = 'https://demo-vod.cn-shanghai.aliyuncs.com/voddemo/CreateUploadVideo?Title=testvod1&FileName=aa.mp4&BusinessType=vodai&TerminalType=pc&DeviceModel=iPhone9,2&UUID=59ECA-4193-4695-94DD-7E1247288&AppVersion=1.0.0&VideoId=5bfcc7864fc14b96972842172207c9e6'
$.get(createUrl, function (data) {
var uploadAuth = data.UploadAuth
var uploadAddress = data.UploadAddress
var videoId = data.VideoId
uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress,videoId)
}, 'json')
$('#status').text('文件开始上传...')
console.log("onUploadStarted:" + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object)
} else {
// 如果videoId有值根据videoId刷新上传凭证
// https://help.aliyun.com/document_detail/55408.html?spm=a2c4g.11186623.6.630.BoYYcY
var refreshUrl = 'https://demo-vod.cn-shanghai.aliyuncs.com/voddemo/RefreshUploadVideo?BusinessType=vodai&TerminalType=pc&DeviceModel=iPhone9,2&UUID=59ECA-4193-4695-94DD-7E1247288&AppVersion=1.0.0&Title=haha1&FileName=xxx.mp4&VideoId=' + uploadInfo.videoId
$.get(refreshUrl, function (data) {
var uploadAuth = data.UploadAuth
var uploadAddress = data.UploadAddress
var videoId = data.VideoId
uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress,videoId)
}, 'json')
}
},
// 文件上传成功
onUploadSucceed: function (uploadInfo) {
console.log("onUploadSucceed: " + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object)
$('#status').text('文件上传成功!')
},
// 文件上传失败
onUploadFailed: function (uploadInfo, code, message) {
console.log("onUploadFailed: file:" + uploadInfo.file.name + ",code:" + code + ", message:" + message)
$('#status').text('文件上传失败!')
},
// 取消文件上传
onUploadCanceled: function (uploadInfo, code, message) {
console.log("Canceled file: " + uploadInfo.file.name + ", code: " + code + ", message:" + message)
$('#status').text('文件上传已暂停!')
},
// 文件上传进度,单位:字节, 可以在这个函数中拿到上传进度并显示在页面上
onUploadProgress: function (uploadInfo, totalSize, progress) {
console.log("onUploadProgress:file:" + uploadInfo.file.name + ", fileSize:" + totalSize + ", percent:" + Math.ceil(progress * 100) + "%")
var progressPercent = Math.ceil(progress * 100)
$('#auth-progress').text(progressPercent)
$('#status').text('文件上传中...')
},
// 上传凭证超时
onUploadTokenExpired: function (uploadInfo) {
// 上传大文件超时, 如果是上传方式一即根据 UploadAuth 上传时
// 需要根据 uploadInfo.videoId 调用刷新视频上传凭证接口(https://help.aliyun.com/document_detail/55408.html)重新获取 UploadAuth
// 然后调用 resumeUploadWithAuth 方法, 这里是测试接口, 所以我直接获取了 UploadAuth
$('#status').text('文件上传超时!')
let refreshUrl = 'https://demo-vod.cn-shanghai.aliyuncs.com/voddemo/RefreshUploadVideo?BusinessType=vodai&TerminalType=pc&DeviceModel=iPhone9,2&UUID=59ECA-4193-4695-94DD-7E1247288&AppVersion=1.0.0&Title=haha1&FileName=xxx.mp4&VideoId=' + uploadInfo.videoId
$.get(refreshUrl, function (data) {
var uploadAuth = data.UploadAuth
uploader.resumeUploadWithAuth(uploadAuth)
console.log('upload expired and resume upload with uploadauth ' + uploadAuth)
}, 'json')
},
// 全部文件上传结束
onUploadEnd: function (uploadInfo) {
$('#status').text('文件上传完毕!')
console.log("onUploadEnd: uploaded all the files")
}
})
return uploader
}
function AliyunUploaderDemo(props) {
useEffect(() => {
if (window.AliyunUpload && window.AliyunUpload.Vod) {
} else {
$.getScript(
`${_url_origin}/react/${_path}/js/aliyun-upload/lib/es6-promise.min.js`,
(data, textStatus, jqxhr) => {
$.getScript(
`${_url_origin}/react/${_path}/js/aliyun-upload/lib/aliyun-oss-sdk-5.3.1.min.js`,
(data, textStatus, jqxhr) => {
$.getScript(
`${_url_origin}/react/${_path}/js/aliyun-upload/aliyun-upload-sdk-1.5.0.min.js`,
(data, textStatus, jqxhr) => {
});
});
});
}
$('#fileUpload').on('change', function (e) {
var file = e.target.files[0]
if (!file) {
alert("请先选择需要上传的文件!")
return
}
var Title = file.name
var userData = '{"Vod":{}}'
if (uploader) {
uploader.stopUpload()
$('#auth-progress').text('0')
$('#status').text("")
}
if (!uploader) {
uploader = createUploader()
}
// 首先调用 uploader.addFile(event.target.files[i], null, null, null, userData)
console.log(uploader)
uploader.addFile(file, null, null, null, userData)
$('#authUpload').attr('disabled', false)
// $('#pauseUpload').attr('disabled', true)
// $('#resumeUpload').attr('disabled', true)
})
// return () => {
// }
}, [])
let { source, id, className, type } = props;
function onStop() {
$('#resumeUpload').attr('disabled', false)
// $('#pauseUpload').attr('disabled', true)
uploader.stopUpload()
}
function onResume() {
// $('#resumeUpload').attr('disabled', true)
// $('#pauseUpload').attr('disabled', false)
uploader.startUpload()
}
return(
<React.Fragment>
<div>
<input type="file" id="fileUpload"></input>
<label class="status">上传状态: <span id="status"></span></label>
</div>
<div class="upload-type">
上传方式一, 使用 UploadAuth 上传:
{/* <button id="authUpload" disabled="true"></button>
<button id="pauseUpload" disabled="true" onClick={onStop}>暂停</button>
<button id="resumeUpload" disabled="true" onClick={onResume}>恢复上传</button> */}
<button id="authUpload" >开始上传</button>
<button id="pauseUpload" onClick={onStop}>暂停</button>
<button id="resumeUpload" onClick={onResume}>恢复上传</button>
<span class="progress">上传进度: <i id="auth-progress">0</i> %</span>
<span></span>
</div>
</React.Fragment>
)
}
export default AliyunUploaderDemo

@ -4,7 +4,10 @@ export const themes = {
light: {
foreground: '#000000',
background: '#eeeeee',
foreground_select: '#4CACFF'
foreground_select: '#4CACFF',
foreground_orange1: '#FF6800',
foreground_tip: '#333',
},
dark: {
foreground: '#ffffff',

@ -17,10 +17,10 @@ class ActionBtn extends Component {
to==undefined ?
<a href="javascript:void(0)" onClick={this.props.onClick}
{...others}
className={"Actionbtn "+`${map[style]} ${this.props.className}`}
className={"Actionbtn "+`${map[style || 'blue']} ${this.props.className}`}
>{children}</a>
:
<Link to={to} className={"btn "+`${map[this.props.style]} ${this.props.className}`}>{this.props.children}</Link>
<Link to={to} className={"btn "+`${map[this.props.style]} ${this.props.className}`} {...others}>{this.props.children}</Link>
}
</React.Fragment>
)

@ -30,6 +30,9 @@ export { trace_collapse, trace, debug, info, warn, error, trace_c, debug_c, info
export { EDU_ADMIN, EDU_BUSINESS, EDU_SHIXUN_MANAGER, EDU_SHIXUN_MEMBER, EDU_CERTIFICATION_TEACHER
, EDU_GAME_MANAGER, EDU_TEACHER, EDU_NORMAL} from './Const'
export { default as AttachmentList } from './components/attachment/AttachmentList'
export { themes, ThemeContext } from './context/ThemeContext'
export { ModalHOC } from './components/ModalHOC'
@ -56,12 +59,14 @@ export { default as MarkdownToHtml } from './components/markdown/MarkdownToHtml'
export { default as DMDEditor } from './components/markdown/DMDEditor'
export { default as Clappr } from './components/media/Clappr'
export { default as AliyunUploader } from './components/media/AliyunUploader'
export { default as ImageLayerHook } from './hooks/ImageLayerHook'
// 外部
export { default as CBreadcrumb } from '../modules/courses/common/CBreadcrumb'
export { CNotificationHOC as CNotificationHOC } from '../modules/courses/common/CNotificationHOC'
export { default as ModalWrapper } from '../modules/courses/common/ModalWrapper'
export { default as NoneData } from '../modules/courses/coursesPublic/NoneData'

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

@ -72,7 +72,11 @@ const WrappedCoursesNewApp= Loadable({
loader: () => import('./new/CoursesNew'),
loading: Loading,
})
//新建金品课堂
const WrappedCoursesNewAppGoldclass= Loadable({
loader: () => import('./new/Goldsubject'),
loading: Loading,
})
const ShixunHomeworkPage =Loadable({
loader: () => import('./shixunHomework/ShixunHomeworkPage'),
loading: Loading,
@ -821,6 +825,20 @@ class CoursesIndex extends Component{
(props) => (<WrappedCoursesNewApp {...this.props} {...props} {...this.state} />)
}
></Route>
{/*新建金品课堂*/}
{/*coursesId 课堂id */}
{/*id 是否是私有或者公有*/}
<Route path="/courses/:subjectid/newgold/:id"
render={
(props) => (<WrappedCoursesNewAppGoldclass {...this.props} {...props} {...this.state} />)
}
></Route>
{/*修改金品课堂*/}
<Route path="/courses/:coursesId/newgolds/settings/:id"
render={
(props) => (<WrappedCoursesNewAppGoldclass {...this.props} {...props} {...this.state} />)
}
></Route>
{/*修改课堂*/}
<Route path="/courses/:coursesId/settings"
render={
@ -828,7 +846,6 @@ class CoursesIndex extends Component{
}
></Route>
{/* 实训作业子页面*/}
<Route path="/courses/:coursesId/shixun_homework/:category_id"
render={

@ -32,7 +32,15 @@ const TeacherList= Loadable({
const StudentsList= Loadable({
loader: () => import('./members/studentsList'),
loading: Loading,
})
});
const Eduinforms= Loadable({
loader: () => import('./gradinforms/Eduinforms.js'),
loading: Loading,
});
const Elearning= Loadable({
loader: () => import('./elearning/Elearning.js'),
loading: Loading,
});
//
const Exercise= Loadable({
loader: () => import('./exercise/Exercise'),
@ -239,6 +247,21 @@ class ListPageIndex extends Component{
}
></Route>
{/*公告栏列表*/}
<Route path="/courses/:coursesId/informs"
render={
(props) => (<Eduinforms {...this.props} {...props} {...this.state} />)
}
></Route>
{/*在线学习*/}
<Route
path="/courses/:coursesId/online_learning"
render={
(props) => (<Elearning {...this.props} {...props} {...this.state} />)
}
>
</Route>
{/* 实训作业页面*/}
<Route path="/courses/:coursesId/shixun_homeworks/:main_id"
render={

@ -10,7 +10,7 @@ class CBreadcrumb extends Component{
render(){
let { items, className, separator }=this.props;
return(
<p className={`clearfix mb10 ${className}`}>
<p className={`clearfix mb10 ${className} cBreadcrumb`}>
{ items && items.map( (item, index) => {
if (!item.name) {
return ''

@ -16,6 +16,7 @@ class ModalWrapper extends Component{
}
onCancel = () => {
this.setVisible(false)
this.props.onCancel && this.props.onCancel()
}
onOk = () => {
this.props.onOk && this.props.onOk()

@ -11,7 +11,7 @@ import Modals from "../../modals/Modals";
import Guide from './CoursesGuide';
import AddStudentModal from '../members/modal/AddStudentModal'
import AddTeacherModal from '../members/modal/AddTeacherModal'
import Jointheclass from '../../modals/Jointheclass'
// 点击按钮复制功能
// function jsCopy(){
// var e = document.getElementById("copy_invite_code");
@ -45,7 +45,9 @@ class CoursesBanner extends Component {
modalsBottomval: "",
antIcon:false,
coursedata:undefined,
is_guide:false
is_guide:false,
excellent:false,//是否是金品课堂
yslJointhe:false,
}
}
componentDidMount() {
@ -69,7 +71,7 @@ class CoursesBanner extends Component {
let data = result.data;
this.setState({
coursedata: data,
excellent:data.excellent,
})
}else{
this.onloadupdatabanner()
@ -107,6 +109,13 @@ class CoursesBanner extends Component {
}
};
//金品课堂的弹框
myyslgradin=()=>{
this.setState({
yslJointhe:true
})
}
showActionPoll=(i,s,ss)=>{
this.setState({
@ -342,17 +351,42 @@ class CoursesBanner extends Component {
}
postsettings=()=>{
window.location.href = "/courses/" + this.props.match.params.coursesId + "/settings";
};
postsettingstwo=()=>{
var ids =1;
try {
if(this.state.coursedata.is_public){
ids=1;
}else {
ids=0;
}
}catch (e) {
}
window.location.href = `/courses/${this.props.match.params.coursesId}/newgolds/settings/${ids}`;
};
hideAccountProfile=()=>{
this.setState({
AccountProfiletype:false
})
}
};
ysljoinmodalCancel=()=>{
this.setState({
yslJointhe:false
})
};
ysljoinmodalCanceltwo=()=>{
this.setState({
yslJointhe:false
})
window.location.reload();
};
render() {
let { Addcoursestypes, coursedata, modalsType, modalsTopval, loadtype,modalsBottomval,antIcon,is_guide,AccountProfiletype} = this.state;
let { Addcoursestypes, coursedata,excellent, modalsType, modalsTopval, loadtype,modalsBottomval,antIcon,is_guide,AccountProfiletype} = this.state;
return (
<div>
@ -383,6 +417,7 @@ class CoursesBanner extends Component {
modalSave={this.ModalAction}
antIcon={antIcon}
></Modals>
<Jointheclass {...this.props} {...this.state} ysljoinmodalCancel={()=>this.ysljoinmodalCancel()} ysljoinmodalCanceltwo={()=>this.ysljoinmodalCanceltwo()}></Jointheclass>
<AddTeacherModal ref="addTeacherModal"
{...this.props}
isTeacher={this.state.isTeacher}
@ -407,6 +442,28 @@ class CoursesBanner extends Component {
{/*访
公开公开课堂非课堂成员可以访问*/}
<style>
{
`
.tag-orange .tag-name{
display: block;
width: auto;
background-color: #FF6800;
background-size: 100% 100%;
padding: 0px 4px;
color: #fff;
float: left;
height: 28px;
line-height: 28px;
font-size: 14px;
border-radius: 4px;
}
`
}
</style>
{excellent===true?
<span className="tag-orange fl"><span className="tag-name mt10 ml10">国家精品</span></span>
:""}
<span className={"TabsWarp"}>
<CoursesListType
typelist={coursedata.course_end === true ? ["已结束"] : coursedata.is_public === true ? ["公开"] : ["私有"]}
@ -437,7 +494,7 @@ class CoursesBanner extends Component {
</div>
{excellent===false?
<div>
{coursedata.switch_to_student === true ?
<Tooltip placement="bottom" title={
@ -485,6 +542,31 @@ class CoursesBanner extends Component {
> 退出课堂 </a>:""}
</div>
:
<div>
<style>
{
`
.user_white_btn{border: 1px solid #ffffff;color: #ffffff!important;}
`
}
</style>
{/*coursedata.course_identity === 6 是非课堂成员*/}
{coursedata.course_identity === 6&&coursedata.educoder_teacher===false?
<a className="fr user_default_btn user_white_btn mr20 font-18" style={{width:"130px"}}
onClick={() => this.myyslgradin()}>加入课堂</a>: ""}
{coursedata.course_identity === 6&&coursedata.educoder_teacher===true?
<a className="fr user_default_btn user_white_btn mr20 font-18" style={{width:"130px"}}
onClick={() => this.myyslgradin()}>加入课堂</a>: ""}
{/*{this.props.isStudent()?<a className="fr user_default_btn user_blue_btn mr20 font-18"*/}
{/* onClick={() => this.exitclass()}*/}
{/*> 退出课堂 </a>:""}*/}
</div>
}
<style>
{
@ -583,10 +665,10 @@ class CoursesBanner extends Component {
<li className={"mt7 mr10im"}>
<a onClick={()=>this.addStudent()}>
<span className="color-white fl font-16 bannerurli width100f">添加学生</span>
<span className={"color-white fl font-16 bannerurli width100f"}>添加学生</span>
</a>
</li>
{excellent===false?
<li className={"mt7 mr10im ml10"} style={{overflow:"hidden"}}>
<a>
<span className="color-grey-c fl font-16" style={{marginRight: "10px"}}>邀请码</span>
@ -612,7 +694,7 @@ class CoursesBanner extends Component {
<input id="copy_invite_code" value={coursedata.invite_code}/>
</span>
</a>
</li>
</li>:""}
<li className={"mt7 ml10 mr0 "}>
<style>
@ -623,6 +705,7 @@ class CoursesBanner extends Component {
}
</style>
<Popover placement="bottom" content={
excellent===false?
<ul className="sandianbox" style={{
display: 'block',
right: "-113px",
@ -646,6 +729,16 @@ class CoursesBanner extends Component {
{coursedata.is_admin===true?coursedata.course_identity <3?<div className={"defaults"} onClick={() => this.ActionPoll(1)}>删除</div>
:"":""}
</ul>
:
<ul className="sandianbox" style={{
display: 'block',
right: "-113px",
top: "20px"
}}>
<div className={"defaults"} onClick={this.postsettingstwo}>设置</div>
{coursedata.is_admin===true?coursedata.course_identity <3?<div className={"defaults"} onClick={() => this.ActionPoll(1)}>删除</div>
:"":""}
</ul>
} trigger="hover">
<i className="iconfont icon-weibiaoti12 color-white font-14 relative"></i>
</Popover>

@ -648,7 +648,6 @@ class Coursesleftnav extends Component{
}
onDragEnd=(result)=>{
debugger
// console.log(result)
// let {course_modules}=this.props;
// let newcourse_modules=course_modules;
@ -739,6 +738,7 @@ class Coursesleftnav extends Component{
maincontent=(item,key)=>{
return ( <div className={"sandianbox"}>
{/*公告栏*/}
{/*作业*/}
{item.type==="shixun_homework"?<div onClick={e=>this.Navmodalnames(e,1,"shixun_homework",item.id)}>添加目录</div>:""}
{/*资源*/}
@ -792,6 +792,10 @@ class Coursesleftnav extends Component{
// // console.log(item.category_url)
// console.log(this.props.location.pathname)
console.log("778");
console.log("CoursesLeftNav");
console.log(this.props);
console.log(course_modules);
return(
@ -879,6 +883,8 @@ class Coursesleftnav extends Component{
<li title={item.name.length<7?"":item.name} onClick={(e)=>this.showsandians(e,key,item.category_url,1)} className={this.props.mainurl===item.category_url&&key===this.props.indexs?"liactive":"clearfix active"} onMouseLeave={(e)=>this.hidesandian(e,key)} onMouseEnter={(e)=>this.showsandian(e,key)}>
<a onClick={(e)=>this.showsandians(e,key,item.category_url,1)} className={ item.second_category===undefined?"fl ml20 pd0":item.second_category.length===0?"fl ml20 pd0":this.state.sandiantypes===key?"fl ml20 pd0 ebebeb":"fl ml20 pd0"}>
{
item.type==="announcement"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-xiaoxi1 mr10 fl":"iconfont icon-xiaoxi1 mr10 fl"}></i>:
item.type==="online_learning"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-xuexizhongxin mr10 fl":"iconfont icon-xuexizhongxin mr10 fl"}></i>:
item.type==="shixun_homework"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-daima mr10 fl":"iconfont icon-daima mr10 fl"}></i>:
item.type==="common_homework"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-putongzuoye mr10 fl":"iconfont icon-putongzuoye mr10 fl"}></i>:
item.type==="group_homework"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-fenzuzuoye mr10 fl":"iconfont icon-fenzuzuoye mr10 fl"}></i>:
@ -994,6 +1000,8 @@ class Coursesleftnav extends Component{
>
<a className={ item.second_category===undefined?"fl ml20 pd0":item.second_category.length===0?"fl ml20 pd0":this.state.sandiantypes===key?"fl ml20 pd0 ebebeb":"fl ml20 pd0"}>
{
item.type==="announcement"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-xiaoxi1 mr10 fl":"iconfont icon-xiaoxi1 mr10 fl"}></i>:
item.type==="online_learning"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-xuexizhongxin mr10 fl":"iconfont icon-xuexizhongxin mr10 fl"}></i>:
item.type==="shixun_homework"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-daima mr10 fl":"iconfont icon-daima mr10 fl"}></i>:
item.type==="common_homework"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-putongzuoye mr10 fl":"iconfont icon-putongzuoye mr10 fl"}></i>:
item.type==="group_homework"?<i className={this.props.location.pathname===item.category_url?"color-blue iconfont icon-fenzuzuoye mr10 fl":"iconfont icon-fenzuzuoye mr10 fl"}></i>:

@ -32,7 +32,7 @@ class CoursesListType extends Component {
{
typelist===undefined?"":typelist.map((item,key)=>{
return(
<Tooltip placement="bottom" title={tipval} getPopupContainer={()=>document.querySelector('.TabsWarp')}>
<Tooltip placement="bottom" title={tipval} getPopupContainer={()=>document.querySelector('.TabsWarp')} key={key}>
<span key={key}>
{item==="公开"?<span className={"edu-filter-btn edu-filter-btn-028d01 ml15 fl typestyle " + typesylename} >公开</span>:""}
{item==="已开启补交"?<span className={"edu-filter-btn edu-filter-btn-028d01 ml15 fl typestyle "+ typesylename} >已开启补交</span>:""}

@ -6,8 +6,9 @@ class NoneData extends Component{
super(props)
}
render(){
const { style } = this.props;
return(
<div className="edu-tab-con-box clearfix edu-txt-center">
<div className="edu-tab-con-box clearfix edu-txt-center" style={style}>
<img className="edu-nodata-img mb20" src={getImageUrl("images/educoder/nodata.png")}/>
<p className="edu-nodata-p mb20">暂时还没有相关数据哦</p>
</div>

@ -1173,6 +1173,7 @@ samp {
border-right: none!important;
box-shadow: none!important;
}
/* 这个加了干嘛的影响到了带addonAfter的input */
.searchViewAfter,.searchViewAfter:focus,.searchViewAfter .ant-input:hover,.ant-input-group .ant-input:focus{
border-right: none!important;
}

@ -0,0 +1,322 @@
import React,{ Component } from "react";
import { Input,Checkbox,Table, Pagination, Modal,Menu, Tooltip,Spin,Button,Form,Icon,message,Progress} from "antd";
import { WordsBtn,on, off, trigger,markdownToHTML,getImageUrl} from 'educoder';
import './myelearning.css'
import axios from 'axios';
import YslDetailCards from "./YslDetailCards.js";
import Jointheclass from '../../modals/Jointheclass'
//在线学习
class Elearning extends Component{
constructor(props){
super(props);
this.state={
description:"", //简介
isSpin:true,
start_learning:undefined, //是否要开始学习 没开始学习 点击第一个是开始学习 就是学习下面的从第一个开始
learned:0, //学习进度
last_shixun:"", //上次学习的实训
stages:[], //实践课程的章节
yslJointhe:false,
shixunsreplace:false,
hidestartshixunsreplacevalue:"",
shixunsmessage:"",
startshixunCombattype:false,
isSpins:false,
}
}
componentDidMount() {
// 记得删除退出课堂
console.log("获取到数据");
console.log(this.props);
let url = `/courses/${this.props.match.params.coursesId}/online_learning.json`;
// //
axios.get(url).then((response) => {
if(response){
if(response.data){
console.log("获取到到数据");
console.log(response);
this.setState({
description: response.data.description,
start_learning:response.data.start_learning,
learned:response.data.learned,
last_shixun:response.data.last_shixun,
stages:response.data.stages,
});
}
}
this.setState({
isSpin:false,
})
}).catch((error) => {
console.log(error);
this.setState({
isSpin:false,
})
});
}
componentDidUpdate = (prevProps) => {
};
//开始学习
Startlearning=()=>{
if(this.props.isNotMember()===true){
this.setState({
yslJointhe:true
})
}else {
let {stages}=this.state;
if(stages.length>0){
for(var i=0;i<stages.length;i++){
if(i===0){
var id=stages[0].shixuns_list[0].identifier;
let url = "/shixuns/" + id + "/shixun_exec.json";
axios.get(url).then((response) => {
if (response.data.status === -2) {
this.setState({
shixunsreplaces:true,
hidestartshixunsreplacevalues:response.data.message+".json"
})
} else if (response.data.status === -1) {
console.log(response)
}else if(response.data.status===-3){
this.setState({
shixunsmessages:response.data.message,
startshixunCombattypes:true,
})
} else {
console.log("开始学习了");
window.open("/tasks/" + response.data.game_identifier,'_blank');
//这个是传过来 调用刷新
this.Myreload();
// window.location.href = path
// let path="/tasks/"+response.data.game_identifier;
// this.props.history.push(path);
}
}).catch((error) => {
});
break;
}
console.log("这是"+i);
}
}
}
};
Startlearningtwo=()=>{
this.setState({
yslJointhe:true
})
};
ysljoinmodalCancel=()=>{
this.setState({
yslJointhe:false
})
};
ysljoinmodalCanceltwo=()=>{
this.setState({
yslJointhe:false
})
window.location.reload();
};
Myreload = ()=>{
window.location.reload();
};
hidestartshixunsreplace=(url)=>{
this.setState({
isSpins:true,
})
axios.get(url).then((response) => {
// debugger
if(response.status===200){
// let path="/shixuns/"+response.data.shixun_identifier+"/challenges";
// this.props.history.push(path);
message.success('重置成功,正在进入实训!');
this.startgameid(response.data.shixun_identifier);
this.setState({
shixunsreplaces:false,
isSpins:false,
})
// message.success('重置成功,正在进入实训!');
// this.startshixunCombat();
}}
).catch((error) => {
this.setState({
isSpins:false,
shixunsreplaces:false,
})
});
};
startgameid=(id)=>{
if(this.props.isNotMember()===true){
//这个是外部传过来的
this.Startlearningtwo();
return
}
let url = "/shixuns/" + id + "/shixun_exec.json";
axios.get(url).then((response) => {
if (response.data.status === -2) {
this.setState({
shixunsreplaces:true,
hidestartshixunsreplacevalues:response.data.message+".json"
})
} else if (response.data.status === -1) {
console.log(response)
}else if(response.data.status===-3){
this.setState({
shixunsmessages:response.data.message,
startshixunCombattypes:true,
})
} else {
console.log("开始学习了");
window.open("/tasks/" + response.data.game_identifier,'_blank');
//这个是传过来 调用刷新
this.Myreload();
// window.location.href = path
// let path="/tasks/"+response.data.game_identifier;
// this.props.history.push(path);
}
}).catch((error) => {
});
};
hidestartshixunCombattype=()=>{
this.setState({
startshixunCombattypes:false
})
}
render(){
let{description,whethertoedit,isSpin,start_learning,hidestartshixunsreplacevalues,learned,last_shixun} =this.state;
const isNotMembers=this.props.isNotMember();//非课堂成员
const antIcon = <Icon type="loading" style={{ fontSize: 24 }} spin />;
return(
<React.Fragment >
<div id={"zhudiv"}>
<Jointheclass {...this.props} {...this.state} ysljoinmodalCancel={()=>this.ysljoinmodalCancel()} ysljoinmodalCanceltwo={()=>this.ysljoinmodalCanceltwo()}></Jointheclass>
<Modal
keyboard={false}
title="提示"
visible={this.state.startshixunCombattypes}
closable={false}
footer={null}
>
<div className="task-popup-content">
<p className="task-popup-text-center font-16 pb20">本实训的开启时间{this.state.shixunsmessages} <br/>开启时间之前不能挑战 </p>
</div>
<div className="task-popup-submit clearfix">
{/*<a onClick={this.hidestartshixunCombattype} className="task-btn fl">取消</a>*/}
<a className="task-btn task-btn-orange fr"
style={{marginRight:'51px'}}
onClick={this.hidestartshixunCombattype}>知道了</a>
</div>
{/*<p className="inviteTipbtn with100 fl">*/}
{/*<a onClick={this.hidestartshixunCombattype}>知道了</a>*/}
{/*</p>*/}
</Modal>
<Modal
keyboard={false}
title="提示"
visible={this.state.shixunsreplaces}
closable={false}
footer={null}
>
<Spin indicator={antIcon} spinning={this.state.isSpins}>
<div className="task-popup-content">
<p className="task-popup-text-center font-16 pb20">实训已经更新了正在为您重置!</p>
</div>
<div className="task-popup-submit clearfix">
<a className="task-btn task-btn-orange fr"
style={{marginRight:'51px'}}
onClick={() => this.hidestartshixunsreplace(hidestartshixunsreplacevalues)}>知道了</a>
</div>
</Spin>
</Modal>
<div className="edu-back-white">
{
this.props.isAdmin()===true?"":
<div>
{
start_learning===undefined?"":start_learning===false?
<div className="clearfix padding30 bor-bottom-greyE" style={{textAlign: "center"}}>
<div style={{height: '40px',textAlign: "center"}}>
<span className=" fl color-dark-21 " style={{height: '40px', textAlign: "center",fontSize:"19px"}}>还未开始学习</span>
<Button className="ant-btn defalutSubmitbtn ant-btn-primary colorblue font-16 fr" onClick={()=>this.Startlearning()}>
<span>开始学习</span></Button>
</div>
</div>
:
<div className="clearfix padding30 bor-bottom-greyE" style={{textAlign: "center"}}>
<div style={{height: '40px',textAlign: "left"}}>
<span className=" color-dark-21 " style={{height: '40px', textAlign: "center",fontSize:"19px",color:"#05101A"}}>已学{learned}%</span>
</div>
<div style={{marginTop:"7px",width:"401px"}}>
<Progress percent={learned} showInfo={false} />
</div>
<div style={{marginTop:"7px",textAlign: "left"}}>
<span className="font-16">上次学至</span><span style={{color:"#4CADFF",marginLeft:"25px"}}>{last_shixun}</span>
</div>
</div>
}
</div>
}
{/*简介*/}
<div className="clearfix pl30 pr30" style={{paddingBottom:"22px"}}>
<div style={{textAlign: "left",marginTop:"10px",paddingBottom: "10px"}}>
<span className=" color-dark-21 " style={{textAlign: "center",fontSize:"19px"}}>简介</span>
</div>
<div className="edu-back-white new_li editormd-html-preview " >
<p className="markdown-body fonttext " dangerouslySetInnerHTML={{__html: markdownToHTML(description).replace(/▁/g,"▁▁▁")}}>
</p>
</div>
</div>
</div>
<Spin size="large" spinning={isSpin} id={"cdiv"}>
<div className=" clearfix" style={{marginTop:"20px"}}>
<div>
{/*开始学习*/}
<YslDetailCards {...this.state} {...this.props} Startlearningtwo={()=>this.Startlearningtwo()} Myreload={()=>this.Myreload()} ></YslDetailCards>
</div>
</div>
</Spin>
</div>
</React.Fragment>
)
}
}
const Elearningss = Form.create({ name: 'elearning' })(Elearning);
export default Elearningss;

@ -0,0 +1,226 @@
import React, { Component } from 'react';
import {getImageUrl} from 'educoder';
import { Tooltip,Modal,Icon,Spin,message} from 'antd';
import '../../paths/ShixunPaths.css';
import axios from 'axios';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
class YslDetailCards extends Component{
constructor(props){
super(props)
this.state={
showparagraph:false,
showparagraphkey:"",
showparagraphindex:"",
shixunsreplace:false,
hidestartshixunsreplacevalue:"",
shixunsmessage:"",
startshixunCombattype:false,
isSpin:false,
}
}
showparagraph =(key,index)=>{
this.setState({
showparagraph:true,
showparagraphkey:key,
showparagraphindex:index
})
};
hideparagraph=()=>{
this.setState({
showparagraph:false
})
};
startgameid=(id)=>{
if(this.props.isNotMember()===true){
//这个是外部传过来的
this.props.Startlearningtwo();
return
}
let url = "/shixuns/" + id + "/shixun_exec.json";
axios.get(url).then((response) => {
if (response.data.status === -2) {
this.setState({
shixunsreplace:true,
hidestartshixunsreplacevalue:response.data.message+".json"
})
} else if (response.data.status === -1) {
console.log(response)
}else if(response.data.status===-3){
this.setState({
shixunsmessage:response.data.message,
startshixunCombattype:true,
})
} else {
console.log("开始学习了");
window.open("/tasks/" + response.data.game_identifier,'_blank');
//这个是传过来 调用刷新
this.props.Myreload();
// window.location.href = path
// let path="/tasks/"+response.data.game_identifier;
// this.props.history.push(path);
}
}).catch((error) => {
});
};
componentDidMount(){
}
hidestartshixunsreplace=(url)=>{
this.setState({
isSpin:true,
})
axios.get(url).then((response) => {
// debugger
if(response.status===200){
// let path="/shixuns/"+response.data.shixun_identifier+"/challenges";
// this.props.history.push(path);
message.success('重置成功,正在进入实训!');
this.startgameid(response.data.shixun_identifier);
this.setState({
shixunsreplace:false,
isSpin:false,
startbtn:false,
})
// message.success('重置成功,正在进入实训!');
// this.startshixunCombat();
}}
).catch((error) => {
this.setState({
isSpin:false,
shixunsreplace:false,
})
});
}
hidestartshixunCombattype=()=>{
this.setState({
startshixunCombattype:false
})
}
render(){
let{showparagraph,showparagraphkey,showparagraphindex,hidestartshixunsreplacevalue} =this.state;
let { stages }=this.props;
const antIcon = <Icon type="loading" style={{ fontSize: 24 }} spin />;
return(
<div className="lesson-saved-list">
<Modal
keyboard={false}
title="提示"
visible={this.state.startshixunCombattype}
closable={false}
footer={null}
>
<div className="task-popup-content">
<p className="task-popup-text-center font-16 pb20">本实训的开启时间{this.state.shixunsmessage} <br/>开启时间之前不能挑战 </p>
</div>
<div className="task-popup-submit clearfix">
{/*<a onClick={this.hidestartshixunCombattype} className="task-btn fl">取消</a>*/}
<a className="task-btn task-btn-orange fr"
style={{marginRight:'51px'}}
onClick={this.hidestartshixunCombattype}>知道了</a>
</div>
{/*<p className="inviteTipbtn with100 fl">*/}
{/*<a onClick={this.hidestartshixunCombattype}>知道了</a>*/}
{/*</p>*/}
</Modal>
<Modal
keyboard={false}
title="提示"
visible={this.state.shixunsreplace}
closable={false}
footer={null}
>
<Spin indicator={antIcon} spinning={this.state.isSpin}>
<div className="task-popup-content">
<p className="task-popup-text-center font-16 pb20">实训已经更新了正在为您重置!</p>
</div>
<div className="task-popup-submit clearfix">
<a className="task-btn task-btn-orange fr"
style={{marginRight:'51px'}}
onClick={() => this.hidestartshixunsreplace(hidestartshixunsreplacevalue)}>知道了</a>
</div>
</Spin>
</Modal>
<style>{
`
.lesson-saved-list-item {
border-bottom: none!important;
margin-bottom: 20px;
background-color: #fff;
}
`
}</style>
{
stages && stages.map((item,key)=>{
return(
<div className={"lesson-saved-list-item"} key={key} id={"stage_div_"+key} >
<p className="clearfix title-line">
<a className="fl ring-blue mr10 mt2">
<img src={getImageUrl("images/educoder/icon/charpter-white.svg")} className="fl ml3 mt3"/>
</a>
<span className="font-18 font-bd lessonvalue" title={item.stage_name}>{item.stage_name}</span>
</p>
<div className="detail_for_paragraph clearfix" id={"detail_for_paragraph_"+key}>
<p className="color-dark-grey mt20 mb25 ml20 mr20 pl28 justify font-15">{item.stage_description}</p>
<div>
{
item.shixuns_list && item.shixuns_list.map((line,index)=>{
return(
<div className="clearfix paragraph lineh-30" key={index} onMouseEnter={()=>this.showparagraph(key,index)} onMouseLeave={this.hideparagraph}>
<li className="fl li-width63">
<span className="progressRing mr10">
{
line.complete_status === 1 ?<i className="iconfont icon-wancheng progressRing-over font-18 mt10"></i>
:<i className="iconfont icon-bofang progressRing-part font-18 mt10"></i>
}
</span>
<span className={this.props.current_user&&this.props.current_user.admin===false&&line.shixun_status==="暂未公开"?"paragraph_name color204":"paragraph_name color-grey3"}>
<span className="subject_stage_shixun_index">{key+1}</span>-{index+1}&nbsp;&nbsp;{line.shixun_name}
</span>
</li>
{
this.props.current_user&&this.props.current_user.admin===false&&line.shixun_status==="暂未公开"?
<li className="fr status_li"><span className="fr color204">暂未公开</span></li>
:
<li className={showparagraph===false?"none":"fr status_li"}>
{
showparagraphkey===key&&showparagraphindex===index?<div>
<Link to={'/shixuns/'+line.identifier+'/challenges'} className="mr30 color-blue_4C shixun_detail pointer fl" target="_blank">查看详情</Link>
{line.shixun_status==="暂未公开"?"":<a onClick={()=>this.startgameid(line.identifier)} className="btn_auto user_bluebg_btn fl" id="shixun_operation" >开始学习</a>}
</div>:""
}
</li>
}
</div>)
})
}
</div>
</div>
</div>
)
})
}
</div>
)
}
}
export default YslDetailCards;

@ -45,6 +45,7 @@ class Studentshavecompletedthelist extends Component {
teacherlist: undefined,
searchtext: "",
Teacherliststudentlist: undefined,
mylistansum:0,
review: null,
course_groupysls: undefined,
nocomment: false,
@ -1252,6 +1253,7 @@ class Studentshavecompletedthelist extends Component {
exercise_users: response.data.exercise_users,
current_answer_user: response.data.current_answer_user,
course_groups: response.data.course_groups,
mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users
})
if (response.data.current_answer_user === undefined || response.data.current_answer_user === null) {
// 学生未截止
@ -1622,6 +1624,7 @@ class Studentshavecompletedthelist extends Component {
commit_status: response.data.commit_status,
exercise_users: response.data.exercise_users,
current_answer_user: response.data.current_answer_user,
mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
})
if (response.data.exercise_types.subjective === 0) {
@ -1756,6 +1759,7 @@ class Studentshavecompletedthelist extends Component {
commit_status: response.data.commit_status,
exercise_users: response.data.exercise_users,
course_groups: response.data.course_groups,
mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
loadingstate: false,
columnsys: arr,
})
@ -1788,6 +1792,7 @@ class Studentshavecompletedthelist extends Component {
commit_status: response.data.commit_status,
exercise_users: response.data.exercise_users,
course_groups: response.data.course_groups,
mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
columnsys: arr,
})
}
@ -1823,6 +1828,7 @@ class Studentshavecompletedthelist extends Component {
commit_status: response.data.commit_status,
exercise_users: response.data.exercise_users,
course_groups: response.data.course_groups,
mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
loadingstate: false,
columnsys: arr,
})
@ -1854,6 +1860,7 @@ class Studentshavecompletedthelist extends Component {
commit_status: response.data.commit_status,
exercise_users: response.data.exercise_users,
course_groups: response.data.course_groups,
mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
loadingstate: false,
columnsys: arr,
})
@ -1885,6 +1892,7 @@ class Studentshavecompletedthelist extends Component {
commit_status: response.data.commit_status,
exercise_users: response.data.exercise_users,
course_groups: response.data.course_groups,
mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
columnsys: arr,
})
} else {
@ -1909,6 +1917,7 @@ class Studentshavecompletedthelist extends Component {
commit_status: response.data.commit_status,
exercise_users: response.data.exercise_users,
course_groups: response.data.course_groups,
mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
columnsys:arr,
})
}
@ -1923,6 +1932,7 @@ class Studentshavecompletedthelist extends Component {
commit_status: response.data.commit_status,
exercise_users: response.data.exercise_users,
course_groups: response.data.course_groups,
mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
loadingstate: false,
columnsys: this.state.columnsystwo,
})
@ -1948,6 +1958,7 @@ class Studentshavecompletedthelist extends Component {
commit_status: response.data.commit_status,
exercise_users: response.data.exercise_users,
course_groups: response.data.course_groups,
mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
loadingstate: false,
columnsys: arr,
})
@ -2401,7 +2412,7 @@ class Studentshavecompletedthelist extends Component {
render() {
const isAdmin = this.props.isAdmin();
let {data, datas, page, columns, course_groupyslsthree, columnstwo, styletable, course_groupyslstwodatas, limit, course_groupysls, course_groupyslstwodata, course_groupyslstwo, teacherlists, Teacherliststudentlist, order, columnss, course_groupsdatas, course_groups, Evaluationarray, unlimited, unlimiteds, unlimitedtwo, teacherlist, searchtext, loadingstate, review, nocomment, commented, unsubmitted, submitted, columnsys, exercise_users} = this.state;
let {data, datas, page, columns, course_groupyslsthree, columnstwo, styletable, course_groupyslstwodatas, limit, course_groupysls, course_groupyslstwodata, course_groupyslstwo, teacherlists, Teacherliststudentlist, order, columnss, course_groupsdatas, course_groups, Evaluationarray, unlimited, unlimiteds, unlimitedtwo, teacherlist, searchtext, loadingstate, review, nocomment, commented, unsubmitted, submitted, columnsys, exercise_users,mylistansum} = this.state;
// console.log("Studentshavecompletedthelist");
// console.log(this.props.current_status);
return (
@ -2789,11 +2800,11 @@ class Studentshavecompletedthelist extends Component {
</div>
{
Teacherliststudentlist && Teacherliststudentlist.exercise_types.total_users && Teacherliststudentlist.exercise_types.total_users > limit ?
mylistansum && mylistansum > limit ?
<div className="edu-txt-center mt30 mb20">
<Pagination showQuickJumper current={page}
onChange={this.paginationonChanges} pageSize={limit}
total={Teacherliststudentlist.exercise_types.total_users}></Pagination>
total={mylistansum}></Pagination>
</div>
: ""
}

@ -413,7 +413,7 @@ class Testpapersettinghomepage extends Component{
<a className="fr color-blue font-16 mt20" onClick={()=>this.setgameexercise(`/courses/${this.props.match.params.coursesId}/exercises/${this.props.match.params.Id}/users/${this.props.current_user.login}`)}>开始答题</a>:
<Link className="fr color-blue font-16 mt20"
to={`/courses/${this.props.match.params.coursesId}/exercises/${this.props.match.params.Id}/users/${this.props.current_user.login}`}>
{start_Value[Commonheadofthetestpaper&&Commonheadofthetestpaper.user_permission.current_status]}
{Commonheadofthetestpaper && Commonheadofthetestpaper.exercise_status===3?"":Commonheadofthetestpaper && Commonheadofthetestpaper.exercise_status===4?"":start_Value[Commonheadofthetestpaper&&Commonheadofthetestpaper.user_permission.current_status]}
</Link>
:""}

@ -0,0 +1,259 @@
import React,{ Component } from "react";
import { Input,Checkbox,Table, Pagination, Modal,Menu, Tooltip,Spin,Button,Form } from "antd";
import { WordsBtn,on, off, trigger,markdownToHTML,getImageUrl} from 'educoder';
import './myysleduinforms.css'
import axios from 'axios';
import TPMMDEditor from "../../tpm/challengesnew/TPMMDEditor";
// 公告栏
class Eduinforms extends Component{
constructor(props){
super(props);
this.messageRef = React.createRef();
this.state={
description:null,
isSpin:true,
whethertoedit:false,
}
}
componentDidMount() {
console.log("获取到数据");
console.log(this.props);
let url = `/courses/${this.props.match.params.coursesId}/informs.json`;
//
axios.get(url).then((response) => {
if(response){
if(response.data){
this.setState({
description:response.data.description,
isSpin:false,
})
}else {
this.setState({
description:null,
isSpin:false,
})
}
}else {
this.setState({
description:null,
isSpin:false,
})
}
}).catch((error) => {
console.log(error)
this.setState({
description:null,
isSpin:false,
})
});
}
componentDidUpdate = (prevProps) => {
}
bianji = (bians)=>{
this.setState({
whethertoedit:bians,
})
};
handleSubmit=(e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log(values.description);
if(values.description === undefined|| values.description === "" || values.description ===null){
this.props.showNotification(`请输入提交内容`);
return
}
var id=this.props.match.params.coursesId
var url = `/courses/${id}/update_informs.json`;
axios.post(url,{
description:values.description,
}).then((result) => {
if(result){
if(result.data){
if(result.data.status === 0){
this.setState({
description:values.description,
whethertoedit:false,
})
this.props.showNotification(result.data.message);
}else {
this.props.showNotification(result.data.message);
}
}
}
}).catch((error) => {
console.log(error)
})
}else{
console.log(err);
}
});
}
render(){
let{description,whethertoedit} =this.state;
const {getFieldDecorator} = this.props.form;
return(
<React.Fragment >
<div id={"zhudiv"}>
<div className="edu-back-white">
<p className="clearfix padding30 bor-bottom-greyE">
<p style={{height: '20px'}}>
<span className="font-18 fl color-dark-21">公告栏</span>
{
this.props.isAdmin()===true?
<li className="btn colorblue font-16 fr" style={{cursor: "default"}}
onClick={() => this.bianji(true)}>
编辑
</li>
:""
}
</p>
</p>
</div>
<Spin size="large" spinning={this.state.isSpin} id={"cdiv"}>
<div id={"cdiv1"}>
{
whethertoedit === false?
<div>
{
description === null || description=== undefined ||description === "" ?
<div className="alltask ">
<div className="edu-tab-con-box clearfix edu-txt-center"><img className="edu-nodata-img mb20"
src={getImageUrl("images/educoder/nodata.png")} />
<p className="edu-nodata-p mb20">暂时还没有相关数据哦</p></div>
</div>
:
<div className="edu-back-white ">
<div id="MakedownHTML" className={"markdown-body fonttext yslmtopcg"} dangerouslySetInnerHTML={{__html: markdownToHTML(description).replace(/▁/g, "▁▁▁")}}>
</div>
</div>
}
</div>
:
<div>
<Form layout='vertical' onSubmit={this.handleSubmit} >
<div className="edu-back-white ">
<div className={"yslmt16px"}>
<style>
{
`
.ant-form-item-children {
position: unset;
}
.rememberTip{
position:absolute;
right:0px;
bottom:-10px;
}
.chooseDes .ant-form-explain{
position:absolute;
bottom:-10px;
left:0px;
}
.ant-form-vertical .ant-form-explain {
margin-top: 0px !important;
margin-bottom: 0px !important;
padding-left: 0px !important;
}
.chooseDes .ant-form-item-with-help {
margin-bottom: 24px !important;
}
.courseMessageMD .ant-form-item-with-help {
margin-bottom: 24px !important;
}
.chooseDes .editormd-toolbar {
width: 100%;
min-height: 37px;
background: #fff;
display: none;
position: absolute !important;
left: 0;
z-index: 10;
border-bottom: 1px solid #ddd;
}
.yslmt16px .ant-form-item-with-help
{
margin-bottom: 24px !important;
}
`
}
</style>
<Form.Item
style={{"borderBottom":'none'}}
className="chooseDes "
>
{getFieldDecorator('description', {
rules: [{
required: true, message: '请在此输入内容,最多5000个字符',
}, {
max: 5000, message: '最大限制为5000个字符',
}],
})(
<TPMMDEditor ref={this.messageRef}
placeholder={'请在此输入内容,最多5000个字符'}
initValue={description}
mdID={'courseMessageMD'}
className="courseMessageMD "
height={518}
></TPMMDEditor>
)}
</Form.Item>
</div>
</div>
<Form.Item>
<div className="clearfix mt28 fr" style={{display:"block;"}}>
<a className="defalutCancelbtn fl mr20" onClick={()=>this.bianji(false)}>取消</a>
<Button htmlType="submit" className="ant-btn defalutSubmitbtn fl ant-btn-primary">
<span> </span></Button>
</div>
</Form.Item>
</Form>
</div>
}
</div>
</Spin>
</div>
</React.Fragment>
)
}
}
const Eduinformss = Form.create({ name: 'eduinforms' })(Eduinforms);
export default Eduinformss;

@ -0,0 +1,13 @@
.yslmt16px{
padding-top: 25px !important;
padding-left: 25px !important;
padding-right: 25px !important;
padding-bottom: 1px !important;
}
.yslmtopcg
{
padding: 25px !important;
}

@ -0,0 +1,918 @@
import React, {Component} from "React";
import {Form, Select, Input, Button, Checkbox, DatePicker,Spin,Icon,AutoComplete,InputNumber} from "antd";
import ApplyForAddOrgModal from '../../user/modal/ApplyForAddOrgModal';
import axios from 'axios';
import "../css/Courses.css";
import locale from 'antd/lib/date-picker/locale/zh_CN';
import moment from 'moment';
import Modals from '../../modals/Modals';
const { Option } = Select;
const dateFormat = 'YYYY-MM-DD';
function range(start, end) {
const result = [];
for (let i = start; i < end; i++) {
result.push(i);
}
return result;
}
function disabledDateTime() {
return {
disabledMinutes: () => range(1, 30).concat(range(31, 60)),
};
}
function disabledDate(current) {
console.log(current);
return current && current < moment().endOf('day').subtract(1, 'days');
}
function disabledDateysl(current) {
console.log(current);
return current && current < moment().endOf('day').add(1, 'days');
}
class Goldsubject extends Component {
constructor(props) {
super(props)
this.state = {
coursedata: undefined,
searchlist: [],
searchlistscholl:[],
listvalue: undefined,
fetching:false,
subject_id:"",
start_date:"",
}
}
// disabledEndDate= endValue => {
// const { datatimetwo } = this.state;
// var startValue =datatimetwo
// if (!endValue || !startValue) {
// return false;
// }
// return endValue.valueOf() < startValue.valueOf();
// };
isabledStartDate = (startValue) => {
const { datatime } = this.state;
var datatimes="";
if(datatime!==undefined){
datatimes=datatime;
datatimes=moment(datatimes, dateFormat).subtract(0, 'days');;
}else {
datatimes=datatime;
}
if (!startValue || !datatimes) {
return false;
}
return startValue.valueOf() > datatimes.valueOf();
};
disabledEndDate = (endValue) => {
// console.log("---------------------------------")
// console.log(moment(new Date(), dateFormat).add(1, 'days'));
// console.log(moment(new Date(), dateFormat).add(2, 'days'));
const { datatimetwo } = this.state;
var startvalue="";
if(datatimetwo!==undefined){
startvalue=datatimetwo;
startvalue=moment(startvalue, dateFormat).add(1, 'days');
}else {
startvalue=datatimetwo;
}
// console.log("结束时间");
// console.log(endValue);
// console.log(startvalue);
// console.log(datatimetwo);
if (!endValue || !startvalue) {
return false;
}
return endValue.valueOf() < startvalue.valueOf();
};
componentDidMount() {
console.log("新建金品课堂的数据");
console.log(this.props);
let coursesId = this.props.match.params.coursesId;
let user_school=this.props.current_user&&this.props.current_user.user_school;
// this.getschool("")
// this.Searchvalue("")
if (coursesId != undefined) {
let url = "/courses/" + coursesId + "/settings.json"
axios.get(url).then((result) => {
let data = result.data;
this.props.form.setFieldsValue({
course: data.course_list_name,
classroom: data.name,
period: data.class_period,
credit: data.credit,
checkboxgroup: data.course_module_types,
Realnamecertification: data.authentication,
Professionalcertification:data.professional_certification,
starttime: data.start_date === undefined ? "" : moment(data.start_date, dateFormat),
endtime: data.end_date === undefined ? "" : moment(data.end_date, dateFormat),
school:data.school
});
// starttime
this.setState({
datatimetwo: data.start_date === undefined ? "" : moment(data.start_date, dateFormat),
datatime: data.end_date === undefined ? "" : moment(data.end_date, dateFormat),
is_public: this.props.match.params.id,
Realnamecertification: data.authentication,
Professionalcertification:data.professional_certification,
name: data.name,
class_period: data.class_period,
credit: parseFloat(data.credit),
course_module_types: data.course_module_types,
school:data.school,
});
this.handleSearchschool(data.school);
}).catch((error) => {
console.log(error);
})
}else{
let url = "/courses/new.json"
axios.get(url).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error);
})
// console.log(user_school);
this.props.form.setFieldsValue({
school:user_school,
});
this.setState({
school:user_school,
});
this.handleSearchschool(user_school);
}
}
componentDidUpdate(prevProps) {
if(prevProps.current_user !== this.props.current_user){
let user_school=this.props.current_user&&this.props.current_user.user_school;
this.props.form.setFieldsValue({
school:user_school,
});
this.setState({
school:user_school,
});
this.handleSearchschool(user_school);
}
}
onChangeTimepublishs = (date, dateString) => {
console.log("结束时间11111111111");
console.log(dateString);
if(dateString===""){
this.setState({
datatime: undefined,
})
console.log("结束时间111111111116");
this.props.form.setFieldsValue({
endtime:undefined,
});
}else{
console.log("结束时间111111111117");
this.setState({
datatime: dateString,
});
this.props.form.setFieldsValue({
endtime: moment(dateString, dateFormat),
});
}
};
onChangeTimepublishstwo = (date, dateString) => {
console.log("结束时间22222222222");
console.log(dateString);
if(dateString===""){
console.log("结束时间222222222223");
this.setState({
datatimetwo: undefined,
})
this.props.form.setFieldsValue({
starttime: undefined,
});
}else{
console.log("结束时间222222222224");
this.setState({
datatimetwo: dateString,
})
this.props.form.setFieldsValue({
starttime: moment(dateString, dateFormat),
});
}
};
cancelmodel = () => {
this.setState({
Modalstype: false,
Loadtype: false,
Modalstopval: "",
ModalCancel: "",
ModalSave: "",
})
}
scrollToAnchor = (anchorName) => {
if (anchorName) {
// 找到锚点
let anchorElement = document.getElementById(anchorName);
// 如果对应id的锚点存在就跳转到锚点
window.scrollTo(0, anchorElement.offsetTop - window.innerHeight / 2);
}
}
//提交数据的地方
handleSubmit = (e) => {
e.preventDefault();
let first_category_url=this.props.current_user.first_category_url;
let coursesId = this.props.match.params.coursesId;
var is_public =this.props.match.params.id;
let {datatime,datatimetwo} = this.state
// console.log(is_public)
if (coursesId != undefined) {
// 编辑
this.props.form.validateFields((err, values) => {
if (err) {
return;
}
// if(values.course===""||values.course===undefined){
// this.scrollToAnchor("iscourse")
// return
//
// }
// if(values.classroom===""||values.classroom===undefined){
// this.scrollToAnchor("isclassroom")
// return
// }
//
// if(values.school===""||values.school===undefined){
// // this.scrollToAnchor("isschool")
// return
//
// }
if(values.checkboxgroup.length===0){
this.setState({
Modalstype:true,
Modalstopval:"请您至少添加一个课堂模块,",
ModalsBottomval:"否则您将无法新建课堂。",
Loadtype:true,
ModalSave:()=>this.cancelmodel(),
})
return
}
if (!err) {
// console.log('Received values of form: ', values);
let url = "/courses/" + coursesId + ".json";
axios.put(url, {
// subject_id:128, //这是带过来的值 编辑就不要传值了
name: values.classroom,
class_period: values.period,
credit: parseFloat(values.credit),
start_date:values.starttime,
end_date: values.endtime,
is_public: is_public, //这是也是带过来的值
course_module_types: values.checkboxgroup,
school:values.school
}
).then((response) => {
// debugger
if (response.data.status === 0) {
// this.goback()
window.location.href=first_category_url;
var yslGuideone = window.localStorage.getItem('yslGuideone');
try {
if(yslGuideone=== null){
window.localStorage.setItem('yslGuideone', "true");
return
}
if(yslGuideone=== undefined){
window.localStorage.setItem('yslGuideone', "true");
return
}
}catch (e) {
}
}
}).catch((error) => {
console.log(error)
})
}
console.log("错误信息信息信息1");
console.log(err);
console.log(values.starttime);
console.log(values.endtime);
});
} else {
this.props.form.validateFields((err, values) => {
console.log("错误信息信息信息2");
console.log(err);
if (err) {
return;
}
// if(values.course===""||values.course===undefined){
// this.scrollToAnchor("iscourse")
// return
// }
// if(values.classroom===""||values.classroom===undefined){
// this.scrollToAnchor("isclassroom")
// return
// }
// if(values.school===""||values.school===undefined){
// // this.scrollToAnchor("isschool")
// return
//
// }
if (!err) {
// debugger
//新建
// console.log('Received values of form: ', values);
let url = "/courses.json";
axios.post(url, {
subject_id:128, //这是带过来的值
name: values.classroom,
class_period: values.period,
credit: parseFloat(values.credit),
start_date:values.starttime,
end_date: values.endtime,
is_public: is_public, //这是也是带过来的值
course_module_types: values.checkboxgroup,
school:values.school
}
).then((response) => {
if (response.status === 200) {
// this.goback
window.location.href=response.data.first_category_url;
var yslGuideone = window.localStorage.getItem('yslGuideone');
try {
if(yslGuideone=== null){
window.localStorage.setItem('yslGuideone', "true");
return
}
if(yslGuideone=== undefined){
window.localStorage.setItem('yslGuideone', "true");
return
}
}catch (e) {
}
}
}).catch((error) => {
console.log(error)
})
}
});
}
}
goback = () => {
// if(this.props.match.params.coursesId===undefined){
// this.props.history.push("/courses");
// }else{
// this.props.history.push(this.props.current_user.first_category_url);
// }
// window.history.go(-1)
this.props.history.goBack()
}
onCheckAllChange = (e) => {
// console.log(e.target.checked)
this.setState({
is_public: e.target.checked == true ? 1 : 0,
});
}
onchanges =(e)=>{
this.setState({
Realnamecertification:e.target.checked,
})
// console.log(e.target.checked);
}
onchangess=(e)=>{
this.setState({
Professionalcertification:e.target.checked,
})
// console.log(e.target.checked);
}
Searchvalue=(value)=>{
let url="/courses/search_course_list.json";
axios.post(url,{
search:value
}).then((result)=>{
// console.log(result.data)
if (result.data.message===undefined) {
this.setState({
searchlist: result.data.course_lists,
// course:value,
})
}
// this.props.form.setFieldsValue({
// course:value
// })
}).catch((error)=>{
console.log(error)
})
}
handleSearch=(value)=>{
if(value!=""){
this.props.form.setFieldsValue({
classroom:value,
// course:value
});
this.Searchvalue(value)
}
};
handleChange=(value)=>{
this.props.form.setFieldsValue({
// course:value,
classroom:value
})
};
handleSearchschool=(value)=>{
if(value!="") {
// this.props.form.setFieldsValue({
// // school: value,
// fetching: true,
// });
this.setState({
fetching: true,
school: value
})
this.getschool(value)
}
};
handleChangeschools=(value)=>{
// this.props.form.setFieldsValue({
// // school: value,
// fetching: true,
// });
// this.setState({
// fetching: true
// })
}
handleChangeschool=(value)=>{
this.setState({
school:value
});
this.props.form.setFieldsValue({
school:value,
})
};
getschool=(value)=>{
// this.props.form.setFieldsValue({
// school: value
// })
let url="/schools/school_list.json";
axios.get(url,{
params: {
search: value
}
}).then((result)=>{
if(result){
if (result.data.status===0) {
this.setState({
searchlistscholl: result.data.school_names,
})
if(result.data.school_names.length!=0){
this.setState({
fetching: false
})
}
}
}
}).catch((error)=>{
console.log(error)
})
}
showApplyForAddOrgModal = () => {
this.applyForAddOrgForm.setVisible(true)
}
render() {
let {datatime,datatimetwo,school,searchlistscholl} = this.state;
const {getFieldDecorator} = this.props.form;
const propsWithoutForm = Object.assign({}, this.props)
delete propsWithoutForm.form
const options = this.state.searchlist && this.state.searchlist.map(d => <Option key={d.name} value={d.name}>{d.name}</Option>);
const optionschool = this.state.searchlistscholl===undefined?"":this.state.searchlistscholl===null?"":this.state.searchlistscholl==="[]"?"":this.state.searchlistscholl.map(z => <Option key={z} value={z}>{z}</Option>);
// console.log(this.props.current_user.user_school)
// form合并了
// console.log("获取到的数据");
// console.log(this.state);
// console.log(this.props);
// console.log(this.props.current_user);
return (
<React.Fragment>
<div>
<style>
{
`
.color-green-light {
color: #45E660!important;
}
`
}
</style>
<ApplyForAddOrgModal ref="applyForAddOrgModal" wrappedComponentRef={(form) => this.applyForAddOrgForm = form} schoolName={school}
{...propsWithoutForm}></ApplyForAddOrgModal>
{/*提示*/}
<Modals
modalsType={this.state.Modalstype}
modalsTopval={this.state.Modalstopval}
modalCancel={this.state.ModalCancel}
modalSave={this.state.ModalSave}
modalsBottomval={this.state.ModalsBottomval}
loadtype={this.state.Loadtype}
/>
<div className="newMain clearfix">
<div className={"educontent mb20"}>
<div style={{width: '100%', height: '70px'}}>
<p className="ml15 fl color-black mt30 summaryname">{this.props.match.params.coursesId === undefined ? "新建课堂" : "编辑课堂"}</p>
<a onClick={this.goback} className="color-grey-6 fr font-16 ml30 mt18 mr20">返回</a>
</div>
<style>
{`
.ant-col-12{
width:800px;
}
`}
</style>
<Form onSubmit={this.handleSubmit} className={"edu-back-white newcourses"}>
{/*内容*/}
<style>
{`
.ant-select-dropdown{
// top: 221px !important;
// left: 115px !important;
width: 280px !important;
height: 160px;
}
.ant-select-dropdown-menu{
width: 280px !important;
height: 160px;
}
.construction .ant-input{
width: 280px !important;
margin-left: 0px !important;
}
.construction {
width: 280px;
margin-left: 10px;
}
.construction .ant-select-selection__placeholder, .ant-select-search__field__placeholder {
line-height: 28px;
z-index: 2000;
}
`}
</style>
{/*<div className="stud-class-set bor-bottom-greyE padding10200 ">*/}
{/* <div className={"TabsWarpcourse"}>*/}
{/* <Form.Item label="课程名称">*/}
{/* {getFieldDecorator('course', {*/}
{/* rules: [{required: true, message: "不能为空"}],*/}
{/* })(*/}
{/* <AutoComplete style={{ width: 280 }}*/}
{/* onSearch={this.handleSearch}*/}
{/* onChange={this.handleChange}*/}
{/* className={"fl construction "}*/}
{/* placeholder="例如:数据结构"*/}
{/* >*/}
{/* {options}*/}
{/* </AutoComplete>*/}
{/* )}*/}
{/* <span className={"newcoursestitle fl"}>*/}
{/* /!*错误示例数据结构2017本部数据结构2017秋季数据结构2017电子商务1班*!/*/}
{/* <p className="ant-progress-text">*/}
{/* <Icon style={{ color: '#52c41a' }} type="check-circle" theme="filled"/>*/}
{/* <span className={"color-grey-9 font-12 ml5"}>正确示例:数据结构</span>*/}
{/* </p>*/}
{/* <p className="ant-progress-text">*/}
{/* <Icon style={{ color: 'red' }} theme="filled" type="close-circle"/>*/}
{/* <span className={"color-grey-9 font-12 ml5"}>错误示例数据结构2019春</span>*/}
{/* </p>*/}
{/* </span>*/}
{/* <div id='iscourse'></div>*/}
{/* </Form.Item>*/}
{/* </div>*/}
{/*</div>*/}
<style>
{
`
.ml19{
margin-left:19px;
} }
`
}
</style>
<div className="stud-class-set bor-bottom-greyE padding1020 ">
<Form.Item label="课堂名称">
{getFieldDecorator('classroom', {
rules: [{required: true, message: "不能为空"}],
})(
<AutoComplete style={{ width: 280 }}
onSearch={this.handleSearch}
onChange={this.handleChange}
className={"fl construction "}
placeholder="例如:数据结构"
>
{options}
</AutoComplete>
)}
<span className={"newcoursestitle fl"}>
{/*(如果本学期包含多个班级,只需创建一个课堂然后在课堂内部建立不同的分班)*/}
<p className="ant-progress-text">
<Icon style={{ color: '#52c41a' }} type="check-circle" theme="filled"/>
<span className={"color-grey-9 font-12 ml5"}>正确示例数据结构2019春季班级</span>
</p>
<p className="ant-progress-text">
<Icon style={{ color: 'red' }} theme="filled" type="close-circle"/>
<span className={"color-grey-9 font-12 ml5"}>错误示例2019春季班级数据结构</span>
</p>
</span>
<div id='isclassroom'></div>
</Form.Item>
</div>
<div className="stud-class-set bor-bottom-greyE padding1020 coursenavbox coursenavboxtow yslinputcourput">
<style>
{`
.ant-form-item{
margin-bottom: 10px !important;
}
`}
</style>
<Form.Item
label="总学时"
hasFeedback
>
{getFieldDecorator("period",
{
rules:[{
required:false,
pattern: new RegExp(/^[0-9]\d*$/, "g"),
message: ''
}],
getValueFromEvent: (event) => {
return event.target.value.replace(/\D/g,'')
}}
)(
<Input id="period" className="greyInput " placeholder="例如30"/>
)}
</Form.Item>
<Form.Item
label="学分"
hasFeedback
>
{getFieldDecorator("credit",
{
rules:[{
required:false,
pattern: new RegExp(/^[0-9]\d*$/, "g"),
message: ''
}],
getValueFromEvent: (event) => {
return event.target.value.replace(/\D/g,'')
}}
)(
<Input id="credit" className={"greyInput "} placeholder="例如3"/>
)}
</Form.Item>
<Form.Item
label="开始时间"
>
{getFieldDecorator("starttime", {
rules: [{type: 'object',required: true, message: "开始时间不能为空"}],
})(
<span className="fl mt5">
<DatePicker
showToday={false}
placeholder="请选择开始时间"
// showTime={{format: 'HH:mm'}}
locale={locale}
format={dateFormat}
width={"210px"}
value={datatimetwo === undefined ? "" :datatimetwo === null ? "" : moment(datatimetwo, dateFormat)}
disabledTime={disabledDateTime}
disabledDate={this.isabledStartDate}
dropdownClassName="hideDisable"
onChange={this.onChangeTimepublishstwo}
/>
</span>
)}
</Form.Item>
<Form.Item
label="结束时间"
>
{getFieldDecorator("endtime", {
rules: [{type: 'object',required: true, message: "结束时间不能为空"}],
})(
<span className="fl mt5">
<DatePicker
showToday={false}
placeholder="请选择结束时间"
// showTime={{format: 'HH:mm'}}
locale={locale}
format={dateFormat}
width={"210px"}
value={datatime === undefined ? "" :datatime === null ? "" : moment(datatime, dateFormat)}
disabledTime={disabledDateTime}
disabledDate={this.disabledEndDate}
dropdownClassName="hideDisable"
onChange={this.onChangeTimepublishs}
/>
</span>
)}
</Form.Item>
</div>
<div className="stud-class-set bor-bottom-greyE padding10200 coursenavbox">
<Form.Item
label="课堂模块"
hasFeedback
>
{getFieldDecorator("checkboxgroup", {
initialValue: [
"announcement","online_learning","shixun_homework", "exercise",
],
})(
<Checkbox.Group style={{width: "800px", marginTop: "10px"}}>
<Checkbox value={"announcement"} className="fl" defaultChecked disabled>公告栏</Checkbox>
<Checkbox value={"online_learning"} className="fl" defaultChecked disabled>在线学习</Checkbox>
<Checkbox value={"shixun_homework"} className="fl">实训作业</Checkbox>
<Checkbox value={"common_homework"} className="fl">普通作业</Checkbox>
<Checkbox value={"exercise"} className="fl">试卷</Checkbox>
<Checkbox value={"poll"} className="fl">问卷</Checkbox>
<Checkbox value={"attachment"} className="fl">资源</Checkbox>
<Checkbox value={"board"} className="fl">讨论</Checkbox>
<Checkbox value={"course_group"} className="fl">分班</Checkbox>
</Checkbox.Group>
)}
</Form.Item>
</div>
{/*<div className="stud-class-set bor-bottom-greyE padding10200 coursenavbox height100px" >*/}
{/* <span className={"fl"}>*/}
{/* <Form.Item*/}
{/* label="加入课堂条件"*/}
{/* style={{margin: 0}}*/}
{/* >*/}
{/* {getFieldDecorator("Realnamecertification")(*/}
{/* <Checkbox checked={this.state.Realnamecertification} onChange={this.onchanges}>已实名认证</Checkbox>*/}
{/* // <Checkbox.Group style={{ width: "800px",marginTop: "10px"}}>*/}
{/* // <Checkbox value={"authentication"} className="fl">已实名认证</Checkbox>*/}
{/* // <Checkbox value={"professional_certification"} className="fl">已职业认证 <span className="ml30" > (勾选,则通过邀请码加入课堂的用户,需要完成相关认证才能加入课堂)</span></Checkbox>*/}
{/* // </Checkbox.Group>*/}
{/* )}*/}
{/* </Form.Item>*/}
{/* </span>*/}
{/* <span className={"fl mt40"}>*/}
{/* <Form.Item*/}
{/* label=""*/}
{/* >*/}
{/* {getFieldDecorator("Professionalcertification")(*/}
{/* <Checkbox checked={this.state.Professionalcertification} onChange={this.onchangess}>已职业认证<span*/}
{/* className="coursesselects"> (勾选,则通过邀请码加入课堂的用户,需要完成相关认证才能加入课堂)</span></Checkbox>*/}
{/* )}*/}
{/* </Form.Item>*/}
{/* </span>*/}
{/*</div>*/}
{/*<div className="stud-class-set padding10200 coursenavbox bor-bottom-greyE">*/}
{/* <Form.Item*/}
{/* label="公开设置"*/}
{/* hasFeedback*/}
{/* >*/}
{/* {getFieldDecorator("publicclass")(*/}
{/* <Checkbox id="publicclass"*/}
{/* onChange={this.onCheckAllChange}*/}
{/* checked={this.state.is_public}*/}
{/* className="fl">公开课堂</Checkbox>*/}
{/* )}*/}
{/* <span className={"coursesselect"}>(选中后本课堂对所有用户可见,否则仅本课堂成员可见)</span>*/}
{/* </Form.Item>*/}
{/*</div>*/}
<div className="stud-class-set padding10200 mb20">
<Form.Item label="课堂所属单位">
{getFieldDecorator('school', {
rules: [{required: true, message: "不能为空"}],
})(
<AutoComplete style={{ width: 280 }}
onSearch={this.handleSearchschool}
// onChange={this.handleChangeschools}
className={"fl construction mr10 "}
placeholder="请输入并选择课本堂的所属单位"
>
{optionschool}
</AutoComplete>
)}
<span className={"newcoursestitle fl"}>
{/*(输入内容出现匹配的下拉菜单←同账号管理的单位信息填写)*/}
</span>
<div id='isschool'></div>
</Form.Item>
{searchlistscholl.length===0&&this.state.fetching===true?<div style={{height:"20px",lineHeight:"20px"}} className="ml20">
<span>
<span style={{color: '#CDCDCD'}}>未找到包含{school}的高校</span>
<span style={{color: '#4CACFF', cursor: 'pointer'}} onClick={this.showApplyForAddOrgModal}>申请新增</span>
</span>
</div>:""}
</div>
<div className={"FAFAFA"}>
<Form.Item >
<div className="clearfix mt40 mb30">
<Button type="primary" htmlType="submit" className="defalutSubmitbtn fl mr20">
提交
</Button>
{/*<a className="defalutSubmitbtn fl mr20">提交</a>*/}
<a className="defalutCancelbtn fl" onClick={this.goback}>取消</a>
</div>
</Form.Item>
</div>
</Form>
</div>
</div>
</div>
</React.Fragment>
)
}
}
const WrappedCoursesNewAppGoldclass = Form.create({name: 'goldsubject'})(Goldsubject);
export default WrappedCoursesNewAppGoldclass;

@ -206,7 +206,9 @@ class PollDetailIndex extends Component{
{
user_permission && user_permission.current_status!=3 ?
<WordsBtn style="blue" to={`/courses/${this.props.match.params.coursesId}/polls/${this.props.match.params.pollId}/users/${this.props.current_user.login}`}>
{ user_permission && user_permission.current_status ==0 ? "继续答题" : user_permission.current_status == 1 ? "查看答题" : "开始答题" }
{ user_permission && user_permission.current_status ==0 ? "继续答题" : user_permission.current_status == 1 ?
(pollDetail && pollDetail.polls_status===3?"":pollDetail && pollDetail.polls_status===4?"":"查看答题")
: "开始答题" }
</WordsBtn>
:""
}

@ -143,7 +143,7 @@ class CommitSummary extends Component{
</div>
</div>
<div>
<div className="mt20">
<p className=" fl color-black mt10 summaryname" onClick={this.asdasdsad}>{this.state.description ? '修改总结' : '提交总结'}</p>
<a onClick={()=>this.gotohome()} className="color-grey-6 fr font-16 ml30 mt10">返回</a>
</div>
@ -174,7 +174,27 @@ class CommitSummary extends Component{
)}
</div>
</Form.Item>
<style>
{
`
.ant-form-item {
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding: 0;
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5;
list-style: none;
-webkit-font-feature-settings: 'tnum';
font-feature-settings: 'tnum';
/* margin-bottom: 24px; */
vertical-align: top;
}
`
}
</style>
<Form.Item>
<div className="clearfix mt40 mb30">
<Button type="primary" htmlType="submit" className="defalutSubmitbtn fl mr20" >

@ -2412,7 +2412,7 @@ class Listofworksstudentone extends Component {
console.log(error)
});
}
};
//
// setComputeTime=()=>{
// this.setState({

@ -132,7 +132,7 @@ class ShixunHomeworkPage extends Component {
<div className="newMain clearfix ">
<div className={"educontent mt20 mb20"} style={{width: "1200px"}}>
<div className={"educontent mt10 mb20"} style={{width: "1200px"}}>
<div className="educontent mb20">
<p className="clearfix mb20 mt10">

@ -230,7 +230,7 @@ class ShixunsHome extends Component {
<div className="tag-green">
<span className="tag-name"> {item.tag_name}</span>
<img style={{display:item.tag_name===null?"none":'block'}} src={require(`./tag2.png`)}/>
{/*<img style={{display:item.tag_name===null?"none":'block'}} src={require(`./tag2.png`)}/>*/}
</div>
<div className={item.power === false ? "closeSquare" : "none"}>
@ -301,7 +301,7 @@ class ShixunsHome extends Component {
<div className="tag-green">
<span className="tag-name"> {item.tag_name}</span>
<img style={{display:item.tag_name===null?"none":'block'}} src={require(`./tag2.png`)}/>
{/*<img style={{display:item.tag_name===null?"none":'block'}} src={require(`./tag2.png`)}/>*/}
</div>
<div className={item.power === false ? "closeSquare" : "none"}>

@ -0,0 +1,70 @@
import React, { Component } from 'react';
import { Modal} from 'antd';
import axios from 'axios';
//加入金品课堂
class Jointheclass extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
// console.log("加入金品课堂");
// console.log(this.props);
}
modalCancel=()=>{
this.props.ysljoinmodalCancel();
};
setDownload=()=>{
var id=this.props.match.params.coursesId
var url = `/courses/${id}/join_excellent_course.json`;
axios.post(url).then((result) => {
if(result){
if(result.data){
if(result.data.status === 0){
this.props.showNotification(result.data.message);
this.props.ysljoinmodalCanceltwo();
}else {
this.props.showNotification(result.data.message);
}
}
}
}).catch((error) => {
console.log(error)
})
}
render() {
// console.log("加入金品课堂2");
// console.log(this.props);
return(
<Modal
keyboard={false}
closable={false}
footer={null}
destroyOnClose={true}
title="提示"
centered={true}
visible={this.props.yslJointhe===undefined?false:this.props.yslJointhe}
width="600px"
>
<div className="educouddiv">
<div className={"tabeltext-alignleft"}><p style={{fontSize: "16px",marginTop:"46px"}}>是否确认该加入课堂?</p></div>
<div className="clearfix edu-txt-center" style={{marginTop:"98px"}}>
<a className="task-btn mr30" onClick={()=>this.modalCancel()}>取消</a>
<a className="task-btn task-btn-orange" onClick={()=>this.setDownload()}>确认</a>
</div>
</div>
</Modal>
)
}
}
export default Jointheclass;

@ -2,13 +2,15 @@ import React,{ Component } from "react";
import './css/moopCases.css'
import '../courses/css/Courses.css'
import { getImageUrl , MarkdownToHtml , ActionBtn } from 'educoder';
import { getImageUrl , MarkdownToHtml , ActionBtn , AttachmentList } from 'educoder';
import Tags from './CaseTags'
import axios from 'axios';
import Modals from '../modals/Modals'
// import AttachmentList from '../../common/components/attachment/AttachmentList'
class CaseDetail extends Component{
constructor(props){
super(props);
@ -65,6 +67,9 @@ class CaseDetail extends Component{
} = this.state;
return(
<div className="educontent mt10 mb50">
{
CaseDetail &&
<React.Fragment>
<Modals
modalsType={modalsType}
modalsTopval={modalsTopval}
@ -74,22 +79,22 @@ class CaseDetail extends Component{
>
</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>
<a href="/moop_cases" className="color-grey-9">教学案例</a> &gt; <span className="color-grey-3">{ CaseDetail.title}</span>
</p>
<p className="lineh-25 mb20 clearfix">
<span className="font-22 fl mr10 task-hide" style={{maxWidth:"800px"}}>
{ CaseDetail && CaseDetail.title}
{ 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>
CaseDetail.status == "pending" && <span class="edu-filter-btn fl cdefault edu-activity-green ml10">草稿</span>
}
{
CaseDetail && CaseDetail.status == "processing" && <span class="edu-filter-btn fl cdefault edu-activity-green ml10">审核中</span>
CaseDetail.status == "processing" && <span class="edu-filter-btn fl cdefault edu-activity-green ml10">审核中</span>
}
{
CaseDetail && CaseDetail.status == "refused" && <span class="edu-filter-btn fl cdefault edu-activity-orange ml10">未通过</span>
CaseDetail.status == "refused" && <span class="edu-filter-btn fl cdefault edu-activity-orange ml10">未通过</span>
}
</span>
<a href="/moop_cases" className="fr color-grey-9 mt5">返回</a>
@ -112,19 +117,19 @@ class CaseDetail extends Component{
<li className="clearfix lineh-20">
<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 mr30">编码<span className="color-grey-6">{CaseDetail.uuid}</span></span>
{
CaseDetail && CaseDetail.status=="published" ?
<span className="fl color-grey-9">发布时间<span className="color-grey-6">{CaseDetail && CaseDetail.published_at}</span></span>
<span className="fl color-grey-9">发布时间<span className="color-grey-6">{CaseDetail.published_at}</span></span>
:
<span className="fl color-grey-9">上传时间<span className="color-grey-6">{CaseDetail && CaseDetail.created_at}</span></span>
<span className="fl color-grey-9">上传时间<span className="color-grey-6">{CaseDetail.created_at}</span></span>
}
</span>
</li>
</div>
</div>
<div>
<span className="fl color-grey-9">作者</span>{CaseDetail && CaseDetail.author_name}/{CaseDetail && CaseDetail.author_school_name}
<span className="fl color-grey-9">作者</span>{CaseDetail.author_name}/{CaseDetail.author_school_name}
</div>
<style>
{`
@ -134,23 +139,11 @@ class CaseDetail extends Component{
`}
</style>
<div class="mt20 setMDStyle">
{ CaseDetail && CaseDetail.content && <MarkdownToHtml content={CaseDetail.content} id="casesDetail" selector="casesDetail" style={{width:"100%!important"}}></MarkdownToHtml>}
{ CaseDetail.content && <MarkdownToHtml content={CaseDetail.content} id="casesDetail" 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>
)
})
}
<AttachmentList {...this.props} {...this.state} attachments={attachments}></AttachmentList>
</div>
}
<div class="mt40">
@ -169,6 +162,8 @@ class CaseDetail extends Component{
</div>
</div>
</div>
</React.Fragment>
}
</div>
)
}

@ -1,3 +1,46 @@
.userbluebgfont{
color:#fff !important;
}
.kaike{
border-radius: 4px;
border: 1px solid rgba(255,255,255);
padding: 0px 10px;
cursor: pointer;
font-size: 14px;
display: block;
width: 120px;
text-align: center;
height: 40px;
line-height: 40px!important;
border-radius: 4px;
box-sizing: border-box;
}
.userNavs{
line-height: 96px;
background: #fff;
height:96px;
background:rgba(255,255,255,1);
box-shadow:3px 5px 11px 1px rgba(230,230,230,0.5);
border-radius:4px;
}
.userNavs li {
display: inline-block;
padding: 0 30px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.newbianji1{
font-size: 16px !important;
margin-right: 10px;
color: #4CACFF;
margin-bottom: 3px;
display: inline-block;
}
.solidright{
border-right: 1px solid #000;
height: 42px;
}

@ -1,5 +1,5 @@
import React,{ Component } from "react";
import { Modal,Radio,Input,Tooltip } from "antd";
import { Modal,Radio,Input,Tooltip, Menu, Dropdown, Icon,Breadcrumb } from "antd";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import SendPanel from "./sendPanel.js";
import { getImageUrl } from 'educoder';
@ -20,10 +20,25 @@ class DetailTop extends Component{
Modalsbottomval:'',
loadtype:false,
deletepathtype:false,
cardsModalsavetype:false
cardsModalsavetype:false,
MenuItemskey:1,
courseslist:[]
}
}
componentDidMount(){
let courseslist=[]
this.props.courses.map((item,key)=>{
if(1===key+1){
return(
courseslist.push(item)
)
}
})
this.setState({
courseslist:courseslist
})
console.log(courseslist)
}
allow_deletepath=()=>{
this.setState({
Modalstype:true,
@ -141,6 +156,29 @@ class DetailTop extends Component{
})
}
onVisibleChanges=(type)=>{
this.setState({
onVisibleChangestype:type
})
}
MenuItems=(keys)=>{
let courseslist=[]
this.props.courses.map((item,key)=>{
if(keys===key+1){
return(
courseslist.push(item)
)
}
})
console.log(courseslist)
this.setState({
MenuItemskey:keys,
courseslist:courseslist
})
}
render(){
let{detailInfoList}=this.props;
let{Modalstype,Modalstopval,cardsModalcancel,cardsModalsave,Modalsbottomval,cardsModalsavetype,loadtype}=this.state;
@ -150,8 +188,28 @@ class DetailTop extends Component{
lineHeight: '30px',
};
// <div className={this.props.courses===undefined||this.props.courses.length===0?"subhead":"subhead mb70"}>
let menu = (
<Menu>
{this.props.courses&&this.props.courses.map((item,key)=>{
return(
<div className="subhead">
<Menu.Item>
<a rel="noopener noreferrer" onClick={()=>this.MenuItems(key+1)}>
{key+1}次开课
</a>
</Menu.Item>
)
})}
</Menu>
);
return(
<div className={this.props.courses===undefined?"subhead":"subhead mb70"}>
<Modals
modalsType={Modalstype}
modalsTopval={Modalstopval}
@ -161,14 +219,37 @@ class DetailTop extends Component{
loadtype={loadtype}
>
</Modals>
{/*<div className={this.props.courses===undefined||this.props.courses.length===0?"subhead_content":"subhead_content pt100"}>*/}
{
detailInfoList &&
<div className="subhead_content">
<div className={this.props.courses===undefined?"subhead_content":"subhead_content pt100"}>
<div className="font-28 color-white clearfix">
<span className="fl lineh-40">
{detailInfoList.name}
</span>
<style>
{
`
.tag-orange .tag-name{
display: block;
width: auto;
background-color: #FF6800;
background-size: 100% 100%;
padding: 0px 4px;
color: #fff;
float: left;
height: 28px;
line-height: 28px;
font-size: 14px;
border-radius: 4px;
}
`
}
</style>
{this.props.courses===undefined?"":
<span className="tag-orange">
<span className="tag-name mt6 ml10">开放课程</span>
</span>}
{detailInfoList===undefined?"":detailInfoList.allow_statistics===true?
<Tooltip placement="bottom" title={"编辑"}>
<Link to={"/paths/"+this.props.match.params.pathId+"/edit"} className="ml10 ring-green fl mt10" >
@ -178,15 +259,15 @@ class DetailTop extends Component{
:""
}
{detailInfoList===undefined?"":detailInfoList.allow_statistics===true?
{detailInfoList===undefined?"":detailInfoList.allow_statistics===true?this.props.courses===undefined?
<Link to={"/paths/"+this.props.match.params.pathId+"/statistics"} className="user_default_btn edu-greenback-btn fr font-18"
>
学习统计
</Link>:""
</Link>:"":""
}
{ detailInfoList.allow_send === true &&
<SendPanel {...this.props} {...this.state}></SendPanel>
{ detailInfoList.allow_send === true?this.props.courses===undefined?
<SendPanel {...this.props} {...this.state}></SendPanel>:"":""
}
</div>
@ -202,7 +283,7 @@ class DetailTop extends Component{
{ detailInfoList.member_count!=0 ? <li><span>学习人数</span><span>{detailInfoList.member_count}</span></li> : ""}
</ul>
}
<div className="fr pr">
{this.props.courses===undefined?<div className="fr pr">
{detailInfoList===undefined?"":detailInfoList.allow_delete===true?<a
className={detailInfoList.publish_status===-1?"fl font-18 color-white mt5 mr20":"fl font-18 color-white mt5"}
style={{opacity: '0.6'}} onClick={this.allow_deletepath}
@ -210,30 +291,192 @@ class DetailTop extends Component{
{
detailInfoList.publish_status===0&&detailInfoList.allow_add_member===true?
<a className="user_default_btn user_bluebg_btn font-18 fl ml40 userbluebgfont"
onClick={this.applyissuePath}
>申请发布</a>:""
<a className="user_default_btn user_bluebg_btn font-18 fl ml40 userbluebgfont" onClick={this.applyissuePath}>申请发布</a>:""
}
{
detailInfoList.publish_status===1 && detailInfoList.allow_statistics===true?
<a className="user_default_btn user_grey_btn font-18 fl pointer ml40"
onClick={this.cancelissuePath}
>撤销申请</a>:""
<a className="user_default_btn user_grey_btn font-18 fl pointer ml40" onClick={this.cancelissuePath}>撤销申请</a>:""
}
{
detailInfoList.publish_status===2 && detailInfoList.allow_statistics===true?
<a className="user_default_btn user_grey_btn font-18 fl pointer ml40"
onClick={this.reovkissuePath}
>撤销发布</a>:""
<a className="user_default_btn user_grey_btn font-18 fl pointer ml40" onClick={this.reovkissuePath}>撤销发布</a>:""
}
</div>:""}
{this.props.courses===undefined?"":detailInfoList.is_creator===true?<div className="fr pr">
<a className={"fl font-18 color-white mt5 kaike mr20"}>开课</a>
<Link to={"/paths/"+this.props.match.params.pathId+"/statistics"} className="fl font-18 color-white mt5 kaike">
学习统计
</Link>
</div>:""}
</div>
{/*{this.props.courses===undefined||this.props.courses.length===0?"":<div className="userNavs mt20">*/}
{this.props.courses===undefined?"":<div className="userNavs mt20">
<li className={"fl"}>
<style>
{
`
.anticon-down{
font-size:14px !important;
transform:none !important;
}
.ant-dropdown-menu-item:hover, .ant-dropdown-menu-submenu-title:hover {
background-color: rgba(240,240,240,1);
}
.alist{
color:#000;
}
.alist:hover{
color:#000;
}
.aIcons{
color:#CDCDCD;
}
`
}
</style>
<Dropdown
overlay={menu}
onVisibleChange={this.onVisibleChanges}
>
<a className={"alist"}>
<i className="iconfont icon-bianji1 newbianji1"></i><span className={"color-orange"}>{this.state.MenuItemskey}</span> <Icon className="aIcons" type={!this.state.onVisibleChangestype?"down":"up"} />
</a>
</Dropdown>
</li>
<style>
{
`
.pdt28{
padding-top: 28px;
}
.ml23{
margin-left:23px;
}
.pathtime{
color: #9B9B9B;
font-size: 12px;
}
.pathtimes{
color: #05101A;
font-size: 14px;
}
`
}
</style>
<li className={"ml20"}>
{this.state.courseslist.map((item,key)=>{
// course_id: 1309
// course_identity: 5
// course_status: {status: 1, time: "进行至第5周共1642周"}
// first_category_url: "/courses/1309/informs"
return(
<div className={"ant-breadcrumb pdt28"} key={key}>
<span>
<div className="ant-breadcrumb-link fl mr23">
<div className={"pathtime"}>
开课时间
</div>
<div className={"pathtimes"}>
{item.start_date}
</div>
</div>
<div className="fl solidright"></div>
</span>
<span>
<div className="ant-breadcrumb-link fl mr23 ml23">
<div className={"pathtime"}>
结课时间
</div>
<div className={"pathtimes"}>
{item.end_date}
</div>
</div>
<div className="fl solidright"></div>
</span>
<span>
<div className="ant-breadcrumb-link fl mr23 ml23">
<div className={"pathtime"}>
报名人数
</div>
<div className={"pathtimes"}>
{item.student_count}
</div>
</div>
</span>
</div>
)
})
}
</li>
<li className={"fr"}>
<style>
{
`
.user-colorgrey-9b{color:#9B9B9B}
.user-colorgrey-green{color:#7ED321}
.background191{
background: rgba(191,191,191,1) !important;
color: #fff;
}
`
}
</style>
{this.state.courseslist.map((item,key)=>{
return(
<div key={key}>
{item.course_status.status===0?<a className="mr30 shixun_detail pointer fl user-colorgrey-green">即将开课</a>:""}
{item.course_status.status===1?<a className="mr30 shixun_detail pointer fl color-orange">{item.course_status.time}</a>:""}
{item.course_status.status===2?
detailInfoList.is_creator===true?<a className="mr30 shixun_detail pointer fl user-colorgrey-9b">已结束</a>:item.course_identity<6?
<a className="mr30 shixun_detail pointer fl user-colorgrey-9b">已结束</a>:"":""}
{item.course_status.status===0?
detailInfoList.is_creator===true?<a className="fr user_default_btn task-btn-orange font-18 mt25" href={item.first_category_url} target="_blank">
进入课堂
</a>:item.course_identity<6?<div className="fr user_default_btn background191 font-18 mt25"></div>
:<a className="fr user_default_btn task-btn-orange font-18 mt25" >立即报名</a>:""}
{item.course_status.status===1?
detailInfoList.is_creator===true?<a className="fr user_default_btn task-btn-orange font-18 mt25" href={item.first_category_url} target="_blank">
进入课堂
</a>:item.course_identity<6?<a className="fr user_default_btn task-btn-orange font-18 mt25" href={item.first_category_url} target="_blank">
立即学习
</a>:<a className="fr user_default_btn task-btn-orange font-18 mt25" ></a>:""}
{item.course_status.status===2?
detailInfoList.is_creator===true?<a className="fr user_default_btn task-btn-orange font-18 mt25" href={item.first_category_url} target="_blank">
进入课堂
</a>:item.course_identity<6?<a className="fr user_default_btn task-btn-orange font-18 mt25" href={item.first_category_url} target="_blank">
进入课堂
</a>:<div className="fr user_default_btn background191 font-18 mt25"></div>:""}
</div>
)})}
</li>
</div>}
</div>
}
</div>
)
}

@ -69,6 +69,9 @@ class PathDetailIndex extends Component{
constructor(props){
super(props)
this.state={
progress:undefined,
tags:undefined,
members:undefined,
detailInfoList:undefined,
clickdetailInfoListtype:false,
Modalstype:false,
@ -78,6 +81,32 @@ class PathDetailIndex extends Component{
cardsModalsave:this.cardsModalsave,
user_id:undefined,
loadtype:false,
courses:[
{
course_id: 1309,
first_category_url: "/courses/1309/informs",
start_date: "2019-07-16", // 开始时间
end_date: "2050-12-31", // 结束时间
student_count: 112, // 学习人数
course_identity: 5, // 当前用户在该课堂的身份
course_status: {
status: 1, // status0即将开课 1进行中 2已结束
time: "进行至第5周共1642周" // time当前进度
}
},
{
course_id: 1319,
first_category_url: "/courses/1319/shixun_homeworks/11549",
start_date:"2019-08-16",
end_date:"2050-12-31",
student_count: 112,
course_identity: 1,
course_status: {
status: 0,
time: ""
}
}
],
items: getItems(10)
}
this.onDragEnd = this.onDragEnd.bind(this);
@ -95,16 +124,17 @@ class PathDetailIndex extends Component{
}
const items = reorder(
this.state.detailInfoList.members,
this.state.members,
result.source.index,
result.destination.index
);
this.state.detailInfoList.members=items;
this.setState({
detailInfoList:this.state.detailInfoList,
members:items,
items
});
console.log(this.state.detailInfoList.members)
console.log(this.state.members)
console.log("items 数组数组数组数组")
console.log(items)
}
@ -157,13 +187,40 @@ class PathDetailIndex extends Component{
if(result.data.allow_visit===true){
this.setState({
detailInfoList:result.data,
items: getItems(result.data.members.length),
// courses:result.data.courses
// items: getItems(result.data.members.length),
})
}
}).catch((error)=>{
console.log(error);
})
let righturl="/paths/"+pathid+"/right_banner.json";
axios.get(righturl).then((result)=>{
if (result.data.status === 407 || result.data.status === 401) {
debugger
return;
}
if (result.data.status === 403) {
debugger
// window.location.href = "/403";
return;
}
this.setState({
// detailInfoList:result.data,
tags:result.data.tags,
progress:result.data.progress,
members:result.data.members,
items: getItems(result.data.members.length),
})
}).catch((error)=>{
console.log(error);
})
}
updatadetailInfoList=()=>{
@ -282,9 +339,10 @@ class PathDetailIndex extends Component{
console.log("上移");
// console.log(this.state.detailInfoList.members);
// console.log(response);
this.state.detailInfoList.members=response.data.members;
this.setState({
detailInfoList:this.state.detailInfoList,
members:response.data.members
});
// console.log(this.state.detailInfoList.members);
@ -307,9 +365,9 @@ class PathDetailIndex extends Component{
console.log("下移");
// console.log(this.state.detailInfoList.members);
// console.log(response);
this.state.detailInfoList.members=response.data.members;
this.setState({
detailInfoList:this.state.detailInfoList,
members:response.data.members
});
// console.log(this.state.detailInfoList.members);
}
@ -329,7 +387,11 @@ class PathDetailIndex extends Component{
Modalsbottomval,
cardsModalcancel,
cardsModalsave,
loadtype
loadtype,
progress,
members,
tags,
courses,
} = this.state
@ -403,14 +465,14 @@ class PathDetailIndex extends Component{
}
</div>
</div>
{detailInfoList === undefined ? "" : detailInfoList.tags === null ? "":
{tags === undefined ? "" :tags === null ? "":
<div className="edu-back-white padding40-20 mb10 relative">
<p className="font-16 mb20">技能标签 <span className="color-grey-c">{detailInfoList.tags.length}</span></p>
<p className="font-16 mb20">技能标签 <span className="color-grey-c">{tags.length}</span></p>
<div className={clickdetailInfoListtype===false?"newedbox newedboxheight":"newedbox newminheight"}>
<div className="clearfix" id="boxheight">
{
detailInfoList.tags && detailInfoList.tags.map((item,key)=>{
tags && tags.map((item,key)=>{
return(
<span value={key} className = {item.status == true ? "edu-filter-btn29BD8B fl" : "newedu-filter-btn fl"}>{item.tag_name}</span>
)
@ -420,7 +482,7 @@ class PathDetailIndex extends Component{
</div>
<Tooltip placement="bottom" title={"显示全部"}>
<div className={detailInfoList.tags.length>15&&clickdetailInfoListtype===false?"newsubscript mb9 color-grey-9 fr":"newsubscript mb9 color-grey-9 none"}
<div className={tags.length>15&&clickdetailInfoListtype===false?"newsubscript mb9 color-grey-9 fr":"newsubscript mb9 color-grey-9 none"}
onClick={()=>this.clickNewsubscript(0)}
><span className="mr8">...</span><Icon type="caret-down" />
</div>
@ -435,29 +497,29 @@ class PathDetailIndex extends Component{
</div>
}
{
this.props.checkIfLogin()===false?"":detailInfoList === undefined ? "" : detailInfoList.progress === null ? "" :
this.props.checkIfLogin()===false?"123213":progress === undefined ? "" : progress === null ? "" :
<div className="edu-back-white myProgress padding40-20 mb10">
<p className="mb20">
<span className="font-16 mr10">我的进展</span>
<Tooltip placement="bottom" title="获得经验值/总经验值">
<span className="color-green" >{detailInfoList.progress.my_score} / {detailInfoList.progress.all_score}</span>
<span className="color-green" >{progress.my_score} / {progress.all_score}</span>
</Tooltip>
</p>
<p className="clearfix mb10">
<span className="fl color-green">已学 {detailInfoList.progress.learned}%</span>
<span className="fr color-grey-9" id="time-consuming">学习耗时{this.timeStamp(detailInfoList.progress.time)} </span>
<span className="fl color-green">已学 {progress.learned}%</span>
<span className="fr color-grey-9" id="time-consuming">学习耗时{this.timeStamp(progress.time)} </span>
</p>
<div className="myProgressNav"><div className="myProgressGreen" style={{"width":`${detailInfoList.progress.learned+"%"}`}}></div></div>
<div className="myProgressNav"><div className="myProgressGreen" style={{"width":`${progress.learned+"%"}`}}></div></div>
</div>
}
{
detailInfoList ===undefined ?"":detailInfoList.members === null ?"":
members ===undefined ?"":members === null ?"":
<div className="teacherTeam edu-back-white clearfix" id="subject_members">
<p className="font-16 clearfix">教学团队</p>
{ detailInfoList===undefined?
detailInfoList.members && detailInfoList.members.map((item,key)=>{
{ members===undefined?
members && members.map((item,key)=>{
return(
<div className="teacherTeamItem clearfix df" key={key}>
<a href={item.user_url} target="_blank" className="fl">
@ -478,8 +540,8 @@ class PathDetailIndex extends Component{
</div>
)
})
:detailInfoList.allow_add_member===true?
detailInfoList.members && detailInfoList.members.map((item,key)=>{
:detailInfoList===undefined?"":detailInfoList.allow_add_member===true?
members && members.map((item,key)=>{
return(
<div className="teacherTeamItem clearfix df" key={key}>
<a href={item.user_url} target="_blank" className="fl">
@ -501,7 +563,7 @@ class PathDetailIndex extends Component{
{
detailInfoList===undefined?"":detailInfoList.allow_add_member===true? <div>
{key!=0?<div className="fr ml15 flex1"><a onClick={()=>this.moveup(item)}><Tooltip title="上移"><i className="color-green font-18 iconfont icon-xiangshangyi"></i></Tooltip></a></div>:""}
{key+1!=detailInfoList.members.length?<div className="fr ml15 flex1 "><a onClick={()=>this.movedown(item)}><Tooltip title="下移"><i className="color-green font-18 iconfont icon-xiangxiayi"></i></Tooltip></a></div>:""}
{key+1!= members&&members.length?<div className="fr ml15 flex1 "><a onClick={()=>this.movedown(item)}><Tooltip title="下移"><i className="color-green font-18 iconfont icon-xiangxiayi"></i></Tooltip></a></div>:""}
</div>
:""
}
@ -510,7 +572,7 @@ class PathDetailIndex extends Component{
</div>
)
})
: detailInfoList.members && detailInfoList.members.map((item,key)=>{
: members && members.map((item,key)=>{
return(
<div className="teacherTeamItem clearfix df" key={key}>
<a href={item.user_url} target="_blank" className="fl">

@ -13,6 +13,33 @@ class ShixunPathCard extends Component{
let {pathList}=this.props;
return(
<div className="educontent" id="subjects_list_content">
<style>
{
`
.tag-green{
position: absolute;
left: 15px;
bottom: 95px;}
.tag-green .tag-name{display: block;width: auto;
/*background-image: url("/images/educoder/tag1.png");*/
background: #000000;
border: 1px solid #fff;
border-radius: 3px;
font-size: 14px;
background-size: 100% 100%;padding: 0px 8px;color: #fff;float: left;}
.tag-orange{position: absolute;right: 0px;top:20px;}
.tag-orange .tag-name{display: block;width: auto;background-color:#FF6800;
background-size: 100% 100%;padding: 0px 8px;color: #fff;float: left;
height: 28px;
line-height: 28px;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
`
}
</style>
{
pathList && pathList.length > 0 ?
(
@ -22,15 +49,23 @@ class ShixunPathCard extends Component{
return(
<div className="square-Item" id={"item_"+key}>
{
item.tag_name === null ? "" :
<div className="tag-green">
<span className="tag-name">{item.tag_name}</span>
<img src={getImageUrl('images/educoder/tag2.png')}/>
{/*<img src={getImageUrl('images/educoder/tag2.png')}/>*/}
</div>
}
{
item.excellent === false ? "" :
<div className="tag-orange">
<span className="tag-name">开放课程</span>
</div>
}
<div className={item.allow_visit=== false ? "closeSquare" : "none"}>
<img src={getImageUrl("images/educoder/icon/lockclose.svg")}
className="mt80 mb25"/>
@ -38,7 +73,7 @@ class ShixunPathCard extends Component{
</div>
<Link to={"/paths/"+item.id} className="square-img" >
{/*target="_blank"*/}
<img alt="13?1543211263" src={"/"+item.image_url} style={{"display":"block"}}/>
<img alt="详情图片" src={"/"+item.image_url} style={{"display":"block"}}/>
</Link>
<div className="square-main">
<p className="task-hide">
@ -78,3 +113,4 @@ class ShixunPathCard extends Component{
}
}
export default ShixunPathCard;

@ -107,7 +107,7 @@ export default class TPMFork_listComponent extends Component {
<div className="tag-green">
<span className="tag-name"> {item.tag_name}</span>
<img src={require(`./shixunCss/tag2.png`)}/>
{/*<img src={require(`./shixunCss/tag2.png`)}/>*/}
</div>
<div className={item.power === false ? "closeSquare" : "none"}>

@ -314,6 +314,7 @@ export function TPMIndexHOC(WrappedComponent) {
})
}
showProfileCompleteDialog = () => {
this.dialogObj = {}
this.setState({
AccountProfiletype: true
})
@ -321,6 +322,20 @@ export function TPMIndexHOC(WrappedComponent) {
checkIfProfileCompleted = () => {
return this.state.current_user && this.state.current_user.profile_completed
}
showProfessionalCertificationDialog = () => {
this.dialogObj = {
content: '您需要去完成您的职业认证,才能使用此功能',
okText: '立即完成',
okHref: '/account/certification'
}
this.setState({
AccountProfiletype: true,
})
}
checkIfProfessionalCertification = () => {
return this.state.current_user && this.state.current_user.professional_certification
}
ShowOnlinePdf = (url) => {
return axios({
@ -362,10 +377,14 @@ export function TPMIndexHOC(WrappedComponent) {
isNotMember: this.isNotMember,
isUserid:this.state.coursedata&&this.state.coursedata.userid,
fetchUser: this.fetchUser,
showLoginDialog: this.showLoginDialog,
checkIfLogin: this.checkIfLogin,
showProfileCompleteDialog: this.showProfileCompleteDialog,
checkIfProfileCompleted: this.checkIfProfileCompleted,
checkIfProfessionalCertification: this.checkIfProfessionalCertification,
showProfessionalCertificationDialog: this.showProfessionalCertificationDialog,
ShowOnlinePdf:(url)=>this.ShowOnlinePdf(url),
DownloadFileA:(title,url)=>this.DownloadFileA(title,url),
DownloadOpenPdf:(type,url)=>this.DownloadOpenPdf(type,url)
@ -377,10 +396,12 @@ export function TPMIndexHOC(WrappedComponent) {
{...this.props}
{...this.state}
/> : ""}
{/* AccountProfile 也用作职业认证 */}
{AccountProfiletype===true ? <AccountProfile
hideAccountProfile={()=>this.hideAccountProfile()}
{...this.props}
{...this.state}
{...this.dialogObj}
/>:""}
<SiderBar Headertop={Headertop}/>
{/* 注释掉了1440 影响到了手机屏幕的展示 */}

@ -88,7 +88,7 @@ class ShixunCard extends Component {
item.tag_name === null ? "":
<div className="tag-green">
<span className="tag-name"> {item.tag_name}</span>
<img style={{display:'block',height: '28px'}} src={require(`./shixunCss/tag2.png`)}/>
{/*<img style={{display:'block',height: '28px'}} src={require(`./shixunCss/tag2.png`)}/>*/}
</div>
}
<div className={item.power === false ? "closeSquare" : "none"}>

@ -46,8 +46,13 @@ class AccountProfile extends Component {
}
/**
content: '您需要去完成您的职业认证,才能使用此功能',
okText: '立即完成',
okHref: '/account/certification'
*/
render() {
const { content, okText, okHref } = this.props;
return(
<Modal
keyboard={false}
@ -60,10 +65,10 @@ render() {
width="530px"
>
<div className="task-popup-content">
<p className="task-popup-text-center font-16">您需要去完善您的个人资料才能使用此功能</p>
<p className="task-popup-text-center font-16"> {content || '您需要去完善您的个人资料,才能使用此功能'}</p>
<div className="clearfix mt30 edu-txt-center">
<a className="task-btn mr30" onClick={()=>this.gotoback()}>取消</a>
<a className="task-btn task-btn-orange" href={"/account/profile/edit"}>立即完善</a>
<a className="task-btn task-btn-orange" href={ okHref || "/account/profile/edit" }> {okText || '立即完善'}</a>
</div>
</div>
</Modal>

@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { SnackbarHOC } from 'educoder';
import {Link} from 'react-router-dom';
import {Tooltip,Menu} from 'antd';
import Loadable from 'react-loadable';
@ -8,8 +8,8 @@ import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import UpgradeModals from '../../modals/UpgradeModals';
import axios from 'axios';
import {getImageUrl} from 'educoder';
import { TPMIndexHOC } from '../../tpm/TPMIndexHOC';
import { CNotificationHOC } from '../../courses/common/CNotificationHOC'
import InfosBanner from './InfosBanner'
import "./usersInfo.css"
import "../../courses/css/members.css"
import "../../courses/css/Courses.css"
@ -39,6 +39,11 @@ const InfosProject = Loadable({
loader: () => import('./InfosProject'),
loading:Loading,
})
const InfosVideo = Loadable({
loader: () => import('./video/InfosVideo'),
loading:Loading,
})
const $ = window.$;
class Infos extends Component{
@ -46,7 +51,7 @@ class Infos extends Component{
super(props);
this.state={
data:undefined,
is_current:true,
is_current:undefined,
is_edit:false,
sign:undefined,
type:0,
@ -88,6 +93,7 @@ class Infos extends Component{
}
//获取个人主页信息
getInfo = (user_login) =>{
let url =`/users/${user_login}/homepage_info.json`;
@ -196,12 +202,12 @@ class Infos extends Component{
}
// 试用申请
trialapplications =()=>{
this.setState({
isRenders: true,
showTrial:true
})
}
// trialapplications =()=>{
// this.setState({
// isRenders: true,
// showTrial:true
// })
// }
cancelModulationModels=()=>{
this.setState({
isRenders: false
@ -214,21 +220,25 @@ class Infos extends Component{
render(){
let {
data ,
is_current,
is_edit,
sign,
type,
followed,
id,
login,
isRenders,
moduleName,
next_gold
}=this.state;
let {username}= this.props.match.params;
let {pathname}=this.props.location;
moduleName=pathname.split("/")[3];
let isCurrent = true;
let currentLogin;
if(this.props.current_user && this.props.current_user.login != this.props.match.params.username){
isCurrent=false;
currentLogin = this.props.current_user.login;
}
const _commonProps = {
is_current: isCurrent,
login: currentLogin
}
return(
<div className="newMain">
{this.state.updata===undefined?"":<UpgradeModals
@ -237,165 +247,14 @@ class Infos extends Component{
{
isRenders && <Trialapplication {...this.props} {...this.state} Cancel={() => this.cancelModulationModels()}/>
}
<div className="user-main-half">
<div className="user-headImg"></div>
<div className="user-headCon">
<div className="pr" style={{"min-height": "465px"}}>
<div className="educontent pt80 clearfix edu-txt-center">
<div className="inline">
<div className="fl headtab">
<span>{is_current ? "我":"TA"}的经验值</span>
<a style={{ cursor: 'default' }}
// href={`${this.props.Headertop && this.props.Headertop.old_url}/users/${username}/user_experience`}
>{data && data.experience}</a>
</div>
<em className="v-h-line fl"></em>
<div className="fl headtab">
<span>{is_current ? "我":"TA"}的金币</span>
<a style={{ cursor: 'default' }}
// href={`${this.props.Headertop && this.props.Headertop.old_url}/users/${username}/user_grade`}
id="user_code">{data && data.grade}</a>
</div>
<div className="headphoto mt14">
<img alt="头像" id="user_avatar_show" nhname="avatar_image" src={data && `${getImageUrl('images/'+data.avatar_url)}`}/>
</div>
<div className="fl headtab">
<span>{is_current ? "我":"TA"}的粉丝</span>
<a style={{ cursor: 'default' }}
// href={`${this.props.Headertop && this.props.Headertop.old_url}/users/${username}/user_fanslist`}
id="user_h_fan_count">{data && data.fan_count}</a>
</div>
<em className="v-h-line fl"></em>
<div className="fl headtab">
<span>{is_current ? "我":"TA"}的关注</span>
<a style={{ cursor: 'default' }}
// href={`${this.props.Headertop && this.props.Headertop.old_url}/users/${username}/user_watchlist`}
>{data && data.follow_count}</a>
</div>
<span className="clearfix"></span>
<span className="myName">{data && data.name}</span>
</div>
</div>
<div className="educontent mt10 clearfix edu-txt-center">
<div className="inline">
{
data && is_current == false && data.identity =="学生" ? "" : <span className="mypost fl mr10">{data && data.identity}</span>
}
<a
// href={is_current ? `${this.props.Headertop && this.props.Headertop.old_url}/account/authentication` :"javascript:void(0)"}
// target="_blank"
className={is_current ? "ringauto fl" :"ringauto fl cdefault"}>
<Tooltip placement='bottom' title={ data && data.authentication ?"已实名认证":"未实名认证"}>
<i className={ data && data.authentication ? "iconfont icon-shenfenrenzheng font-13 color-blue":"iconfont icon-shenfenrenzheng font-13 color-grey-9"}></i>
</Tooltip>
</a>
<a
// href={is_current ? `${this.props.Headertop && this.props.Headertop.old_url}/account/professional_certification` :"javascript:void(0)"}
// target="_blank"
className={is_current ? "ringauto fl" :"ringauto fl cdefault"}>
<Tooltip placement='bottom' title={ data && data.professional_certification ?"已职业认证":"未职业认证"}>
<i className={ data && data.professional_certification ? "iconfont icon-zhiyerenzheng font-13 color-blue":"iconfont icon-zhiyerenzheng font-13 color-grey-9"}></i>
</Tooltip>
</a>
<a
// href={is_current ? `${this.props.Headertop && this.props.Headertop.old_url}/account/change_or_bind?type=phone` :"javascript:void(0)"}
// target="_blank"
className={is_current ? "ringauto fl" :"ringauto fl cdefault"}>
<Tooltip placement='bottom' title={ data && data.phone_binded ?"已手机认证":"未手机认证"}>
<i className={ data && data.phone_binded ? "iconfont icon-shoujirenzheng font-13 color-blue":"iconfont icon-shoujirenzheng font-13 color-grey-9"}></i>
</Tooltip>
</a>
<a
// href={is_current ? `${this.props.Headertop && this.props.Headertop.old_url}/my/account` :"javascript:void(0)"}
// target="_blank"
className={is_current ? "ringauto fl" :"ringauto fl cdefault"}>
<Tooltip placement='bottom' title={ data && data.email_binded ?"已邮箱认证":"未邮箱认证"}>
<i className={ data && data.email_binded ? "iconfont icon-youxiangrenzheng font-13 color-blue":"iconfont icon-youxiangrenzheng font-13 color-grey-9"}></i>
</Tooltip>
</a>
{/* <!--学院管理员身份--> */}
{
data && data.college_identifier &&
<a
// href={`${this.props.Headertop && this.props.Headertop.old_url}/colleges/${data.college_identifier}/statistics`} target="_blank"
className={is_current ? "ringauto fl" :"ringauto fl cdefault"}>
<Tooltip placement='bottom' title="学院管理员">
<i className="iconfont icon-chengyuanguanli font-12 color-blue" data-tip-down="学院管理员"></i>
</Tooltip>
</a>
}
</div>
</div>
<div className="mt15 educontent clearfix edu-txt-center">
{sign && <p className="mb20" style={{"height": "28px"}}>
{/* 这家伙很懒,什么都没留下~ */}
{
is_edit && is_current ?
<input type="text" id="mysign" class="mysign-input" placeholder="请输入您的个性签名" style={{height:"20px"}} value={sign} onInput={this.inputSign} onBlur={this.savemysign}/>
:
is_current ?
<a className="mysign-span" onClick={this.editmysign} style={{"display": "block"}}>{sign || ""}</a>
:
<span className="mysign-span" style={{"display": "block","cursor":"default"}}>{sign || ""}</span>
}
</p>}
{
is_current ?
<div className="inline">
{
data && data.attendance_signed ?
<React.Fragment>
<span className="user_default_btn user_grey_btn mb5">已签到</span>
<p id="attendance_notice" className="none font-12 color-grey-6" style={{"display":"block"}}>明日签到&nbsp;<font className="color-orange">+{next_gold}</font>&nbsp;</p>
</React.Fragment>
:
<a herf="javascript:void(0);" onClick={this.signFor} id="attendance" className="user_default_btn user_orange_btn fl mb15">签到</a>
// <a herf="javascript:void(0);" onClick={this.trialapplications} id="authentication_apply" className="user_default_btn user_private_btn fl ml15">试用申请</a>
}
</div>
:
<div className="inline">
<a href="javascript:void(0);" onClick={this.followPerson} className="user_default_btn user_watch_btn user_private_btn fl mr20">{followed ? "取消关注":"关注"}</a>
<a href={`${this.props.Headertop && this.props.Headertop.old_url}/messages/${login}/message_detail?target_ids=${id}`} className="user_default_btn user_private_btn fl">私信</a>
</div>
}
</div>
<div className="edu-txt-center navInfo">
<div className="inline">
<li className={`${moduleName == 'courses' ||moduleName == undefined ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'courses'})}
to={`/users/${username}/courses`}>课堂</Link>
</li>
<li className={`${moduleName == 'shixuns' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'shixuns'})}
to={`/users/${username}/shixuns`}>实训</Link>
</li>
<li className={`${moduleName == 'paths' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'paths'})}
to={`/users/${username}/paths`}>实践课程</Link>
</li>
<li className={`${moduleName == 'projects' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'projects'})}
to={`/users/${username}/projects`}>项目</Link>
</li>
<li className={`${moduleName == 'package' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'package'})}
to={`/users/${username}/package`}>众包</Link>
</li>
{/*{ data && data.identity!="学生" && <li> <a href={`${this.props.Headertop && this.props.Headertop.old_url}/users/${username}?type=m_bank`}>题库</a></li>}*/}
<InfosBanner
{...this.props}
{...this.state}
{..._commonProps}
signFor={this.signFor}
followPerson={this.followPerson}
></InfosBanner>
</div>
</div>
</div>
</div>
</div>
<Switch {...this.props}>
{/* --------------------------------------------------------------------- */}
@ -405,7 +264,7 @@ class Infos extends Component{
{/* http://localhost:3007/courses/1309/homework/9300/setting */}
<Route exact path="/users/:username/package"
render={
(props) => (<InfosPackage {...this.props} {...props} {...this.state} />)
(props) => (<InfosPackage {...this.props} {...props} {...this.state} {..._commonProps}/>)
}
></Route>
@ -413,35 +272,42 @@ class Infos extends Component{
{/* http://localhost:3007/courses/1309/homework/9300/setting */}
<Route exact path="/users/:username/courses"
render={
(props) => (<InfosCourse {...this.props} {...props} {...this.state} />)
(props) => (<InfosCourse {...this.props} {...props} {...this.state} {..._commonProps}/>)
}
></Route>
{/* 实训 */}
<Route exact path="/users/:username/shixuns"
render={
(props) => (<InfosShixun {...this.props} {...props} {...this.state} />)
(props) => (<InfosShixun {...this.props} {...props} {...this.state} {..._commonProps}/>)
}
></Route>
{/* 实训课程 */}
<Route exact path="/users/:username/paths"
render={
(props) => (<InfosPath {...this.props} {...props} {...this.state} />)
(props) => (<InfosPath {...this.props} {...props} {...this.state} {..._commonProps}/>)
}
></Route>
{/* 项目 */}
<Route exact path="/users/:username/projects"
render={
(props) => (<InfosProject {...this.props} {...props} {...this.state} />)
(props) => (<InfosProject {...this.props} {...props} {...this.state} {..._commonProps}/>)
}
></Route>
{/* 项目 */}
<Route exact path="/users/:username/videoes"
render={
(props) => (<InfosVideo {...this.props} {...props} {...this.state} {..._commonProps}/>)
}
></Route>
<Route exact path="/users/:username"
render={
(props) => (<InfosCourse {...this.props} {...props} {...this.state} />)
(props) => (<InfosCourse {...this.props} {...props} {...this.state} {..._commonProps}/>)
}
></Route>
@ -450,4 +316,5 @@ class Infos extends Component{
)
}
}
export default CNotificationHOC() ( SnackbarHOC() ( TPMIndexHOC(Infos) ));
// CNotificationHOC() ( SnackbarHOC() ( TPMIndexHOC))
export default (Infos) ;

@ -161,7 +161,7 @@ class InfosBank extends Component{
modalSave={modalSave}
></Modals>
<Spin size="large" spinning={isSpin}>
<div className="white-panel edu-back-white pt25 pb25 clearfix ">
<div className="white-panel edu-back-white pt20 pb20 clearfix ">
<li className={type=="publicly" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeType("publicly")}>{is_current ? "我":"TA"}的题库</a></li>
<li className={type=="personal" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeType("personal")}>公共题库</a></li>
</div>

@ -0,0 +1,115 @@
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import {Tooltip,Menu} from 'antd';
import {getImageUrl} from 'educoder';
import "./usersInfo.css"
import "../../courses/css/members.css"
import "../../courses/css/Courses.css"
import banner from '../../../images/account/infobanner.png'
class InfosBanner extends Component{
constructor(props){
super(props);
}
render(){
let {
data ,
id,
login,
moduleName,
current_user,
}=this.props;
let is_current=this.props.is_current;
let {username}= this.props.match.params;
let {pathname}=this.props.location;
moduleName=pathname.split("/")[3];
return(
<div className="bannerPanel mb60">
<div className="educontent">
<div className="clearfix color-white mb25">
<p className="myPhoto mr20 fl"><img alt="头像" src={data && `${getImageUrl('images/'+data.avatar_url)}`}/></p>
<div className="fl">
<p className="clearfix mt20">
<span className="username task-hide" style={{"maxWidth":'370px'}}>{data && data.name}</span>
{
data && is_current == false && data.identity =="学生" ? "" :
<span className="userpost"><label>{data && data.identity}</label></span>
}
</p>
<p className="mt15">
<Tooltip placement='bottom' title={ data && data.professional_certification ?"已职业认证":"未职业认证"}>
<i className={ data && data.professional_certification ? "iconfont icon-shenfenzhenghaomaguizheng font-18 user-colorgrey-green mr20 ml2":"iconfont icon-shenfenzhenghaomaguizheng font-18 user-colorgrey-B8 mr20 ml2"}></i>
</Tooltip>
<Tooltip placement='bottom' title={ data && data.authentication ?"已实名认证":"未实名认证"}>
<i className={ data && data.authentication ? "iconfont icon-renzhengshangjia font-18 user-colorgrey-green":"iconfont icon-renzhengshangjia font-18 user-colorgrey-B8"}></i>
</Tooltip>
</p>
</div>
<div className="fr">
<div class="fl headtab mt20">
<span>{is_current ? "我":"TA"}的经验值</span>
<a style={{"cursor":"default"}}>{data && data.experience}</a>
</div>
<div class="fl headtab mt20 pr leftTransform pl20">
<span>{is_current ? "我":"TA"}的金币</span>
<a style={{"cursor":"default"}}>{data && data.grade}</a>
</div>
{
is_current ?
<span className="fl mt35 ml60">
{
data && data.attendance_signed ?
<span className="user_default_btn user_grey_btn font-18">已签到</span>
:
<a herf="javascript:void(0);" onClick={this.props.signFor} className="user_default_btn user_yellow_btn fl font-18">签到</a>
}
</span>
:
<span className="fl mt35 ml60">
<a href={`/messages/${login}/message_detail?target_ids=${id}`} className="user_default_btn user_yellow_btn fl font-18">私信</a>
</span>
}
</div>
</div>
<div className="userNav">
<li className={`${moduleName == 'courses' ||moduleName == undefined ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'courses'})}
to={`/users/${username}/courses`}>翻转课堂</Link>
</li>
<li className={`${moduleName == 'shixuns' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'shixuns'})}
to={`/users/${username}/shixuns`}>开发社区</Link>
</li>
<li className={`${moduleName == 'paths' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'paths'})}
to={`/users/${username}/paths`}>实践课程</Link>
</li>
<li className={`${moduleName == 'projects' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'projects'})}
to={`/users/${username}/projects`}>项目</Link>
</li>
<li className={`${moduleName == 'package' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'package'})}
to={`/users/${username}/package`}>众包</Link>
</li>
{((is_current && current_user && current_user.is_teacher ) || current_user && current_user.admin)
&& <li className={`${moduleName == 'videoes' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'videoes'})}
to={`/users/${username}/videoes`}>视频</Link>
</li>}
</div>
</div>
</div>
)
}
}
export default InfosBanner;

@ -107,7 +107,7 @@ class InfosCourse extends Component{
return(
<div className="educontent">
<Spin size="large" spinning={isSpin}>
<div className="white-panel edu-back-white pt25 pb25 clearfix ">
<div className="white-panel edu-back-white pt20 pb20 clearfix ">
<li className={category ? "" : "active"}><a href="javascript:void(0)" onClick={()=>this.changeCategory()}>全部</a></li>
<li className={category=="manage" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeCategory("manage")}>{is_current ? "我":"TA"}管理的</a></li>
<li className={category=="study" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeCategory("study")}>{is_current ? "我":"TA"}学习的</a></li>
@ -126,7 +126,7 @@ class InfosCourse extends Component{
</p>
<div className="square-list clearfix">
{
page == 1 && is_current &&
page == 1 && is_current && !category &&
this.props.current_user && this.props.current_user.user_identity != "学生" ? <Create href={"/courses/new"} name={"新建课堂"} index="1"></Create> : ""
}
{

@ -0,0 +1,79 @@
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import { SnackbarHOC } from 'educoder';
import { TPMIndexHOC } from '../../tpm/TPMIndexHOC';
import { CNotificationHOC } from '../../courses/common/CNotificationHOC'
import Loadable from 'react-loadable';
import Loading from '../../../Loading';
const UsersInfo = Loadable({
loader: () => import('./Infos'),
loading: Loading,
})
const VideoUploadList = Loadable({
loader: () => import('./video/VideoUploadList'),
loading: Loading,
})
const VideoPublishSuccess = Loadable({
loader: () => import('./video/VideoPublishSuccess'),
loading: Loading,
})
const $ = window.$;
class InfosIndex extends Component{
constructor(props){
super(props);
this.state={
data:undefined,
}
}
componentDidMount =()=>{
}
//判断是否看的是当前用户的个人主页
componentDidUpdate =(prevProps)=> {
}
render(){
let {
data ,
}=this.state;
return(
<Switch {...this.props}>
{/* --------------------------------------------------------------------- */}
{/* 视频发布 */}
<Route exact path="/users/:username/videoes/upload"
render={
(props) => (<VideoUploadList {...this.props} {...props} {...this.state} />)
}
></Route>
<Route exact path="/users/:username/videoes/success"
render={
(props) => (<VideoPublishSuccess {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/users/:username"
render={
(props) => (<UsersInfo {...this.props} {...props} {...this.state} />)
}
></Route>
</Switch>
)
}
}
export default CNotificationHOC() ( SnackbarHOC() ( TPMIndexHOC(InfosIndex) ));

@ -168,7 +168,7 @@ class InfosPackage extends Component{
modalSave={this.state.ModalSave}
/>
<Spin size="large" spinning={isSpin}>
<div className="white-panel edu-back-white pt25 pb25 clearfix ">
<div className="white-panel edu-back-white pt20 pb20 clearfix ">
<li className={category ? "" : "active"}><a onClick={()=>this.changeCategory()}>全部</a></li>
<li className={category=="manage" ? "active" : ""}><a onClick={()=>this.changeCategory("manage")}>{is_current ? "我":"TA"}管理的</a></li>
<li className={category=="bidden" ? "active" : ""}><a onClick={()=>this.changeCategory("bidden")}>{is_current ? "我":"TA"}参与的</a></li>

@ -120,7 +120,7 @@ class InfosPath extends Component{
return(
<div className="educontent">
<Spin size="large" spinning={isSpin}>
<div className="white-panel edu-back-white pt25 pb25 clearfix ">
<div className="white-panel edu-back-white pt20 pb20 clearfix ">
<li className={category ? "" : "active"}><a href="javascript:void(0)" onClick={()=>this.changeCategory()}>全部</a></li>
<li className={category=="manage" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeCategory("manage")}>{is_current ? "我":"TA"}管理的</a></li>
<li className={category=="study" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeCategory("study")}>{is_current ? "我":"TA"}学习的</a></li>
@ -148,7 +148,7 @@ class InfosPath extends Component{
</div>
<div className="square-list clearfix">
{
page == 1 && is_current &&
page == 1 && is_current && !category &&
this.props.current_user && this.props.current_user.user_identity != "学生" ? <Create href={"/paths/new"} name={"新建实践课程"} index="3"></Create>:""
}
{
@ -159,7 +159,9 @@ class InfosPath extends Component{
return(
<div className="square-Item" onClick={()=>this.turnToCourses(`/paths/${item.id}`)}>
{
item.tag && <div className="tag-green"><span className="tag-name">{item.tag}</span><img src={setImagesUrl("images/educoder/tag2.png")} className="fl"/></div>
item.tag && <div className="tag-green"><span className="tag-name">{item.tag}</span>
{/*<img src={setImagesUrl("images/educoder/tag2.png")} className="fl"/>*/}
</div>
}
<a href="javascript:void(0)" className="square-img"><img alt="Subject12" src={getImageUrl(`${item.image_url}`)}/></a>
<div className="square-main">

@ -102,7 +102,7 @@ class InfosProject extends Component{
return(
<div className="educontent">
<Spin size="large" spinning={isSpin}>
<div className="white-panel edu-back-white pt25 pb25 clearfix ">
<div className="white-panel edu-back-white pt20 pb20 clearfix ">
<li className={category ? "" : "active"}><a href="javascript:void(0)" onClick={()=>this.changeCategory()}>全部</a></li>
<li className={category=="manage" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeCategory("manage")}>{is_current ? "我":"TA"}管理的</a></li>
<li className={category=="study" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeCategory("study")}>{is_current ? "我":"TA"}学习的</a></li>
@ -121,7 +121,7 @@ class InfosProject extends Component{
</p>
<div className="square-list clearfix">
{
page == 1 && is_current && this.props.current_user && this.props.current_user.user_identity != "学生" ?
page == 1 && is_current && this.props.current_user && !category && this.props.current_user.user_identity != "学生" ?
<Create href={`${this.props.Headertop && this.props.Headertop.old_url}/projects/new`} name={"新建项目"} index="4"></Create>:""
}
{

@ -120,7 +120,7 @@ class InfosShixun extends Component{
return(
<div className="educontent">
<Spin size="large" spinning={isSpin}>
<div className="white-panel edu-back-white pt25 pb25 clearfix ">
<div className="white-panel edu-back-white pt20 pb20 clearfix ">
<li className={category ? "" : "active"}><a href="javascript:void(0)" onClick={()=>this.changeCategory()}>全部</a></li>
<li className={category=="manage" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeCategory("manage")}>{is_current ? "我":"TA"}管理的</a></li>
<li className={category=="study" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeCategory("study")}>{is_current ? "我":"TA"}学习的</a></li>
@ -157,7 +157,7 @@ class InfosShixun extends Component{
</div>
<div className="square-list clearfix">
{
page == 1 && is_current && this.props.current_user && this.props.current_user.user_identity != "学生" ?
page == 1 && is_current && !category && this.props.current_user && this.props.current_user.user_identity != "学生" ?
<Create href={"/shixuns/new"} name={"新建实训"} index="2"></Create>:""
}
{
@ -168,7 +168,9 @@ class InfosShixun extends Component{
return(
<div className="square-Item" onClick={()=>this.turnToCourses(`/shixuns/${item.identifier}/challenges`)}>
{
item.tag && <div className="tag-green"><span className="tag-name">{item.tag}</span><img className="fl" src={setImagesUrl("images/educoder/tag2.png")}/></div>
item.tag && <div className="tag-green"><span className="tag-name">{item.tag}</span>
{/*<img className="fl" src={setImagesUrl("images/educoder/tag2.png")}/>*/}
</div>
}
<a href="javascript:void(0)" className="square-img">
<img src={setImagesUrl(`${item.image_url}`)}/>

@ -0,0 +1,190 @@
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import {Tooltip,Menu} from 'antd';
import {getImageUrl} from 'educoder';
import "./usersInfo.css"
import "../../courses/css/members.css"
import "../../courses/css/Courses.css"
class banner_out extends Component{
constructor(props){
super(props);
}
render(){
let {
data ,
is_current,
is_edit,
sign,
type,
followed,
id,
login,
moduleName,
next_gold
}=this.props;
let {username}= this.props.match.params;
return(
<div className="user-main-half">
<div className="user-headImg"></div>
<div className="user-headCon">
<div className="pr" style={{"min-height": "465px"}}>
<div className="educontent pt80 clearfix edu-txt-center">
<div className="inline">
<div className="fl headtab">
<span>{is_current ? "我":"TA"}的经验值</span>
<a style={{ cursor: 'default' }}
// href={`${this.props.Headertop && this.props.Headertop.old_url}/users/${username}/user_experience`}
>{data && data.experience}</a>
</div>
<em className="v-h-line fl"></em>
<div className="fl headtab">
<span>{is_current ? "我":"TA"}的金币</span>
<a style={{ cursor: 'default' }}
// href={`${this.props.Headertop && this.props.Headertop.old_url}/users/${username}/user_grade`}
id="user_code">{data && data.grade}</a>
</div>
<div className="headphoto mt14">
<img alt="头像" id="user_avatar_show" nhname="avatar_image" src={data && `${getImageUrl('images/'+data.avatar_url)}`}/>
</div>
<div className="fl headtab">
<span>{is_current ? "我":"TA"}的粉丝</span>
<a style={{ cursor: 'default' }}
// href={`${this.props.Headertop && this.props.Headertop.old_url}/users/${username}/user_fanslist`}
id="user_h_fan_count">{data && data.fan_count}</a>
</div>
<em className="v-h-line fl"></em>
<div className="fl headtab">
<span>{is_current ? "我":"TA"}的关注</span>
<a style={{ cursor: 'default' }}
// href={`${this.props.Headertop && this.props.Headertop.old_url}/users/${username}/user_watchlist`}
>{data && data.follow_count}</a>
</div>
<span className="clearfix"></span>
<span className="myName">{data && data.name}</span>
</div>
</div>
<div className="educontent mt10 clearfix edu-txt-center">
<div className="inline">
{
data && is_current == false && data.identity =="学生" ? "" : <span className="mypost fl mr10">{data && data.identity}</span>
}
<a
// href={is_current ? `${this.props.Headertop && this.props.Headertop.old_url}/account/authentication` :"javascript:void(0)"}
// target="_blank"
className={is_current ? "ringauto fl" :"ringauto fl cdefault"}>
<Tooltip placement='bottom' title={ data && data.authentication ?"已实名认证":"未实名认证"}>
<i className={ data && data.authentication ? "iconfont icon-shenfenrenzheng font-13 color-blue":"iconfont icon-shenfenrenzheng font-13 color-grey-9"}></i>
</Tooltip>
</a>
<a
// href={is_current ? `${this.props.Headertop && this.props.Headertop.old_url}/account/professional_certification` :"javascript:void(0)"}
// target="_blank"
className={is_current ? "ringauto fl" :"ringauto fl cdefault"}>
<Tooltip placement='bottom' title={ data && data.professional_certification ?"已职业认证":"未职业认证"}>
<i className={ data && data.professional_certification ? "iconfont icon-zhiyerenzheng font-13 color-blue":"iconfont icon-zhiyerenzheng font-13 color-grey-9"}></i>
</Tooltip>
</a>
<a
// href={is_current ? `${this.props.Headertop && this.props.Headertop.old_url}/account/change_or_bind?type=phone` :"javascript:void(0)"}
// target="_blank"
className={is_current ? "ringauto fl" :"ringauto fl cdefault"}>
<Tooltip placement='bottom' title={ data && data.phone_binded ?"已手机认证":"未手机认证"}>
<i className={ data && data.phone_binded ? "iconfont icon-shoujirenzheng font-13 color-blue":"iconfont icon-shoujirenzheng font-13 color-grey-9"}></i>
</Tooltip>
</a>
<a
// href={is_current ? `${this.props.Headertop && this.props.Headertop.old_url}/my/account` :"javascript:void(0)"}
// target="_blank"
className={is_current ? "ringauto fl" :"ringauto fl cdefault"}>
<Tooltip placement='bottom' title={ data && data.email_binded ?"已邮箱认证":"未邮箱认证"}>
<i className={ data && data.email_binded ? "iconfont icon-youxiangrenzheng font-13 color-blue":"iconfont icon-youxiangrenzheng font-13 color-grey-9"}></i>
</Tooltip>
</a>
{/* <!--学院管理员身份--> */}
{
data && data.college_identifier &&
<a
// href={`${this.props.Headertop && this.props.Headertop.old_url}/colleges/${data.college_identifier}/statistics`} target="_blank"
className={is_current ? "ringauto fl" :"ringauto fl cdefault"}>
<Tooltip placement='bottom' title="学院管理员">
<i className="iconfont icon-chengyuanguanli font-12 color-blue" data-tip-down="学院管理员"></i>
</Tooltip>
</a>
}
</div>
</div>
<div className="mt15 educontent clearfix edu-txt-center">
<p className="mb20" style={{"height": "28px"}}>
{
is_edit && is_current ?
<input type="text" id="mysign" class="mysign-input" placeholder="请输入您的个性签名" style={{height:"20px"}} value={sign} onInput={this.inputSign} onBlur={this.savemysign}/>
:
is_current ?
<a className="mysign-span" onClick={this.editmysign} style={{"display": "block"}}>{sign || "这家伙很懒,什么都没留下~"}</a>
:
<span className="mysign-span" style={{"display": "block","cursor":"default"}}>{sign || "这家伙很懒,什么都没留下~"}</span>
}
</p>
{
is_current ?
<div className="inline">
{
data && data.attendance_signed ?
<React.Fragment>
<span className="user_default_btn user_grey_btn mb5">已签到</span>
<p id="attendance_notice" className="none font-12 color-grey-6" style={{"display":"block"}}>明日签到&nbsp;<font className="color-orange">+{next_gold}</font>&nbsp;</p>
</React.Fragment>
:
<a herf="javascript:void(0);" onClick={this.props.signFor} id="attendance" className="user_default_btn user_orange_btn fl mb15">签到</a>
// <a herf="javascript:void(0);" onClick={this.trialapplications} id="authentication_apply" className="user_default_btn user_private_btn fl ml15">试用申请</a>
}
</div>
:
<div className="inline">
<a href="javascript:void(0);" onClick={this.props.followPerson} className="user_default_btn user_watch_btn user_private_btn fl mr20">{followed ? "取消关注":"关注"}</a>
<a href={`${this.props.Headertop && this.props.Headertop.old_url}/messages/${login}/message_detail?target_ids=${id}`} className="user_default_btn user_private_btn fl">私信</a>
</div>
}
</div>
<div className="edu-txt-center navInfo">
<div className="inline">
<li className={`${moduleName == 'courses' ||moduleName == undefined ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'courses'})}
to={`/users/${username}/courses`}>课堂</Link>
</li>
<li className={`${moduleName == 'shixuns' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'shixuns'})}
to={`/users/${username}/shixuns`}>实训</Link>
</li>
<li className={`${moduleName == 'paths' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'paths'})}
to={`/users/${username}/paths`}>实践课程</Link>
</li>
<li className={`${moduleName == 'projects' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'projects'})}
to={`/users/${username}/projects`}>项目</Link>
</li>
<li className={`${moduleName == 'package' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'package'})}
to={`/users/${username}/package`}>众包</Link>
</li>
{/*{ data && data.identity!="学生" && <li> <a href={`${this.props.Headertop && this.props.Headertop.old_url}/users/${username}?type=m_bank`}>题库</a></li>}*/}
</div>
</div>
</div>
</div>
</div>
)
}
}
export default banner_out;

@ -0,0 +1,96 @@
import React, { useState, useEffect, useContext, useRef, memo } from 'react';
import {Link} from 'react-router-dom';
import { Icon } from 'antd'
import { getUrl2, isDev, ThemeContext } from 'educoder'
import axios from 'axios'
function CRoundSelect (props) {
const [open, setOpen] = useState(false)
const theme = useContext(ThemeContext);
const { category, changeCategory, categories, right, width, items,
sortKey, onSortChange } = props;
const username = props.match.params.username
useEffect(() => {
}, [])
function onToggleOpen(over) {
if (over) {
console.log('over')
setOpen(true)
} else {
console.log('out')
setOpen(false)
}
}
function findIndexByKey(key) {
let _index = -1
items && items.some((item, index) => {
if (item.key == key) {
_index = index
return true;
}
})
return _index
}
function _onSortChange(key, index) {
if (index == 0) {
return;
}
setOpen(false)
onSortChange(key, index)
}
let index = findIndexByKey(sortKey)
return (
<React.Fragment>
<div className="" style={{position: 'relative', lineHeight: '24px'}}>
{/* onMouseOut={onToggleOpen} */}
<div className="trigger" onMouseOver={() => onToggleOpen(true)} >
<style>{`
.trigger, .droplist {
padding: 0px 6px;
border: 1px solid ${theme.foreground_select};
color: ${theme.foreground_select};
border-radius: 6px;
}
.trigger {
width: ${width || 'fit-content'};
cursor: pointer;
}
.droplist {
width: ${width || 'fit-content'};
position: absolute;
z-index: 2;
top: 0px;
background: #fff;
cursor: pointer;
}
`}</style>
<div className="currentItem">
{items[index].name} <Icon type="down" />
</div>
</div>
{true && <ul className="droplist" onMouseLeave={() => onToggleOpen(false)}
style={{display: open ? 'block' : 'none'}}
>
{items.map((item, index) =>
<li key={item.key} className=""
onClick={() => _onSortChange(item.key, index)}>{item.name}</li>
)}
{/* <li className="">AAAAAAAA</li>
<li className="">BBBBBBB</li>
<li className="">CCCCCCC</li> */}
</ul> }
</div>
</React.Fragment>
)
}
export default CRoundSelect

@ -0,0 +1,56 @@
import React, { useState, useEffect, useContext, useRef, memo } from 'react';
import {Link} from 'react-router-dom';
import { getUrl2, isDev, ThemeContext } from 'educoder'
import { Modal } from 'antd'
function HeadlessModal (props) {
// const [ visible, setVisible ] = useState(false)
const theme = useContext(ThemeContext);
const { category, visible, setVisible, className, width } = props;
useEffect(() => {
}, [])
return (
<Modal
visible={visible}
className={`headless ${className}`}
title={null}
footer={null}
width={width}
>
<style>{`
.headless .ant-modal-close {
display:none;
}
.headless .ant-modal-body {
padding: 0px;
}
.headless .closeBtn {
position: absolute;
color: ${theme.foreground_select};
top: -8px;
right: -10px;
font-size: 24px !important;
background: #fff;
width: 14px;
height: 8px;
margin-right: 0px;
}
.headless .icon-htmal5icon19:before {
left: -4px;
position: absolute;
top: -13px;
}
`}</style>
<i className="iconfont icon-htmal5icon19 closeBtn" onClick={ () => setVisible(false) }></i>
{props.children}
</Modal>
)
}
export default HeadlessModal

@ -0,0 +1,35 @@
import React, { useState, useEffect, useContext, useRef, memo } from 'react';
import {Link} from 'react-router-dom';
import { getUrl2, isDev, ThemeContext } from 'educoder'
import axios from 'axios'
function InfoTab (props) {
const theme = useContext(ThemeContext);
const { category, changeCategory, categories, right } = props;
const username = props.match.params.username
useEffect(() => {
}, [])
return (
<div className="white-panel edu-back-white pt20 pb20 clearfix ">
{categories && categories.map(item => {
return (
<li key={item.key} className={category == item.key ? "active" : ''}><a href="javascript:void(0)" onClick={()=>changeCategory(item.key)}>{item.name}</a></li>
)
})}
{/* <li className={category ? "" : "active"}><a href="javascript:void(0)" onClick={()=>this.changeCategory()}></a></li>
<li className={category=="manage" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeCategory("manage")}>{is_current ? "我":"TA"}管理的</a></li>
<li className={category=="study" ? "active" : ""}><a href="javascript:void(0)" onClick={()=>this.changeCategory("study")}>{is_current ? "我":"TA"}学习的</a></li> */}
<div className="fr">
{right}
</div>
</div>
)
}
export default InfoTab

@ -120,3 +120,110 @@
display: block;
font-size: 20px;
}
/* 个人主页头部改版 */
.bannerPanel{
width:100%;
top:0px;
left:0px;
height:186px;
padding-top:25px;
box-sizing: border-box;
background: url('../../../images/account/infobanner.png') no-repeat top center;
background-color: #0d4473
}
.username{
font-size:26px;
margin-right:15px;
height: 30px;
line-height: 30px;
float: left;
color:#fff;
}
.myPhoto img{
border-radius: 50%;
width:100%;
height:100%
}
.myPhoto{
border-radius: 50%;
border:2px solid #fff;
width:110px;
height:110px;
}
.userpost{
padding:0px 10px;
border:1px solid #fff;
border-radius: 2px;
color:#fff!important;
display: inline;
margin-top: 6px;
float:left;
}
.userpost label{
display:block;
height: 18px;
line-height:18px;
}
.user-colorgrey-B8{color:#B8B8B8}
.user-colorgrey-green{color:#7ED321}
.user_yellow_btn {
color: #fff!important;
background-color: #FF8E02;
}
.headtab span {
color: #e8e8e8;
font-size: 14px;
}
.headtab span, .headtab a {
display: block;
width: 100%;
text-align: center;
}
.headtab {
width: 140px;
height: 70px;
text-align: center;
}
.leftTransform:before{
position: absolute;
content:'';
left:0px;
top:6px;
width:1px;
height:84%;
background: #fff;
transform: rotate(18deg);
}
.userNav{
height: 54px;
line-height: 54px;
background: #fff;
box-shadow:0px 9px 16px -1px #e6e6e6;
border-radius:7px;
padding:0px 10px;
}
.userNav li{
display: inline-block;
padding:0px 30px;
box-sizing: border-box
}
.userNav li a{
color:#333333;
font-size: 16px;
display: block
}
.userNav li.active a{
position: relative;
color:#4CACFF;
}
.userNav li.active a:after{
position: absolute;
bottom: 0px;
height: 4px;
width:100%;
content: '';
left:0px;
background: #4CACFF;
}

@ -0,0 +1,178 @@
import { getUrl2, isDev } from 'educoder'
import axios from 'axios'
let _url_origin = getUrl2()
let _path = isDev() ? 'public' : 'build'
let _testHost = '' ; // 'http://192.168.2.63:3001/api' ; // '' ;
let login = 'innov'
let uploader;
let $ = window.$
function loadLib(callback) {
$.getScript(
`${_url_origin}/react/${_path}/js/aliyun-upload/lib/es6-promise.min.js`,
(data, textStatus, jqxhr) => {
$.getScript(
`${_url_origin}/react/${_path}/js/aliyun-upload/lib/aliyun-oss-sdk-5.3.1.min.js`,
(data, textStatus, jqxhr) => {
$.getScript(
`${_url_origin}/react/${_path}/js/aliyun-upload/aliyun-upload-sdk-1.5.0.min.js`,
(data, textStatus, jqxhr) => {
callback && callback()
});
});
});
}
function createUploader(options) {
if (window.AliyunUpload && window.AliyunUpload.Vod) {
doCreateUploader(options)
} else {
loadLib(() => {
doCreateUploader(options)
})
}
}
function doCreateUploader (options) {
uploader = new window.AliyunUpload.Vod({
timeout: $('#timeout').val() || 60000,
partSize: $('#partSize').val() || 1048576,
parallel: $('#parallel').val() || 5,
retryCount: $('#retryCount').val() || 3,
retryDuration: $('#retryDuration').val() || 2,
region: $('#region').val() || 'ap-southeast-1',
userId: $('#userId').val() || 1829848226361863, // 1303984639806000,
// 添加文件成功
addFileSuccess: function (uploadInfo) {
console.log("addFileSuccess: " + uploadInfo.file.name)
uploader.startUpload()
},
// 开始上传
onUploadstarted: function (uploadInfo) {
// 如果是 UploadAuth 上传方式, 需要调用 uploader.setUploadAuthAndAddress 方法
// 如果是 UploadAuth 上传方式, 需要根据 uploadInfo.videoId是否有值调用点播的不同接口获取uploadauth和uploadAddress
// 如果 uploadInfo.videoId 有值,调用刷新视频上传凭证接口,否则调用创建视频上传凭证接口
// 注意: 这里是测试 demo 所以直接调用了获取 UploadAuth 的测试接口, 用户在使用时需要判断 uploadInfo.videoId 存在与否从而调用 openApi
// 如果 uploadInfo.videoId 存在, 调用 刷新视频上传凭证接口(https://help.aliyun.com/document_detail/55408.html)
// 如果 uploadInfo.videoId 不存在,调用 获取视频上传地址和凭证接口(https://help.aliyun.com/document_detail/55407.html)
const fileName = uploadInfo.file.name
if (!uploadInfo.videoId) {
var createUrl = `${_testHost}/users/${login}/video_auths.json?debug=true`
axios.post(createUrl, {
title: fileName,
file_name: fileName
}).then((response) => {
// if (response.data.status == )
const data = response.data.data
var uploadAuth = data.UploadAuth
var uploadAddress = data.UploadAddress
var videoId = data.VideoId
uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress, videoId)
options.addFileSuccess && options.addFileSuccess(uploadInfo)
}).catch((error) => {
// 删除当前出错的,并执行下一个任务
uploader.deleteFile(uploader._curIndex)
uploader.nextUpload()
console.log(error)
})
$('#status').text('文件开始上传...')
console.log("onUploadStarted:" + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object)
} else {
// 如果videoId有值根据videoId刷新上传凭证
var refreshUrl = `${_testHost}/users/${login}/video_auths.json?debug=true`
axios.put(refreshUrl, {
video_id: uploadInfo.videoId,
}).then((response) => {
if (response.data.status == -1) {
options.onUploadError && options.onUploadError(uploadInfo)
return;
}
const data = response.data.data
var uploadAuth = data.UploadAuth
var uploadAddress = data.UploadAddress
var videoId = data.VideoId
uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress, videoId)
options.addFileSuccess && options.addFileSuccess(uploadInfo)
}).catch((error) => {
uploader.deleteFile(uploader._curIndex)
uploader.nextUpload()
console.log(error)
})
}
},
// 文件上传成功
onUploadSucceed: function (uploadInfo) {
options.onUploadSucceed && options.onUploadSucceed(uploadInfo)
console.log("onUploadSucceed: " + uploadInfo.file.name + ", endpoint:" + uploadInfo.endpoint + ", bucket:" + uploadInfo.bucket + ", object:" + uploadInfo.object)
$('#status').text('文件上传成功!')
},
// 文件上传失败
onUploadFailed: function (uploadInfo, code, message) {
options.onUploadFailed && options.onUploadFailed(uploadInfo)
console.log("onUploadFailed: file:" + uploadInfo.file.name + ",code:" + code + ", message:" + message)
$('#status').text('文件上传失败!')
},
// 取消文件上传
onUploadCanceled: function (uploadInfo, code, message) {
console.log("Canceled file: " + uploadInfo.file.name + ", code: " + code + ", message:" + message)
$('#status').text('文件上传已暂停!')
},
// 文件上传进度,单位:字节, 可以在这个函数中拿到上传进度并显示在页面上
onUploadProgress: function (uploadInfo, totalSize, progress) {
options.onUploadProgress && options.onUploadProgress(uploadInfo, totalSize, progress)
console.log("onUploadProgress:file:" + uploadInfo.file.name + ", fileSize:" + totalSize + ", percent:" + Math.ceil(progress * 100) + "%")
var progressPercent = Math.ceil(progress * 100)
$('#auth-progress').text(progressPercent)
$('#status').text('文件上传中...')
},
// 上传凭证超时
onUploadTokenExpired: function (uploadInfo) {
// 上传大文件超时, 如果是上传方式一即根据 UploadAuth 上传时
// 需要根据 uploadInfo.videoId 调用刷新视频上传凭证接口(https://help.aliyun.com/document_detail/55408.html)重新获取 UploadAuth
// 然后调用 resumeUploadWithAuth 方法, 这里是测试接口, 所以我直接获取了 UploadAuth
$('#status').text('文件上传超时!')
var refreshUrl = `${_testHost}/users/${login}/video_auths.json?debug=true`
axios.put(refreshUrl, {
video_id: uploadInfo.videoId,
}).then((response) => {
const data = response.data.data
var uploadAuth = data.UploadAuth
uploader.resumeUploadWithAuth(uploadAuth)
}).catch((error) => {
console.log(error)
})
},
// 全部文件上传结束
onUploadEnd: function (uploadInfo) {
options.onUploadEnd && options.onUploadEnd(uploadInfo)
$('#status').text('文件上传完毕!')
console.log("onUploadEnd: uploaded all the files")
}
})
if (options.gotUploader) {
options.gotUploader(uploader)
}
}
export function getUploader (_login, options) {
_login && (login = _login)
if (!uploader || options.create == true) {
createUploader(options)
}
}

@ -0,0 +1,86 @@
import React, { useState, useEffect, useContext, useRef, memo } from 'react';
import { Progress, Input, Tooltip, Form } from 'antd'
import { getUrl2, isDev, CBreadcrumb, ActionBtn, ThemeContext, ModalWrapper } from 'educoder'
import axios from 'axios'
const MAX_LENGTH = 30
function EditVideoModal (props) {
const modalEl = useRef(null);
const theme = useContext(ThemeContext);
const { history, videoId, cover_url, title, created_at, isReview, onEditVideo, visible, setVisible,
form, editSuccess } = props;
const getFieldDecorator = form.getFieldDecorator
const username = props.match.params.username
const _title = form.getFieldsValue().title;
function toList() {
history.push(`/users/${username}/videoes`)
}
function toUpload() {
history.push(`/users/${username}/videoes/upload`)
}
function onOk() {
form.validateFieldsAndScroll((err, values) => {
if (!err) {
const url = `/users/${username}/videos/${videoId}.json`
axios.put(url, {
title: _title
}).then((response) => {
if (response.data) {
onCancel()
editSuccess()
}
}).catch((e) => {
})
} else {
// $("html").animate({ scrollTop: $('html').scrollTop() - 100 })
}
})
// setVisible(false)
}
function onCancel() {
setVisible(false)
}
useEffect(() => {
modalEl.current.setVisible(visible)
}, [visible])
useEffect(() => {
visible && form.setFieldsValue({
title,
})
}, [visible])
return (
<ModalWrapper
ref={modalEl}
width="600px"
title={`视频编辑`}
{ ...props }
onOk={onOk}
onCancel={onCancel}
className="editVideoModal"
>
<Form.Item
label="视频标题"
className="title formItemInline"
>
{getFieldDecorator('title', {
rules: [{
required: true, message: '请输入标题',
}, {
max: MAX_LENGTH, message: '最大限制为30个字符',
}],
})(
<Input placeholder="" className="titleInput" maxLength={MAX_LENGTH}
addonAfter={String(_title ? `${String(_title.length)}/${MAX_LENGTH}` : 0)} />
)}
</Form.Item>
</ModalWrapper>
)
}
const WrappedEditVideoModal = Form.create({ name: 'editVideoModal' })(EditVideoModal);
export default WrappedEditVideoModal

@ -0,0 +1,116 @@
.itemWrap {
display: flex;
flex-wrap: wrap;
}
/* item */
.videoItem {
width: 280px;
margin-right: 26px;
margin-bottom: 26px;
position: relative;
}
.videoItem:nth-child(4n+0) {
margin-right: 0px;
}
.videoItem img.cover {
width: 100%;
border-radius: 6px 6px 0px 0px;
height: 158px;
}
.nItem.videoItem:hover .mask {
display: block;
top: 0px;
width: 100%;
height: 158px;
cursor: pointer;
}
.nItem.videoItem:hover .playWrap {
display: inline-block;
}
.nItem .mask {
border-radius: 6px 6px 0px 0px;
display: none;
text-align: center;
position: absolute;
background: #000;
opacity: 0.5;
}
.videoItem .playWrap {
display: none;
width: 100%;
text-align: center;
height: 70px;
position: absolute;
top: 0px;
left: 0px;
}
.videoItem img.play {
margin-top: 20%;
position: relative;
z-index: 99;
}
.videoItem .square-main {
padding: 9px 8px;
background: #fff;
border-radius: 0px 0px 6px 6px;
}
.videoItem .square-main .title{
max-width: 256px;
line-height: 18px;
}
.videoInReviewItem .square-main {
background: #EAEAEA;
}
.videoItem .time {
color: #A0A0A0;
}
.videoItem .square-main .buttonRow {
justify-content: space-between;
line-height: 15px;
}
.nItem.videoItem:hover .square-main {
color: #fff;
background: #333;
}
/* 预览弹框 */
.showVideoModal .ant-modal-body {
display: flex;
flex-direction: column;
}
.showVideoModal video{
width: 800px;
height: 450px;
}
.showVideoModal .copyLine {
justify-content: space-between;
padding: 9px;
background: #000000;
width: 800px;
}
.showVideoModal .copyLine input {
color: #707070;
background-color: #000 !important;
border-color: #707070;
margin-right: 12px;
}
.showVideoModal .copyLine a {
flex: 0 0 106px;
}
.toolbarRow {
justify-content: space-between;
padding: 0 8px;
margin-bottom: 6px;
}
/* 跳转按钮 */
.infoVideo .toUploadBtn {
height: 48px;
margin-right: 20px;
}

@ -0,0 +1,348 @@
import React, { useState, useEffect, useContext, useRef, memo } from 'react';
import {Link} from 'react-router-dom';
import { Pagination, Input, Button } from 'antd'
import { getUrl2, isDev, ThemeContext, ActionBtn, NoneData } from 'educoder'
import axios from 'axios'
import VideoInReviewItem from './VideoInReviewItem'
import EditVideoModal from './EditVideoModal'
import './InfosVideo.css'
import InfoTab from '../common/InfoTab'
import HeadlessModal from '../common/HeadlessModal'
import CRoundSelect from '../common/CRoundSelect'
import ClipboardJS from 'clipboard'
function useModal(initValue) {
const [visible, setVisible] = useState(initValue)
return {
visible,
setVisible
}
}
function useCategory(initValue) {
const [category, setCategory] = useState(initValue)
function changeCategory(key) {
setCategory(key)
}
return {
category,
changeCategory
}
}
function usePagination() {
const [page, setPage] = useState(1)
function onPageChange(page) {
setPage(page)
}
return {
current: page,
onChange: onPageChange
}
}
const PAGE_SIZE = 16
const DEFAULT_VIDEO_WIDTH_IN_MD = "90%" // 400
const DEFAULT_VIDEO_HEIGHT_IN_MD = "55%" // 400
let videoId = {};
let _clipboard = null;
const _items=[
{key: 'published_at-desc', name: '最新上传'},
{key: 'published_at-asc', name: '最早上传'},
]
function InfoVideo (props) {
const [videoes, setVideoes] = useState(undefined)
const [reviewVideoes, setReviewVideoes] = useState(undefined)
const [count, setCount] = useState(0)
const [loading, setLoading] = useState(true)
const [sortKey, setSortKey] = useState(_items[0].key)
const editModalObj = useModal(false)
const videoModalObj = useModal(false)
const categoryObj = useCategory('all')
const pageObj = usePagination()
const theme = useContext(ThemeContext);
const editModalEl = useRef(null);
const videoEl = useRef(null);
const { showNotification, history } = props;
const username = props.match.params.username
function toUpload() {
if (props.current_user.admin || (props.current_user.is_teacher && props.checkIfProfessionalCertification())) {
history.push(`/users/${username}/videoes/upload`)
} else {
props.showProfessionalCertificationDialog()
}
}
function fetchVideoes() {
const fetchUrl = `/users/${username}/videos.json`
const sorts = sortKey.split('-')
setLoading(true)
axios.get(fetchUrl, {
params: {
page: pageObj.current,
per_page: PAGE_SIZE,
sort_by: sorts[0],
sort_direction: sorts[1],
//
}
})
.then((response) => {
setLoading(false)
if (response.data.videos) {
setVideoes(response.data.videos)
setCount(response.data.count)
}
}).catch(() => {
})
}
function fetchReviewVideoes() {
const fetchUrl = `/users/${username}/videos/review.json`
setLoading(true)
axios.get(fetchUrl, {
params: {
per_page: 200
}
})
.then((response) => {
setLoading(false)
if (response.data.videos) {
setReviewVideoes(response.data.videos)
setCount(response.data.count)
}
}).catch(() => {
})
}
useEffect(() => {
fetchVideoes()
}, [pageObj.current, sortKey])
useEffect(() => {
if (categoryObj.category == 'all') {
fetchVideoes()
} else {
fetchReviewVideoes()
}
}, [categoryObj.category])
useEffect(() => {
if (videoModalObj.visible == false) {
// 关闭视频
videoEl.current && videoEl.current.pause()
if (_clipboard) {
_clipboard.destroy();
_clipboard = null;
}
} else {
setTimeout(() => {
if (!_clipboard) {
_clipboard = new ClipboardJS('.copybtn');
_clipboard.on('success', (e) => {
showNotification('复制成功')
});
}
}, 200)
}
}, [videoModalObj.visible])
useEffect(() => {
}, [])
function editSuccess() {
fetchVideoes()
}
function onEditVideo(item) {
videoId = {
videoId: item.id,
title: item.title
}
editModalObj.setVisible(true)
// editModalEl.current.toList(true, video);
// this.refs['editVideoModal'].setVisible(true, video);
}
function onMaskClick(item) {
videoId = {
videoId: item.id,
title: item.title,
file_url: item.file_url,
cover_url: item.cover_url
}
videoModalObj.setVisible(true)
}
// TODO use封装
function onSortChange(key, index) {
const _item = _items[index]
_items.splice(index, 1)
_items.unshift(_item)
setSortKey(key)
}
function getCopyText (file_url, cover_url) {
return `<video src="${file_url}" controls="true" controlslist="nodownload" width="${DEFAULT_VIDEO_WIDTH_IN_MD}" height="${DEFAULT_VIDEO_HEIGHT_IN_MD}" poster="${cover_url}">您的浏览器不支持 video 标签。</video>`
}
const _inputValue = getCopyText(videoId.file_url, videoId.cover_url)
return (
<div className="educontent infoVideo">
<EditVideoModal {...props} {...editModalObj}
editSuccess={editSuccess}
{...videoId}
></EditVideoModal>
<HeadlessModal
{...videoModalObj}
className="showVideoModal"
width={800 - 1}
>
<video
ref={videoEl}
src={videoId.file_url} controls="true" controlslist="nodownload">
您的浏览器不支持 video 标签
</video>
<div className="df copyLine">
<Input value={_inputValue}
className="dark"
></Input>
<ActionBtn className="copybtn" data-clipboard-text={_inputValue}>复制视频地址</ActionBtn>
</div>
</HeadlessModal>
<style>{`
/* item */
.videoPublishSuccess .section {
background: #fff;
padding: 16px 20px;
padding-top: 0px;
position: relative;
text-align: center;
color: ${theme.foreground_tip};
}
.videoItem .square-main .buttonRow i {
vertical-align: top;
font-size: 16px;
color: ${theme.foreground_select} !important;
margin-left: 6px;
}
/*
(26 - 24) * 3 / 2
*/
.itemWrap {
margin-left: 3px;
}
.videoItem {
margin-right: 24px;
}
`}</style>
<InfoTab
{...props}
categories={[{
key: 'all',
name: '全部视频'
}, {
key: 'review',
name: '待审核视频'
}]}
{...categoryObj}
right={
<Button type="primary" icon="upload"
onClick={() => { toUpload() }}
className="toUploadBtn"
>
上传视频
</Button>
}
></InfoTab>
<div className="toolbarRow mt20 df">
<span>
<span style={{color: theme.foreground_orange1}}> {count} </span>
个视频
</span>
{categoryObj.category == 'all' && <CRoundSelect {...props}
width={'90px'}
items={_items}
onSortChange={onSortChange}
sortKey={sortKey }
></CRoundSelect>}
</div>
{categoryObj.category == 'all' ?
<div className="itemWrap">
{
videoes == undefined ? '' :
videoes.length ?
videoes.map((item, index) => {
return (<VideoInReviewItem
{...props}
{...item}
key={item.id}
onEditVideo={onEditVideo}
onMaskClick={onMaskClick}
getCopyText={getCopyText}
>
</VideoInReviewItem>)
})
: <NoneData style={{width: '100%'}}></NoneData>
}
</div>
:
<div className="itemWrap">
{
reviewVideoes == undefined ? '' :
reviewVideoes.length ?
reviewVideoes.map((item, index) => {
return (<VideoInReviewItem
{...props}
{...item}
key={item.id}
isReview={true}
>
</VideoInReviewItem>)
})
: <NoneData style={{width: '100%'}}></NoneData>
}
</div>
}
{
categoryObj.category == 'all' && count > PAGE_SIZE &&
<div className="mt30 mb50 edu-txt-center">
<Pagination showQuickJumper total={count} pageSize={PAGE_SIZE}
{...pageObj}
/>
</div>
}
</div>
)
}
export default InfoVideo
/**
<video src="http://outin-396971199eed11e991a100163e1c7426.oss-cn-shanghai.aliyuncs.com/sv/52943d8b-16c8dc2a8ca/52943d8b-16c8dc2a8ca.mp4" controls="true" controlslist="nodownload" width="400">
您的浏览器不支持 video 标签
</video>
*/

@ -0,0 +1,77 @@
import React, { useState, useEffect, useContext, memo } from 'react';
import { Progress, Input, Tooltip } from 'antd'
import { getUrl2, isDev, CBreadcrumb, ActionBtn, ThemeContext } from 'educoder'
import axios from 'axios'
import moment from 'moment'
import playIcon from './images/play.png'
import ClipboardJS from 'clipboard'
/**
cover_url: "http://video.educoder.net/f6ba49c3944b43ee98736898e31b7d88/snapshots/12da3f7df07c499b8f0fc6dc410094e9-00005.jpg"
created_at: "2019-08-12 13:48:26"
file_url: "http://video.educoder.net/sv/4c7eb4-16c845ee09c/4c7eb4-16c845ee09c.mp4"
id: 1
published_at: "2019-08-12 15:38:00"
title: "测试标题"
updated_at: "2019-08-12 17:17:09"
*/
let _clipboard = null;
function VideoInReviewItem (props) {
const theme = useContext(ThemeContext);
const { history, file_url, cover_url, title, created_at, published_at, isReview, id
, onEditVideo, onMaskClick, getCopyText, showNotification } = props;
useEffect(()=> {
if (!isReview) {
_clipboard = new ClipboardJS(`.copybtn_item_${id}`);
_clipboard.on('success', (e) => {
showNotification('复制成功')
});
}
return () => {
_clipboard && _clipboard.destroy();
}
}, [])
const username = props.match.params.username
function toList() {
history.push(`/users/${username}/videoes`)
}
function toUpload() {
history.push(`/users/${username}/videoes/upload`)
}
return (
<div className={`${isReview ? 'videoInReviewItem' : 'nItem'} videoItem`}>
<img className="cover" src={cover_url || "http://video.educoder.net/e7d18970482a46d2a6f0e951b504256c/snapshots/491e113950d74f1dab276097dae287dd-00005.jpg"}
></img>
{!isReview && <div className="mask" onClick={() => onMaskClick(props)}>
</div>}
{!isReview &&
<div className="playWrap" onClick={() => onMaskClick(props)}>
<img className="play" src={playIcon}></img>
</div>
}
<div className="square-main">
<div className="title overflowHidden1"
title={title && title.length > 20 ? title : ''}
>{title}</div>
<div className="df buttonRow">
{/* 2019-09-01 10:00:22 */}
<span className="time">{moment(published_at || created_at).format('YYYY-MM-DD HH:mm:ss')}</span>
{ isReview != true && <div>
<Tooltip title="编辑" placement="bottom">
<i className="icon-bianji1 iconfont" onClick={() => onEditVideo(props)}
style={{ marginTop: '1px', display: 'inline-block'}}
></i>
</Tooltip>
<Tooltip title="复制视频地址" placement="bottom">
<i className={`icon-fuzhi iconfont copybtn_item_${id}`} data-clipboard-text={getCopyText(file_url, cover_url)}></i>
</Tooltip>
</div> }
</div>
</div>
</div>
)
}
export default VideoInReviewItem

@ -0,0 +1,73 @@
import React, { useState, useEffect, useContext, memo } from 'react';
import { Progress, Input } from 'antd'
import { getUrl2, isDev, CBreadcrumb, ActionBtn, ThemeContext } from 'educoder'
import axios from 'axios'
import okIcon from './images/ok_border.png'
function VideoUpload (props) {
const theme = useContext(ThemeContext);
const { history } = props;
const username = props.match.params.username
function toList() {
history.push(`/users/${username}/videoes`)
}
function toUpload() {
history.push(`/users/${username}/videoes/upload`)
}
return (
<div className={`videoPublishSuccess educontent`}>
<CBreadcrumb
className="mb26 mt16"
separator=" > "
items={[
{ to: `/users/${username}/videoes`, name: '视频'},
{ name: '上传'}
]}
></CBreadcrumb>
<style>{`
.videoPublishSuccess .section {
background: #fff;
padding: 72px 20px;
position: relative;
text-align: center;
color: ${theme.foreground_tip};
}
.videoPublishSuccess img.ok {
width: 64px;
margin: 16px;
margin-top: 0px;
}
.videoPublishSuccess .tip {
margin-top: 10px;
margin-bottom: 12px;
}
.videoPublishSuccess .toListBtn {
margin-right: 10px;
}
.videoPublishSuccess .toUploadBtn {
width: 112px;
}
`}</style>
<div className="section">
<div>
<img className="ok" src={okIcon}></img>
{/* <i className="icon-wanchenggouxuan iconfont font-36" style={{color: theme.foreground_select}}></i> */}
</div>
<div className="font-16" style={{ 'line-height': '16px'}}>恭喜</div>
<div className="font-16">提交成功</div>
<div className="tip">平台正在审核您的申请审核结果将以平台消息的形式通知您</div>
<div>
<ActionBtn className="toListBtn" onClick={toList}>查看已上传视频</ActionBtn>
<ActionBtn className="toUploadBtn" onClick={toUpload}>继续上传</ActionBtn>
</div>
</div>
</div>
)
}
export default VideoUpload

@ -0,0 +1,64 @@
import update from 'immutability-helper'
function find(state, action) {
let _index = -1
state.videoes.some((item, index) => {
// 同文件不同名字 fileHash也是一样的
if (item.loaded != 100 && (action.uploadInfo.fileHash == item.fileHash && action.uploadInfo.file.name == item.name)) {
_index = index
return true;
}
})
return _index;
}
export function reducer(state, action) {
switch (action.type) {
case 'addVideo':
const uploadInfo = action.uploadInfo
return {videoes: [...state.videoes, {
name: uploadInfo.file.name,
size: uploadInfo.file.size,
type: uploadInfo.file.type,
fileHash: uploadInfo.fileHash, // "ba1bbc53fdecd9eaaae479fbd9518442"
state: uploadInfo.state, // "Uploading" "Ready" "Success"
videoId: uploadInfo.videoId, // "719b82c875c34ac39f94feb145d25ad2"
loaded: 0,
title: ''
}]};
case 'removeVideo':
return {
videoes: update(state.videoes, {$splice: [[action.index, 1]]})
}
case 'updateProgress':
let _index = find(state, action)
let newVideoes = state.videoes
// 删除先执行
if (_index != -1) {
newVideoes = update(state.videoes, {[_index]: {
loaded: {$set: action.progressPercent},
videoId: {$set: action.uploadInfo.videoId},
// addFileSuccess的时候没有fileHash
fileHash: {$set: action.uploadInfo.fileHash}
}})
}
return {videoes: newVideoes};
case 'updateTitle':
let _upadteIndex = action.index
let newVideoes2 = state.videoes
if (_upadteIndex != -1) {
newVideoes2 = update(state.videoes, {[_upadteIndex]: {
title: {$set: action.title},
}})
}
return {videoes: newVideoes2};
default:
throw new Error();
}
}
export const initialState = {videoes: []};

@ -0,0 +1,52 @@
import React, { useState, useEffect, memo } from 'react';
import { Progress, Input } from 'antd'
import { getUrl2, isDev, CBreadcrumb, ActionBtn } from 'educoder'
import axios from 'axios'
const MAX_LENGTH = 30
/**
name: file.name,
size: file.size,
type: file.type,
fileHash: uploadInfo.fileHash, // "ba1bbc53fdecd9eaaae479fbd9518442"
state: uploadInfo.state, // "Uploading"
videoId: uploadInfo.videoId, // "719b82c875c34ac39f94feb145d25ad2"
loaded: 0
*/
function VideoUpload (props) {
const { className, index, name, loaded, state, cancelUpload, onTitleChange, title } = props;
// const [title, setTitle] = useState('')
const username = props.match.params.username
function titleChange (e) {
onTitleChange(e.target.value, index)
}
return (
<div className={`videoUpload ${className}`}>
<div className="filename">{index+1}. {name}</div>
<div className="progress df">
<Progress percent={loaded}
status={loaded == '100' ? "" : 'active'}
/>
<div className="cancelUpload">
<ActionBtn className="" onClick={() => cancelUpload(index, loaded == '100' )}>{loaded == '100' ? "删除" : "取消上传"}</ActionBtn>
</div>
</div>
<div>
<span className="titleLabel">标题</span>
<Input placeholder={`标题支持最多${MAX_LENGTH}个字符`} onInput={titleChange} maxLength={MAX_LENGTH} suffix={
<span className="color-grey-6 font-13">{String(title.length)}/{MAX_LENGTH}</span>
}
className="titleInput"
></Input>
</div>
</div>
)
}
export default VideoUpload

@ -0,0 +1,445 @@
import React, { useState, useEffect, useReducer, useContext, memo } from 'react';
import { getUrl2, isDev, CBreadcrumb, ActionBtn, ThemeContext } from 'educoder'
import axios from 'axios'
import VideoUpload from './VideoUpload'
import { Button } from 'antd'
import { getUploader } from './AliyunUploaderManager'
import { reducer, initialState } from './VideoReducer'
import { deleteVideoInCloud } from './VideoUtil'
import uploadIcon from './images/upload.png'
import uploadHoverIcon from './images/upload_hover.png'
let uploader
const files = []
const MAX_FILE_COUNT = 3
const MAX_FILE_SIZE = 200
let noUploads = true
function VideoUploadList (props) {
// const [videoes, setVideoes] = useState([]);
const [state, dispatch] = useReducer(reducer, initialState);
const theme = useContext(ThemeContext)
useEffect(() => {
window.addEventListener("beforeunload", beforeunload);
return () => {
uploader = null;
window.removeEventListener("beforeunload", beforeunload);
}
}, [])
// TODO 闭包!
noUploads = (!state.videoes || state.videoes.length == 0);
function beforeunload(e) {
if (noUploads) {
return true;
}
var confirmationMessage = "确认要离开当前页面,当前数据不可恢复";
(e || window.event).returnValue = confirmationMessage; //Gecko + IE
return confirmationMessage; //Webkit, Safari, Chrome etc.
}
const _beforeunload = beforeunload // .bind(this, noUploads, state)
const username = props.match.params.username
const { showNotification, history } = props;
const uploaderOptions = {
}
function onUploadChange (e) {
var file = e.target.files[0]
if (!file) {
// alert("请先选择需要上传的文件!")
return
}
if (file.size > 200 * 1024 * 1024) {
// 超过200m TODO
clearInput()
showNotification(`视频大小超过${MAX_FILE_SIZE}M`)
return;
}
let gotTheSameFileName = false;
state.videoes.some((item) => {
if (item.name == file.name) {
gotTheSameFileName = true;
return true;
}
})
if (gotTheSameFileName) {
clearInput()
showNotification(`你不能上传同一个视频文件名称,请重新选择。`)
return;
}
var Title = file.name
var userData = '{"Vod":{}}'
if (!uploader) {
getUploader(username,
// Object.assign(uploaderOptions,
{
// 重新创建 才会用最新的 dispatch
create: !uploader,
addFileSuccess: (uploadInfo) => {
const file = uploadInfo.file
console.log('addFileSuccess', uploadInfo)
// const newVideoes = [...videoes, {
// name: file.name,
// size: file.size,
// type: file.type,
// fileHash: uploadInfo.fileHash, // "ba1bbc53fdecd9eaaae479fbd9518442"
// state: uploadInfo.state, // "Uploading" "Ready"
// videoId: uploadInfo.videoId, // "719b82c875c34ac39f94feb145d25ad2"
// loaded: 0
// }]
// setVideoes(newVideoes)
// files.push(file)
clearInput()
dispatch({type: 'addVideo', uploadInfo})
},
onUploadProgress: (uploadInfo, totalSize, progress) => {
var progressPercent = Math.ceil(progress * 100)
// let _index = -1;
// videoes.some((item, index) => {
// // addFileSuccess的时候没有fileHash
// // if (uploadInfo.fileHash == item.fileHash) {
// if (uploadInfo.file.name == item.name) {
// _index = index
// return true;
// }
// })
// TODO 这里不用reducer会出现state被重置的问题
// if (_index == -1) {
// const newVideoes = [...videoes, {
// name: file.name,
// size: file.size,
// type: file.type,
// fileHash: uploadInfo.fileHash, // "ba1bbc53fdecd9eaaae479fbd9518442"
// state: uploadInfo.state, // "Uploading" "Ready"
// videoId: uploadInfo.videoId, // "719b82c875c34ac39f94feb145d25ad2"
// loaded: progressPercent
// }]
// setVideoes(newVideoes)
// return;
// }
// // exercise_questions : update(prevState.exercise_questions, {[index]: { isNew: {$set: false}}})
// setVideoes(update(videoes, {[_index]: { loaded: {$set: progressPercent}}}))
dispatch({type: 'updateProgress', uploadInfo, progressPercent})
},
onUploadFailed: (uploadInfo) => {
console.log('onUploadFailed', uploadInfo)
props.showNotification('视频云服务出现异常,请重新上传。')
},
onUploadEnd: (uploadInfo) => {
console.log('onUploadEnd', uploadInfo)
},
onUploadSucceed: (uploadInfo) => {
console.log('onUploadSucceed', uploadInfo)
},
onUploadError: (uploadInfo) => {
},
// 可能需要等lib加载完毕才能执行
gotUploader: (_uploader) => {
// 首先调用 uploader.addFile(event.target.files[i], null, null, null, userData)
console.log(_uploader)
let result = _uploader.addFile(file, null, null, null, userData)
uploader = _uploader;
window.uploader = uploader;
}
}
// )
)
} else {
let result = uploader.addFile(file, null, null, null, userData)
}
}
function clearInput() {
const _input = document.getElementById('fileUpload')
_input.value = ''
}
function doDelete(index, isSuccess) {
uploader.deleteFile(index)
// uploader.cancelFile(index)
if (isSuccess) {
deleteVideoInCloud(username, state.videoes[index].videoId)
}
clearInput()
dispatch({type: 'removeVideo', index})
// setVideoes([...videoes.splice(index, 1)])
}
// uploader.deleteFile(index);
function cancelUpload(index, isSuccess) {
props.confirm({
content: <div>
<div>您确认要{isSuccess ? '删除' : '取消上传'}该视频吗</div>
</div>,
onOk: () => {
doDelete(index, isSuccess)
}
})
}
function onPublish() {
if (state.videoes.length == 0) {
showNotification('请先上传视频')
return;
}
const publishUrl = `/users/${username}/videos/batch_publish.json`
axios.post(publishUrl, {
videos: state.videoes.map(item => {
return {
video_id: item.videoId,
// todo
title: item.title
}
})
}).then((response) => {
// to success page
if (response.data.status == 0) {
history.push(`/users/${username}/videoes/success`)
}
}).catch((error) => {
console.log(error)
})
}
function onTitleChange(title, index) {
dispatch({type: 'updateTitle', title, index})
}
// login
const protocolLine = <div>上传视频即表示您已同意<span style={{color: theme.foreground_select}}>上传内容协议</span></div>
return (
<div className="educontent videoUploadList" style={{ marginBottom: '200px' }}>
<style>{`
.videoUploadList .section {
background: #fff;
padding: 16px 20px;
padding-top: 0px;
position: relative;
padding-bottom: 36px;
}
.videoUploadList .cBreadcrumb {
margin-top: 16px;
}
.videoUploadList .uploadTip {
line-height: 18px;
margin-bottom: 16px;
}
.videoUploadList .title {
margin-bottom: 4px;
}
.videoUploadList .title .head {
display: inline-block;
margin-right: 8px;
}
.videoUploadList .title .titleDescription {
color: #555;
}
.videoUploadList .section .description {
padding-top: 10px;
margin-top: 20px;
margin-bottom: 30px;
color: #777;
}
.videoUploadList .section .description.noUploads {
text-align: 'center';
}
.videoUploadList .publishBtn {
padding: 0 16px
}
.videoUploadList .publishRow .publishBtn {
padding: 6px 24px;
height: auto;
margin-bottom: 24px;
}
.videoUploadList .addVideoBtn {
position: absolute;
right: 30px;
}
.videoUploadList .publishRow {
text-align: center;
margin-top: 42px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.noUploads {
text-align: center;
}
/* item */
.videoUploadList .cancelUpload {
flex: 0 0 200px;
}
.videoUploadList .titleInput {
width: 480px;
margin-top: 16px;
}
.videoUploadList .videoUpload {
padding: 26px 0;
border-bottom: 1px dashed #DCDCDC;
}
.videoUploadList .videoUpload:last-child {
border-bottom: none;
}
.noUploads img {
width: 64px;
height: 48px;
}
.noUploads .uploadHoverIcon {
display: none;
}
.noUploads .imgWrap:hover .uploadHoverIcon {
display: inline;
}
.noUploads .imgWrap:hover .uploadIcon {
display: none;
}
.noUploads .imgWrap {
width: 72px;
height: 54px;
margin: 0 auto;
cursor: pointer;
}
`}</style>
<CBreadcrumb
className="mb26"
separator=" > "
items={[
{ to: `/users/${username}/videoes`, name: '视频'},
{ name: '上传'}
]}
></CBreadcrumb>
<div className="title">
<h2 className="head">上传视频</h2>
{/* <span className="titleDescription">单次最多支持{MAX_FILE_COUNT}个视频文件上传,不支持断点续传,单个视频文件最大{MAX_FILE_SIZE}M</span> */}
</div>
<div className="section">
{/* noUploads */}
{noUploads && <div className="noUploads" style={{paddingTop: '72px'}} >
<div className="imgWrap" onClick={() => document.getElementById('fileUpload').click()}>
<img className="uploadIcon" src={uploadIcon} ></img>
<img className="uploadHoverIcon" src={uploadHoverIcon} ></img>
</div>
<div style={{
color: '#000000',
fontSize: '18px',
fontWeight: 'bold',
marginBottom: '20px'
}}>选择您要上传的视频</div>
{protocolLine}
</div>}
<div>
{state.videoes.map((item, vIndex) => {
return (
<VideoUpload {...props} {...item} className=""
cancelUpload={cancelUpload}
onTitleChange={onTitleChange}
key={vIndex}
index={vIndex}
></VideoUpload>
)
})}
</div>
{state.videoes && state.videoes.length === MAX_FILE_COUNT &&
<div className="uploadTip">
<i className="iconfont icon-tishi" style={{color: '#FF6F6F', verticalAlign: 'text-bottom'}}></i>
<span>单次最多支持3个视频文件上传</span>
</div>}
{(!noUploads && state.videoes.length < MAX_FILE_COUNT) && <ActionBtn className="publishBtn" onClick={() => document.getElementById('fileUpload').click()}
>继续添加</ActionBtn>}
<div className={`description ${noUploads ? 'noUploads' : ''}`}>
<div className="">视频大小不支持断点续传单个视频文件最大200M单次最多支持3个视频文件上传 </div>
<div className="">视频规格aviflvf4vm4vmovmp4rmvbswfwebm </div>
<div className="">温馨提示请勿上传违法视频平台将为每一个视频分配一个地址您可以通过引用改地址将视频使用在开发社区等模块</div>
</div>
{!noUploads && <React.Fragment>
{/* {(state.videoes.length < MAX_FILE_COUNT) && <Button type="primary" icon="plus-square"
onClick={() => { document.getElementById('fileUpload').click()}}
className="fr addVideoBtn"
>
添加更多视频
</Button>} */}
<div style={{}} className="publishRow">
<ActionBtn className="publishBtn" onClick={() => onPublish()}
>立即发布</ActionBtn>
{protocolLine}
</div>
</React.Fragment>}
</div>
<input type="file" id="fileUpload" style={{display: 'none'}} onChange={onUploadChange}
accept="video/*"
></input>
</div>
)
}
export default VideoUploadList
/**
bucket: "outin-396971199eed11e991a100163e1c7426"
checkpoint: {file: File, name: "sv/2d0fd065-16c7a62fcc5/2d0fd065-16c7a62fcc5.mp4", fileSize: 491511493, partSize: 1048576, uploadId: "A8DB0663F44C44F58F3F7F45892ED08B", }
endpoint: "https://oss-cn-shanghai.aliyuncs.com"
file: File {name: "[阳光电影-www.ygdy8.com]金秘书为何这样-02.mp4", lastModified: 1532441562000, lastModifiedDate: Tue Jul 24 2018 22:12:42 GMT+0800 (China Standard Time), webkitRelativePath: "", size: 491511493, }
fileHash: "ba1bbc53fdecd9eaaae479fbd9518442"
isImage: false
loaded: 0.5927505330490405
object: "sv/2d0fd065-16c7a62fcc5/2d0fd065-16c7a62fcc5.mp4"
region: "cn-shanghai"
retry: false
ri: "F0FDC11A-9A92-4A50-882A-423C3EA499F3"
state: "Uploading"
userData: "eyJWb2QiOnt9fQ=="
videoId: "719b82c875c34ac39f94feb145d25ad2"
file
lastModified: 1532441562000
lastModifiedDate: Tue Jul 24 2018 22:12:42 GMT+0800 (China Standard Time) {}
name: "[阳光电影-www.ygdy8.com]金秘书为何这样-02.mp4"
size: 491511493
type: "video/mp4"
webkitRelativePath: ""
*/

@ -0,0 +1,13 @@
import axios from 'axios'
export function deleteVideoInCloud(login, video_id) {
const url = `/users/${login}/videos/cancel.json`
axios.post(url, {
video_id
}).then((response) => {
}).catch((error) => {
console.log(error)
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

@ -114,8 +114,25 @@ em.vertical-line{display: inline-block;width: 2px;background: #999;height: 10px}
.smallSquare:nth-child(3n+0){margin-right: 0px;}
.partimg{height: 180px;width: 100%;border-radius: 6px 6px 0px 0px;}
/*块状列表上面的绿色标签*/
.tag-green{position: absolute;left: 0px;top:20px;}
.tag-green .tag-name{display: block;width: auto;background-image: url("/images/educoder/tag1.png");background-size: 100% 100%;padding: 0px 8px;color: #fff;float: left;}
.tag-green{
position: absolute;
left: 15px;
bottom: 95px;}
.tag-green .tag-name{display: block;width: auto;
/*background-image: url("/images/educoder/tag1.png");*/
background: #000000;
border: 1px solid #fff;
border-radius: 3px;
font-size: 14px;
background-size: 100% 100%;padding: 0px 8px;color: #fff;float: left;}
.tag-orange{position: absolute;right: 0px;top:20px;}
.tag-orange .tag-name{display: block;width: auto;background-color:#FF6800;
background-size: 100% 100%;padding: 0px 8px;color: #fff;float: left;
height: 28px;
line-height: 28px;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
/*发送至弹框里的下拉框*/
.downSelectOption{position: relative;height: 35px;}
.downSelectOption .showOption{background-color: #F4F4F4;border: 1px solid #EAEAEA;width: 100%;padding: 5px 40px 5px 5px;outline: none;height: 100%;box-sizing: border-box;}

@ -551,7 +551,7 @@ a:hover.task-btn-orange{background: #459BE6;}
a.user_bluebg_btn{background-color:#4CACFF;color: #fff;}
a.user_orangebg_btn{background-color:#FF6800;color: #fff;}
a.user_greybg_btn{background-color:#747A7F;color: #fff;}
/*.user_white_btn{border: 1px solid #ffffff;color: #ffffff!important;}*/
.pointer{cursor: pointer}
.cdefault{cursor: default}

Loading…
Cancel
Save