Merge branches 'dev_Ysl' and 'dev_Ysm' of https://bdgit.educoder.net/Hjqreturn/educoder into dev_Ysl
commit
71b7557875
@ -1,6 +1,6 @@
|
||||
json.id attachment.id
|
||||
json.title attachment.title
|
||||
json.filesize number_to_human_size(attachment.filesize)
|
||||
json.url download_url(attachment)
|
||||
json.created_on attachment.created_on
|
||||
json.is_pdf attachment.is_pdf?
|
||||
json.url attachment.is_pdf? ? download_url(attachment,disposition:"inline") : download_url(attachment)
|
||||
json.created_on attachment.created_on
|
||||
|
@ -1,9 +1,3 @@
|
||||
|
||||
# json.partial! 'attachments/attachment_small', attachment: @file
|
||||
json.id @file.id
|
||||
json.title @file.title
|
||||
json.filesize number_to_human_size(@file.filesize)
|
||||
json.url download_url(@file).gsub("/api","")
|
||||
json.created_on @file.created_on
|
||||
json.is_pdf @file.is_pdf?
|
||||
json.partial! 'attachments/attachment_small', attachment: @file
|
||||
json.partial! "attachment_histories/list", attachment_histories: @attachment_histories
|
||||
|
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
After Width: | Height: | Size: 7.5 KiB |
@ -1,3 +1,33 @@
|
||||
.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;
|
||||
}
|
@ -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;
|
@ -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) ));
|
@ -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"}}>明日签到 <font className="color-orange">+{next_gold}</font> 金币</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
|
@ -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,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,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)
|
||||
})
|
||||
}
|
After Width: | Height: | Size: 491 B |
After Width: | Height: | Size: 413 B |
After Width: | Height: | Size: 337 B |
After Width: | Height: | Size: 486 B |
Loading…
Reference in new issue