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

dev_video
杨树明 5 years ago
commit bfc5b3c37c

@ -6,12 +6,17 @@ module ControllerRescueHandler
Util.logger_error e
render json: {status: -1, message: "接口异常"}
end
rescue_from Mysql2::Error do |e|
Util.logger_error e
render json: {status: -1, message: "接口数据异常"}
end
# rescue_from ActionView::MissingTemplate, with: :object_not_found
# rescue_from ActiveRecord::RecordNotFound, with: :object_not_found
rescue_from Educoder::TipException, with: :tip_show
rescue_from ::ActionView::MissingTemplate, with: :missing_template
rescue_from ActiveRecord::RecordNotFound, with: :object_not_found
rescue_from ActionController::ParameterMissing, with: :render_parameter_missing
# form validation error
rescue_from ActiveModel::ValidationError do |ex|
render_error(ex.model.errors.full_messages.join(','))

@ -2,14 +2,14 @@ class LiveLinksController < ApplicationController
before_action :require_login
before_action :find_course, only: [:index, :create]
before_action :user_course_identity, :teacher_allowed, only: [:create]
before_action :edit_auth, only: [:edit, :update]
before_action :edit_auth, only: [:edit, :update, :destroy]
def index
lives = @course.live_links
order_str = "on_status desc,id desc"
@total_count = lives.size
@my_live_id = @course.live_links.find_by(user_id: current_user.id)&.id
order_str = "#{@my_live_id} desc, #{order_str}" if @my_live_id.present?
order_str = "live_links.id = #{@my_live_id} desc, #{order_str}" if @my_live_id.present?
lives = lives.order("#{order_str}")
@lives = paginate lives.includes(user: :user_extension)
end
@ -34,8 +34,6 @@ class LiveLinksController < ApplicationController
# 开启时发送消息,关闭直播时删除对应的消息
if params[:on_status].to_i == 1
LivePublishJob.perform_later(current_live.id)
else
current_live.tidings.destroy_all
end
end
else
@ -44,6 +42,11 @@ class LiveLinksController < ApplicationController
render_ok
end
def destroy
current_live.destroy!
render_ok
end
private
def create_params

@ -19,8 +19,9 @@ class TidingsController < ApplicationController
end
tidings = tidings.where(tiding_type: tiding_types) if tiding_types.present?
tidings = tidings.where(container_type: 'JoinCourse') if params[:type] == 'course_apply'
@course_apply_count = tidings.where("created_at > '#{@onclick_time}'").where(container_type: 'JoinCourse').count
tidings = tidings.where(container_type: 'JoinCourse', status: 0) if params[:type] == 'course_apply'
# @course_apply_count = tidings.where("created_at > '#{@onclick_time}'").where(container_type: 'JoinCourse', status: 0).count
@course_apply_count = tidings.where("created_at > '#{@onclick_time}'").where(container_type: 'JoinCourse', status: 0).count
tidings = tidings.where(container_type: 'ProjectPackage') if params[:type] == 'project_package'

@ -111,6 +111,7 @@ class StudentWork < ApplicationRecord
# 作品总体评价
def overall_appraisal
return "--" if work_status == 0
case (self.work_score.to_f / homework_common.total_score).round(2)
when (0.90..1.00)
'优秀'

@ -4,6 +4,7 @@ json.course_name @course.name
json.work_id @work.id
json.work_efficiency @homework.work_efficiency
json.has_commit @work.myshixun.present?
json.work_status @work.work_status
if @shixun
json.shixun_name @shixun.name
# 总体评价

@ -550,7 +550,7 @@ Rails.application.routes.draw do
end
end
resources :live_links, only: [:index, :update, :create, :edit], shallow: true
resources :live_links, only: [:index, :update, :create, :edit, :destroy], shallow: true
resources :homework_commons, shallow: true do

@ -0,0 +1,7 @@
class MigrateDefalutToTiding < ActiveRecord::Migration[5.2]
def change
change_column_default :tidings, :status, from: nil, to: 0
Tiding.where(status: nil).update_all(status: 0)
end
end

@ -0,0 +1,9 @@
class MigrateTidingStatus < ActiveRecord::Migration[5.2]
def change
Tiding.where(container_type: "JoinCourse", status: 0).each do |tiding|
unless CourseMessage.where(course_message_id: tiding.trigger_user_id, course_id: tiding.container_id, course_message_type: "JoinCourseRequest", status: 0).exists?
tiding.update!(status: 1)
end
end
end
end

@ -7,10 +7,10 @@ import './video.css';
class Live extends Component{
render(){
const { liveData , lives , successFunc , pageSize , changePage } = this.props;
const { liveData , lives , successFunc , pageSize , changePage , page } = this.props;
return(
<div className="livePanel">
<div className="livePanel" style={{minHeight:`${lives && lives.length *165}px`}}>
{
lives && lives.length > 0 ?
<React.Fragment>
@ -20,6 +20,7 @@ class Live extends Component{
lives.map((item,key)=>{
return(
<LiveItem
key={key}
item={item}
{...this.props}
{...this.state}
@ -32,7 +33,7 @@ class Live extends Component{
{
liveData && liveData.total_count > pageSize &&
<div className="mt30 mb50 edu-txt-center">
<Pagination showQuickJumper total={liveData.total_count} pageSize={pageSize} onChange={(page)=>changePage(page,'live')}></Pagination>
<Pagination showQuickJumper total={liveData.total_count} pageSize={pageSize} current={page} onChange={(page)=>changePage(page,'live')}></Pagination>
</div>
}
</React.Fragment>

@ -1,6 +1,7 @@
import React,{ Component } from "react";
import { Switch } from 'antd';
import { getImageUrl } from 'educoder';
import { WordsBtn } from 'educoder';
import axios from 'axios';
class LiveItem extends Component{
@ -19,10 +20,33 @@ class LiveItem extends Component{
console.log(error);
})
}
deleteLive=(id)=>{
this.props.confirm({
content: '是否确认删除?',
onOk: () => {
const url = `/live_links/${id}.json`;
axios.delete(url).then(result=>{
if(result){
this.props.showNotification(`删除成功!`);
const { successFunc } = this.props;
successFunc && successFunc(1);
}
}).catch(error=>{
console.log(error);
})
},
onCancel() {
console.log('Cancel');
},
});
}
render(){
const { item } = this.props;
const { key, item , setLiveId } = this.props;
return(
<div className="liveItem">
<div className="liveItem" key={key}>
<div className="lineMiddle livesMain">
<span className="lineMiddle">
<img alt={`${item.author_name}`} className="liveAuthor" src={getImageUrl(`images/${item.author_img}`)}/>
@ -30,8 +54,8 @@ class LiveItem extends Component{
<span className={item.on_status?"labels living":"labels lived"}>{item.on_status?'已开播':'未开播'}</span>
</span>
{
item.op_auth &&
<Switch checkedChildren="on" unCheckedChildren="off" defaultChecked={item.on_status} onChange={(flag,event)=>this.changeStatus(flag,event,item.id)}></Switch>
item.op_auth ?
<Switch checkedChildren="on" key={key} className="switchStyle" unCheckedChildren="off" defaultChecked={item.on_status} onChange={(flag,event)=>this.changeStatus(flag,event,item.id)}></Switch>:""
}
</div>
<div className="lineMiddle mt15">
@ -45,6 +69,16 @@ class LiveItem extends Component{
<span className="btns ect">进入</span>
}
</div>
<p className="lineMiddle livesMain mt15 font-12">
<span className="color-grey-9">创建时间{item.created_at}</span>
{
item.op_auth ?
<span>
<WordsBtn style="grey" onClick={()=>setLiveId(item.id)}>编辑</WordsBtn>
<WordsBtn style="grey" className="ml30" onClick={()=>this.deleteLive(item.id)}>删除</WordsBtn>
</span>:""
}
</p>
</div>
)
}

@ -1,5 +1,5 @@
import React,{ Component } from "react";
import { Modal , Form , Input } from 'antd';
import { Modal , Form , Input , Spin } from 'antd';
import './video.css';
import axios from 'axios';
@ -7,25 +7,83 @@ const { TextArea } = Input;
class LiveNew extends Component{
constructor(props){
super(props);
this.state={
isSpining:true
}
}
componentDidUpdate=(prevState)=>{
if(prevState && prevState.liveId !== this.props.liveId){
this.checkType();
}
}
checkType=()=>{
const { liveId } = this.props;
if(liveId){
const url =`/live_links/${liveId}/edit.json`;
axios.get(url).then(result=>{
if(result){
this.props.form.setFieldsValue({
url:result.data.url,
description:result.data.description,
})
}
})
}
this.setState({
isSpining:false
})
}
handleSubmit=()=>{
this.props.form.validateFields((err, values) => {
if(!err){
const CourseId=this.props.match.params.coursesId;
const url = `/courses/${CourseId}/live_links.json`;
axios.post(url,{
...values
}).then(result=>{
if(result){
this.props.showNotification("添加成功!");
const { setliveVisibel } = this.props;
setliveVisibel && setliveVisibel(false,true);
}
}).catch(error=>{
console.log(error);
})
const { liveId } = this.props;
if(liveId){
// 修改
this.updateFunc(liveId,values);
}else{
this.creatFunc(values);
}
}
})
}
// 修改
updateFunc=(id,values)=>{
const url = `/live_links/${id}.json`;
axios.put(url,{
...values
}).then(result=>{
if(result){
this.props.showNotification("修改成功!");
const { setliveVisibel } = this.props;
setliveVisibel && setliveVisibel(false,true);
}
}).catch(error=>{
console.log(error);
})
}
// 新增
creatFunc=(values)=>{
const CourseId=this.props.match.params.coursesId;
const url = `/courses/${CourseId}/live_links.json`;
axios.post(url,{
...values
}).then(result=>{
if(result){
this.props.showNotification("添加成功!");
const { setliveVisibel } = this.props;
setliveVisibel && setliveVisibel(false,true);
}
}).catch(error=>{
console.log(error);
})
}
validateDesc= (rule, value, callback) => {
if (value.length > 100) {
@ -44,8 +102,10 @@ class LiveNew extends Component{
}
render(){
const { isSpining } = this.state;
const {getFieldDecorator} = this.props.form;
const { visible } = this.props;
return(
<Modal
visible={visible}
@ -55,34 +115,36 @@ class LiveNew extends Component{
closable={false}
className="liveModal"
>
<div className="task-popup-content">
<Form onSubmit={this.handleSubmit}>
<Form.Item label={`直播链接`}>
{getFieldDecorator('url', {
rules: [{required: true, message: "请输入第三方直播链接"}],
})(
<Input placeholder="请输入第三方直播链接,如:腾讯课堂播放链接等。" />
)}
</Form.Item>
<Form.Item label={`直播说明`}>
{getFieldDecorator('description', {
rules: [{
validator: this.validateDesc,
}],
})(
<TextArea rows={4} placeholder="可在此介绍开播具体事项,如开播时间安排等。" />
)}
</Form.Item>
<p className="flex-middle" style={{justifyContent:"space-between"}}>
<span>EduCoder推荐您使用<a href="https://ke.qq.com/" target="_blank" className="color-blue">腾讯课堂</a></span>
<a href="https://pub.idqqimg.com/pc/misc/files/20200204/2e4cb765bef54f0c919c0ab8ab79d969.pdf" target="_blank" className="color-blue">操作指引</a>
</p>
</Form>
<div className="clearfix mt30 edu-txt-center">
<a onClick={this.cancelNew} className="task-btn mr30">取消</a>
<a type="submit" onClick={this.handleSubmit} className="task-btn task-btn-orange">确定</a>
<Spin spinning={isSpining}>
<div className="task-popup-content">
<Form onSubmit={this.handleSubmit}>
<Form.Item label={`直播链接`}>
{getFieldDecorator('url', {
rules: [{required: true, message: "请输入第三方直播链接"}],
})(
<Input placeholder="请输入第三方直播链接,如:腾讯课堂播放链接等。" />
)}
</Form.Item>
<Form.Item label={`直播说明`}>
{getFieldDecorator('description', {
rules: [{
validator: this.validateDesc,
}],
})(
<TextArea rows={4} placeholder="可在此介绍开播具体事项,如开播时间安排等。" />
)}
</Form.Item>
<p className="flex-middle" style={{justifyContent:"space-between"}}>
<span>EduCoder推荐您使用<a href="https://ke.qq.com/" target="_blank" className="color-blue">腾讯课堂</a></span>
<a href="https://pub.idqqimg.com/pc/misc/files/20200204/2e4cb765bef54f0c919c0ab8ab79d969.pdf" target="_blank" className="color-blue">操作指引</a>
</p>
</Form>
<div className="clearfix mt30 edu-txt-center">
<a onClick={this.cancelNew} className="task-btn mr30">取消</a>
<a type="submit" onClick={this.handleSubmit} className="task-btn task-btn-orange">确定</a>
</div>
</div>
</div>
</Spin>
</Modal>
)
}

@ -133,14 +133,14 @@ class Video extends Component{
}
render(){
const { count , visible , videoVisible , videoId } = this.state;
const { visible , videoVisible , videoId } = this.state;
const CourseId=this.props.match.params.coursesId;
const login=this.props.user&&this.props.user.login;
const _inputValue = videoId && this.getCopyText(videoId.file_url, videoId.cover_url);
const { admin , is_teacher ,business} = this.props.user;
const { videos , upload , uploadVideo , videoData , changePage ,pageSize } = this.props;
const { videos , upload , uploadVideo , videoData , changePage ,pageSize ,page } = this.props;
const operation = admin || business || (is_teacher && this.props.checkIfProfessionalCertification())
return(
@ -209,7 +209,7 @@ class Video extends Component{
{
videoData && videoData.count > pageSize &&
<div className="mt30 mb50 edu-txt-center">
<Pagination showQuickJumper total={videoData.count} pageSize={pageSize} onChange={(page)=>changePage(page,'video')}></Pagination>
<Pagination showQuickJumper total={videoData.count} current={page} pageSize={pageSize} onChange={(page)=>changePage(page,'video')}></Pagination>
</div>
}
</React.Fragment>

@ -28,6 +28,9 @@ class VideoIndex extends Component{
lives:undefined,
liveData:undefined,
my_liveId:undefined,
liveId:undefined,
liveVisible:false
}
}
@ -50,6 +53,10 @@ class VideoIndex extends Component{
}
// 获取直播列表
getLiveList=(page)=>{
this.setState({
lives:undefined,
isSpining:true
})
const CourseId=this.props.match.params.coursesId;
const url = `/courses/${CourseId}/live_links.json`;
axios.get(url,{
@ -62,7 +69,9 @@ class VideoIndex extends Component{
this.setState({
liveData:result.data,
lives:result.data.lives,
isSpining:false
isSpining:false,
my_liveId:result.data.my_live_id,
liveId:result.data.my_live_id
})
}
}).catch(error=>{
@ -111,13 +120,13 @@ class VideoIndex extends Component{
onEditVideo=(item)=>{
let videoId = {
videoId: item.id,
title: item.title
}
this.setState({
videoId,
})
this.setVisible(true);
videoId: item.id,
title: item.title
}
this.setState({
videoId,
})
this.setVisible(true);
}
uploadVideo=(upload)=>{
this.setState({
@ -132,7 +141,8 @@ class VideoIndex extends Component{
if (admin || business || (is_teacher && this.props.checkIfProfessionalCertification())) {
this.setState({
type:"video",
upload:true
upload:true,
page:1
})
} else {
this.props.showProfessionalCertificationDialog();
@ -140,12 +150,20 @@ class VideoIndex extends Component{
}
// 直播设置后回调的方法
successFunc=()=>{
// successFunc=()=>{
// this.setState({
// type:"live",
// page:1
// })
// this.checkType("live",1);
// }
// 直播设置
liveSetting=()=>{
const { my_liveId } = this.state;
this.setState({
type:"live",
page:1
liveId:my_liveId
})
this.checkType("live",1);
this.setliveVisibel(true);
}
//直播设置弹框
setliveVisibel=(flag,changetypeFlag)=>{
@ -156,13 +174,21 @@ class VideoIndex extends Component{
this.checkType("live",1);
}
}
// 列表-编辑修改传到编辑的id
setLiveId=(id)=>{
this.setState({
liveId:id
})
this.setliveVisibel(true);
}
render(){
const { videos , upload , videoData , type , liveData , lives , liveVisible , isSpining } = this.state;
const { videos , upload , videoData , type , liveData , lives , page , liveVisible , isSpining , liveId } = this.state;
const { admin , is_teacher } = this.props.user;
return(
<React.Fragment>
<LivesNew
visible={liveVisible}
liveId={liveId}
setliveVisibel={this.setliveVisibel}
{...this.props}
{...this.state}
@ -185,7 +211,7 @@ class VideoIndex extends Component{
:
<WordsBtn style="blue" className="font-16" onClick={this.toUpload}>上传视频</WordsBtn>
}
<WordsBtn style="blue" className="font-16 ml30" onClick={()=>this.setliveVisibel(true)}>直播设置</WordsBtn>
<WordsBtn style="blue" className="font-16 ml30" onClick={this.liveSetting}>直播设置</WordsBtn>
</li>
}
</div>
@ -196,6 +222,7 @@ class VideoIndex extends Component{
<Videos
upload={upload}
videos={videos}
page={page}
data={videoData}
pageSize={PAGE_SIZE}
uploadVideo={this.uploadVideo}
@ -208,9 +235,11 @@ class VideoIndex extends Component{
<Lives
lives={lives}
liveData={liveData}
page={page}
pageSize={LIVE_PAGE_SIZE}
successFunc={this.getLiveList}
changePage={this.changePage}
setLiveId={this.setLiveId}
{...this.props}
{...this.state}
></Lives>

@ -83,8 +83,8 @@
border-radius: 16px;
}
.labels.living{
border:1px solid #FF5555;
color: #FF5555;
border:1px solid #25C03B;
color: #25C03B;
}
.labels.lived{
border:1px solid #999999;
@ -117,7 +117,9 @@
line-clamp: 2;
-webkit-box-orient: vertical;
}
.switchStyle.ant-switch-checked{
background-color: #25C03B;
}
.liveModal .ant-modal-body{
padding:20px;
}

@ -4,7 +4,7 @@ import { getUrl2, isDev, CBreadcrumb, ActionBtn, ThemeContext } from 'educoder'
import axios from 'axios'
import VideoUpload from './VideoUpload'
import { Button } from 'antd'
import { Button, Spin } from 'antd'
import { getUploader } from './AliyunUploaderManager'
import { reducer, initialState } from './VideoReducer'
@ -23,7 +23,8 @@ function VideoUploadList (props) {
// const [videos, setvideos] = useState([]);
const [state, dispatch] = useReducer(reducer, initialState);
const theme = useContext(ThemeContext)
const [couldRouteNav, setCouldRouteNav] = useState(false)
const [couldRouteNav, setCouldRouteNav] = useState(false);
const [loading,setLoading] = useState(false);
useEffect(() => {
setCouldRouteNav(false);
@ -96,13 +97,14 @@ function VideoUploadList (props) {
var userData = '{"Vod":{}}'
if (!uploader) {
getUploader(username,
// Object.assign(uploaderOptions,
{
// 重新创建 才会用最新的 dispatch
create: !uploader,
addFileSuccess: (uploadInfo) => {
const file = uploadInfo.file
setLoading(true);
const file = uploadInfo.file;
console.log('addFileSuccess', uploadInfo)
// const newvideos = [...videos, {
// name: file.name,
@ -121,7 +123,8 @@ function VideoUploadList (props) {
dispatch({type: 'addVideo', uploadInfo})
},
onUploadProgress: (uploadInfo, totalSize, progress) => {
setLoading(false);
console.log("upload",uploadInfo);
var progressPercent = Math.ceil(progress * 100)
// let _index = -1;
@ -401,19 +404,20 @@ function VideoUploadList (props) {
}}>选择您要上传的视频</div>
{protocolLine}
</div>}
<div>
{state.videos.map((item, vIndex) => {
return (
<VideoUpload {...props} {...item} className=""
cancelUpload={cancelUpload}
onTitleChange={onTitleChange}
key={vIndex}
index={vIndex}
></VideoUpload>
)
})}
</div>
<Spin spinning={loading}>
<div>
{state.videos.map((item, vIndex) => {
return (
<VideoUpload {...props} {...item} className=""
cancelUpload={cancelUpload}
onTitleChange={onTitleChange}
key={vIndex}
index={vIndex}
></VideoUpload>
)
})}
</div>
</Spin>
{state.videos && state.videos.length === MAX_FILE_COUNT &&
<div className="uploadTip">
{/* <i className="iconfont icon-tishi" style={{color: '#FF6F6F', verticalAlign: 'text-bottom'}}></i> */}

Loading…
Cancel
Save