|
|
/*
|
|
|
* @Description: jupyter tpi
|
|
|
* @Author: tangjiang
|
|
|
* @Github:
|
|
|
* @Date: 2019-12-11 08:35:23
|
|
|
* @LastEditors: tangjiang
|
|
|
* @LastEditTime: 2019-12-13 15:25:50
|
|
|
*/
|
|
|
import './index.scss';
|
|
|
import React, { useEffect, useState } from 'react';
|
|
|
import SplitPane from 'react-split-pane';
|
|
|
import { Button, Modal,Drawer ,Pagination,Empty,Tooltip,Icon,message,Statistic} from 'antd';
|
|
|
import {
|
|
|
connect
|
|
|
} from 'react-redux';
|
|
|
import FloatButton from '../../page/component/FloatButton';
|
|
|
import UserInfo from '../../developer/components/userInfo';
|
|
|
import actions from '../../../redux/actions';
|
|
|
import LeftPane from './leftPane';
|
|
|
import RightPane from './rightPane';
|
|
|
const { Countdown } = Statistic;
|
|
|
import MyIcon from "../../../common/components/MyIcon";
|
|
|
function jsCopy(s) {
|
|
|
var copyEle = document.getElementById(s);
|
|
|
const range = document.createRange(); // 创造range
|
|
|
window.getSelection().removeAllRanges(); //清除页面中已有的selection
|
|
|
range.selectNode(copyEle); // 选中需要复制的节点
|
|
|
window.getSelection().addRange(range); // 执行选中元素
|
|
|
const copyStatus = document.execCommand("Copy"); // 执行copy操作
|
|
|
// 对成功与否定进行提示
|
|
|
copyStatuss(copyStatus)
|
|
|
}
|
|
|
|
|
|
function copyStatuss(copyStatus){
|
|
|
if (copyStatus) {
|
|
|
message.success('复制成功');
|
|
|
} else {
|
|
|
message.error('复制失败');
|
|
|
}
|
|
|
}
|
|
|
function JupyterTPI (props) {
|
|
|
|
|
|
// 获取 identifier 值
|
|
|
const {
|
|
|
match: {
|
|
|
params = {}
|
|
|
},
|
|
|
url,
|
|
|
loading, // 保存按钮状态
|
|
|
total,
|
|
|
pagination,
|
|
|
dataSets, // 数据集
|
|
|
jupyter_info,
|
|
|
getJupyterInfo,
|
|
|
syncJupyterCode,
|
|
|
jupyter_tpi_url_state,
|
|
|
getJupyterTpiDataSet,
|
|
|
getJupyterTpiUrl,
|
|
|
saveJupyterTpi,
|
|
|
changeLoadingState,
|
|
|
changeGetJupyterUrlState,
|
|
|
jupyter_identifier,
|
|
|
changeCurrentPage,
|
|
|
changeshowDrawer,
|
|
|
drawervisible,
|
|
|
reset_with_tpi,
|
|
|
} = props;
|
|
|
|
|
|
const emptyCtx = (
|
|
|
<div className="jupyter_empty">
|
|
|
<Empty />
|
|
|
</div>
|
|
|
);
|
|
|
const {identifier} = params;
|
|
|
const [userInfo, setUserInfo] = useState({});
|
|
|
const [jupyterInfo, setJupyterInfo] = useState({});
|
|
|
const [updateTip, setUpdateTip] = useState(true);
|
|
|
const [myIdentifier, setMyIdentifier] = useState('');
|
|
|
const [renderCtx, setRenderCtx] = useState(() => (emptyCtx));
|
|
|
|
|
|
// 保存代码
|
|
|
const addEventListeners = () => {
|
|
|
window.addEventListener('message', (e) => {
|
|
|
console.log("触发了jupytermessage");
|
|
|
if(e){
|
|
|
if(e.data){
|
|
|
if(e.data==="jupytermessage"){
|
|
|
saveJupyterTpi();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
}
|
|
|
|
|
|
const stopposttpip=(sum)=>{
|
|
|
var _iframe = document.getElementById("rightPaneframe");
|
|
|
if(_iframe == null || _iframe == undefined || _iframe == ""){
|
|
|
return;
|
|
|
}
|
|
|
if(sum===1){
|
|
|
_iframe.contentWindow.postMessage("stopParent", "*");
|
|
|
}else{
|
|
|
_iframe.contentWindow.postMessage("clonsParent", "*");
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
addEventListeners()
|
|
|
}, []);
|
|
|
|
|
|
useEffect(() => {
|
|
|
/* 先调用 jupyter的 TPI 接口,
|
|
|
* 获取 用户信息,
|
|
|
* 实训的 identifier, 状态, 名称, 是否被修改等信息
|
|
|
*/
|
|
|
getJupyterInfo(identifier);
|
|
|
}, [identifier]);
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
// 设置jupyter信息
|
|
|
setJupyterInfo(jupyter_info || {});
|
|
|
const {user, tpm_modified, myshixun_identifier} = jupyter_info;
|
|
|
if (user) {
|
|
|
setUserInfo(user);
|
|
|
}
|
|
|
|
|
|
if (myshixun_identifier) {
|
|
|
setMyIdentifier(myshixun_identifier);
|
|
|
}
|
|
|
|
|
|
// 同步代码
|
|
|
if (tpm_modified && updateTip && myshixun_identifier) {
|
|
|
|
|
|
setUpdateTip(false);
|
|
|
Modal.confirm({
|
|
|
title: '更新通知',
|
|
|
content: (<div className="update_notice">
|
|
|
{stopposttpip(1)}
|
|
|
<p className="update_txt">关卡任务的代码文件有更新啦</p>
|
|
|
<p className="update_txt">更新操作将保留已完成的评测记录和成绩</p>
|
|
|
<p className="update_txt">还未完成评测的任务代码,请自行保存</p>
|
|
|
</div>),
|
|
|
okText: '确定',
|
|
|
cancelText: '取消',
|
|
|
onOk () {
|
|
|
syncJupyterCode(myshixun_identifier, '同步成功');
|
|
|
},onCancel() {
|
|
|
stopposttpip(2)
|
|
|
},
|
|
|
})
|
|
|
}
|
|
|
}, [props]);
|
|
|
|
|
|
|
|
|
// 重置实训
|
|
|
const handleClickResetTpi = () => {
|
|
|
stopposttpip(1)
|
|
|
Modal.confirm({
|
|
|
title: '重置实训',
|
|
|
content: (
|
|
|
<p style={{ lineHeight: '24px' }}>
|
|
|
你在本文件中修改的内容将丢失,<br />
|
|
|
是否确定重新加载初始代码?
|
|
|
</p>
|
|
|
),
|
|
|
okText: '确定',
|
|
|
cancelText: '取消',
|
|
|
onOk () {
|
|
|
console.log('调用重置代码....', myIdentifier);
|
|
|
if (myIdentifier) {
|
|
|
syncJupyterCode(myIdentifier, '重置成功');
|
|
|
}
|
|
|
},
|
|
|
onCancel() {
|
|
|
stopposttpip(2)
|
|
|
},
|
|
|
})
|
|
|
}
|
|
|
|
|
|
|
|
|
// 重置环境
|
|
|
const handleEnvironmentTpi = () => {
|
|
|
stopposttpip(1)
|
|
|
Modal.confirm({
|
|
|
title: '重置环境',
|
|
|
content: (
|
|
|
<p style={{ lineHeight: '24px' }}>
|
|
|
你在本文件中修改的内容将丢失,<br />
|
|
|
是否确定重置环境?
|
|
|
</p>
|
|
|
),
|
|
|
okText: '确定',
|
|
|
cancelText: '取消',
|
|
|
onOk () {
|
|
|
console.log('调用重置代码....', myIdentifier);
|
|
|
if (myIdentifier) {
|
|
|
reset_with_tpi(myIdentifier, '重置成功');
|
|
|
}
|
|
|
},
|
|
|
onCancel() {
|
|
|
stopposttpip(2)
|
|
|
},
|
|
|
})
|
|
|
}
|
|
|
|
|
|
// 退出实训
|
|
|
const handleClickQuitTpi = () => {
|
|
|
// console.log(jupyterInfo);
|
|
|
const { identifier } = jupyterInfo;
|
|
|
if (!identifier) return;
|
|
|
props.history.push(`/shixuns/${identifier}/challenges`);
|
|
|
}
|
|
|
|
|
|
// 重新获取 jupyter url
|
|
|
const handleOnReloadUrl = (id) => {
|
|
|
// console.log('jupyter 信息: ', jupyterInfo);
|
|
|
// 改变加载状态值
|
|
|
changeGetJupyterUrlState(-1);
|
|
|
getJupyterTpiUrl({identifier: myIdentifier});
|
|
|
}
|
|
|
|
|
|
// 保存代码
|
|
|
const handleOnSave = () => {
|
|
|
// 改变按钮状态
|
|
|
changeLoadingState(true);
|
|
|
saveJupyterTpi();
|
|
|
}
|
|
|
|
|
|
// 分页信息改变时
|
|
|
const handlePageChange = (current) => {
|
|
|
// 改变当前页
|
|
|
changeCurrentPage(current);
|
|
|
// 分页查找数据
|
|
|
getJupyterTpiDataSet(jupyter_identifier);
|
|
|
}
|
|
|
|
|
|
const swtichFirstDrawer = () => {
|
|
|
changeshowDrawer(!drawervisible)
|
|
|
}
|
|
|
|
|
|
const firstDrawerWidth = ()=>{
|
|
|
return 260
|
|
|
};
|
|
|
|
|
|
// 分页处理
|
|
|
const handleChangePage = (page) => {
|
|
|
// console.log(page, pageSize);
|
|
|
handlePageChange(page);
|
|
|
}
|
|
|
// const listCtx = ;
|
|
|
useEffect(() => {
|
|
|
if (dataSets.length > 0) {
|
|
|
console.log('数据集的个数: ', dataSets.length);
|
|
|
const oList = dataSets.map((item, i) => {
|
|
|
return (
|
|
|
<li className="jupyter_item" key={`key_${i}`}>
|
|
|
<Tooltip
|
|
|
placement="right"
|
|
|
// title={item.file_path}
|
|
|
mouseLeaveDelay={0.3}
|
|
|
>
|
|
|
<div className="sortinxdirection">
|
|
|
<Icon type="file-text" className="jupyter_icon fl lineheighttaj" />
|
|
|
<a className="jupyter_name ml10 maxnamewidth150 lineheighttaj colorlineheighttaj" title={item.title}>{item.title}</a>
|
|
|
<a className={"fr color-blue lineheighttaj"}
|
|
|
onClick={() => {
|
|
|
jsCopy("file_path"+i)
|
|
|
}}>复制地址</a>
|
|
|
</div>
|
|
|
<input id={"file_path"+i} className={"file_path_input"} value={item.file_path}/>
|
|
|
</Tooltip>
|
|
|
</li>
|
|
|
);
|
|
|
});
|
|
|
|
|
|
const oUl = (
|
|
|
<ul className="jupyter_data_list">
|
|
|
{ oList }
|
|
|
</ul>
|
|
|
);
|
|
|
|
|
|
setRenderCtx(oUl);
|
|
|
}
|
|
|
}, [props]);
|
|
|
|
|
|
const deadline = Date.now() + 7200 * 1000; // Moment is also OK
|
|
|
|
|
|
return (
|
|
|
<div className="jupyter_area">
|
|
|
<div className="jupyter_header">
|
|
|
<UserInfo userInfo={userInfo} />
|
|
|
<p className="jupyter_title">
|
|
|
<span className="title_desc" style={{ marginTop: '10px' }}>{jupyterInfo.name}</span>
|
|
|
<span className="title_time jupytertitle_time">
|
|
|
<Countdown value={deadline} format="HH:mm:ss" />
|
|
|
</span>
|
|
|
</p>
|
|
|
<p className="jupyter_btn">
|
|
|
{/* sync | poweroff */}
|
|
|
<Button
|
|
|
className="btn_common"
|
|
|
type="link"
|
|
|
icon="history"
|
|
|
onClick={handleClickResetTpi}
|
|
|
>重置实训</Button>
|
|
|
|
|
|
<Button
|
|
|
className="btn_common"
|
|
|
type="link"
|
|
|
icon="sync"
|
|
|
onClick={handleEnvironmentTpi}
|
|
|
>重置环境</Button>
|
|
|
|
|
|
<Button
|
|
|
className="btn_common"
|
|
|
type="link"
|
|
|
icon="poweroff"
|
|
|
onClick={handleClickQuitTpi}
|
|
|
>退出实训</Button>
|
|
|
</p>
|
|
|
</div>
|
|
|
|
|
|
<div className="jupyter_ctx">
|
|
|
<SplitPane split="vertical" minSize={350} maxSize={-350} defaultSize="100%">
|
|
|
{/*<div className={'split-pane-left'}>*/}
|
|
|
{/* <LeftPane*/}
|
|
|
{/* dataSets={dataSets}*/}
|
|
|
{/* total={total}*/}
|
|
|
{/* pagination={pagination}*/}
|
|
|
{/* onPageChange={handlePageChange}*/}
|
|
|
{/* />*/}
|
|
|
{/*</div>*/}
|
|
|
<SplitPane split="vertical" defaultSize="100%" allowResize={false}>
|
|
|
<RightPane
|
|
|
identifier={myIdentifier}
|
|
|
status={jupyter_tpi_url_state}
|
|
|
url={url}
|
|
|
loading={loading}
|
|
|
onReloadUrl={handleOnReloadUrl}
|
|
|
onSave={handleOnSave}
|
|
|
/>
|
|
|
<FloatButton onClick={swtichFirstDrawer} className={drawervisible===false?"jupyter_float_button":"jupyter_float_button newjupyter_float_button"}>{"数据集"}</FloatButton>
|
|
|
</SplitPane>
|
|
|
|
|
|
</SplitPane>
|
|
|
<Drawer
|
|
|
placement={"right"}
|
|
|
closable={false}
|
|
|
mask={false}
|
|
|
// onClose={this.onClose}
|
|
|
visible={drawervisible}
|
|
|
className={"RightPaneDrawer"}
|
|
|
>
|
|
|
{/*<p className={"RightPaneDrawertop"}></p>*/}
|
|
|
<div className="jupyter_data_sets_area newjupyter_data_sets_area">
|
|
|
<h2 className="jupyter_h2_title">
|
|
|
{/*<MyIcon type="iconwenti" className="jupyter_data_icon"/>*/}
|
|
|
<i className={"iconfont icon-base"}></i>数据集
|
|
|
{/* <span className="iconfont icon-java jupyter_data_icon"></span>数据集 */}
|
|
|
</h2>
|
|
|
{ renderCtx }
|
|
|
<div className='jupyter_pagination'>
|
|
|
{total<20?"":<Pagination
|
|
|
simple
|
|
|
current={pagination.page}
|
|
|
pageSize={pagination.limit}
|
|
|
total={total}
|
|
|
onChange={handleChangePage}
|
|
|
/>}
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
</Drawer>
|
|
|
</div>
|
|
|
</div>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
const mapStateToProps = (state) => {
|
|
|
const {
|
|
|
jupyter_info,
|
|
|
jupyter_tpi_url,
|
|
|
jupyter_data_set,
|
|
|
jupyter_tpi_url_state,
|
|
|
jupyter_data_set_count,
|
|
|
jupyter_pagination,
|
|
|
jupyter_identifier
|
|
|
} = state.jupyterReducer;
|
|
|
const { loading ,drawervisible} = state.commonReducer;
|
|
|
return {
|
|
|
loading,
|
|
|
jupyter_info,
|
|
|
url: jupyter_tpi_url,
|
|
|
dataSets: jupyter_data_set,
|
|
|
jupyter_tpi_url_state,
|
|
|
total: jupyter_data_set_count,
|
|
|
pagination: jupyter_pagination,
|
|
|
jupyter_identifier,
|
|
|
drawervisible,
|
|
|
};
|
|
|
}
|
|
|
|
|
|
const mapDispatchToProps = (dispatch) => ({
|
|
|
changeGetJupyterUrlState: (status) => dispatch(actions.changeGetJupyterUrlState(status)),
|
|
|
getJupyterInfo: (identifier) => dispatch(actions.getJupyterInfo(identifier)),
|
|
|
// 重置代码
|
|
|
syncJupyterCode: (identifier, msg) => dispatch(actions.syncJupyterCode(identifier, msg)),
|
|
|
// 重置代码
|
|
|
reset_with_tpi: (identifier, msg) => dispatch(actions.reset_with_tpi(identifier, msg)),
|
|
|
getJupyterTpiDataSet: (identifier, current) => dispatch(actions.getJupyterTpiDataSet(identifier, current)),
|
|
|
getJupyterTpiUrl: (identifier) => dispatch(actions.getJupyterTpiUrl(identifier)),
|
|
|
saveJupyterTpi: () => dispatch(actions.saveJupyterTpi()),
|
|
|
changeLoadingState: (flag) => dispatch(actions.changeLoadingState(flag)),
|
|
|
changeCurrentPage: (current) => dispatch(actions.changeCurrentPage(current)),
|
|
|
//展开Drawer
|
|
|
changeshowDrawer: (type) => dispatch(actions.changeshowDrawer(type))
|
|
|
});
|
|
|
|
|
|
export default connect(
|
|
|
mapStateToProps,
|
|
|
mapDispatchToProps
|
|
|
)(JupyterTPI);
|