parent
6ce9d01ffb
commit
68032285bb
@ -0,0 +1,296 @@
|
|||||||
|
/*
|
||||||
|
* @Description: undefined
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Date: 2019-11-15 11:02:49
|
||||||
|
* @Last Modified by: tangjiang
|
||||||
|
* @Last Modified time: 2019-11-18 16:52:38
|
||||||
|
*/
|
||||||
|
import './index.scss';
|
||||||
|
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
|
||||||
|
import { Table, Button, Dropdown, Icon, Menu, Card, Input } from 'antd';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import actions from '../../redux/actions';
|
||||||
|
import MultipTags from './components/multiptags';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
const { Search } = Input;
|
||||||
|
// import reqwest from 'reqwest';
|
||||||
|
/**
|
||||||
|
* 下拉菜单
|
||||||
|
*/
|
||||||
|
const maps = {
|
||||||
|
'categoryMenu': [
|
||||||
|
{
|
||||||
|
'key': '0',
|
||||||
|
'name': '全部',
|
||||||
|
'vlaue': '0'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'key': '1',
|
||||||
|
'name': '程序设计基础',
|
||||||
|
'vlaue': '1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'key': '2',
|
||||||
|
'name': '数据结构与计算',
|
||||||
|
'vlaue': '2'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'hardMenu': [
|
||||||
|
{
|
||||||
|
'key': '0',
|
||||||
|
'name': '简单',
|
||||||
|
'vlaue': '0'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'key': '1',
|
||||||
|
'name': '中等',
|
||||||
|
'vlaue': '1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'key': '2',
|
||||||
|
'name': '困难',
|
||||||
|
'vlaue': '2'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'statusMenu': [
|
||||||
|
{
|
||||||
|
'key': '0',
|
||||||
|
'name': '未做',
|
||||||
|
'vlaue': '0'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'key': '1',
|
||||||
|
'name': '已通过',
|
||||||
|
'vlaue': '1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'key': '2',
|
||||||
|
'name': '未通过',
|
||||||
|
'vlaue': '2'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'originMenu': [
|
||||||
|
{
|
||||||
|
'key': '0',
|
||||||
|
'name': '全部',
|
||||||
|
'vlaue': '0'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'key': '1',
|
||||||
|
'name': '我创建的',
|
||||||
|
'vlaue': '1'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表格列
|
||||||
|
*/
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '序号',
|
||||||
|
dataIndex: 'id',
|
||||||
|
width: '10%'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '标题',
|
||||||
|
dataIndex: 'title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '分类',
|
||||||
|
dataIndex: 'category',
|
||||||
|
width: '20%'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '难度',
|
||||||
|
dataIndex: 'level',
|
||||||
|
align: 'center',
|
||||||
|
width: '10%'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '热度',
|
||||||
|
dataIndex: 'hot',
|
||||||
|
sorter: true,
|
||||||
|
align: 'center',
|
||||||
|
width: '10%'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '通过率',
|
||||||
|
dataIndex: 'pass',
|
||||||
|
sorter: true,
|
||||||
|
align:'right',
|
||||||
|
width: '10%'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
class DeveloperHome extends PureComponent {
|
||||||
|
state = {
|
||||||
|
data: [],
|
||||||
|
pagination: {
|
||||||
|
showQuickJumper: true
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTableChange = (pagination, filters, sorter) => {
|
||||||
|
const pager = { ...this.state.pagination };
|
||||||
|
pager.current = pagination.current;
|
||||||
|
this.setState({
|
||||||
|
pagination: pager,
|
||||||
|
});
|
||||||
|
this.fetch({
|
||||||
|
results: pagination.pageSize,
|
||||||
|
page: pagination.current,
|
||||||
|
sortField: sorter.field,
|
||||||
|
sortOrder: sorter.order,
|
||||||
|
...filters,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch = (params = {}) => {
|
||||||
|
console.log('params:', params);
|
||||||
|
this.setState({ loading: true });
|
||||||
|
// reqwest({
|
||||||
|
// url: 'https://randomuser.me/api',
|
||||||
|
// method: 'get',
|
||||||
|
// data: {
|
||||||
|
// results: 10,
|
||||||
|
// ...params,
|
||||||
|
// },
|
||||||
|
// type: 'json',
|
||||||
|
// }).then(data => {
|
||||||
|
// const pagination = { ...this.state.pagination };
|
||||||
|
// // Read total count from server
|
||||||
|
// // pagination.total = data.totalCount;
|
||||||
|
// pagination.total = 200;
|
||||||
|
// this.setState({
|
||||||
|
// loading: false,
|
||||||
|
// data: data.results,
|
||||||
|
// pagination,
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据类型获取下拉菜单
|
||||||
|
* @param type 类型
|
||||||
|
* @param handleClick 处理函数
|
||||||
|
*/
|
||||||
|
getMenuItems = (type, handleClick) => {
|
||||||
|
return (
|
||||||
|
<Menu onClick={handleClick}>
|
||||||
|
{
|
||||||
|
maps[type].map((item) => {
|
||||||
|
return (
|
||||||
|
<Menu.Item key={item.key}>
|
||||||
|
{item.name}
|
||||||
|
</Menu.Item>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Menu>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索输入框
|
||||||
|
* @param value 输入框值
|
||||||
|
*/
|
||||||
|
handleInputSearch = (value) => {
|
||||||
|
console.log('搜索值==', value);
|
||||||
|
}
|
||||||
|
// 下拉类别菜单
|
||||||
|
handleCategoryMenuClick = (e) => {
|
||||||
|
console.log('dropdown ==>>>', e);
|
||||||
|
}
|
||||||
|
// 难度下拉
|
||||||
|
handleHardMenuClick = (e) => {}
|
||||||
|
// 状态下拉
|
||||||
|
handleSatusMenuClick = (e) => {}
|
||||||
|
// 来源下拉
|
||||||
|
handleOriginMenuClick = (e) => {}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
// const { testReducer, handleClick } = this.props;
|
||||||
|
return (
|
||||||
|
<div className="developer-list">
|
||||||
|
<div className="ant-spin-container">
|
||||||
|
<div className={'banner-wrap'}></div>
|
||||||
|
<div className="educontent">
|
||||||
|
<div className={'card-top'}>
|
||||||
|
<div className="search-params">
|
||||||
|
<p className={'save-question'}>已解决 <span className={''}>589</span> / 1800 题</p>
|
||||||
|
<div className={'question-level'}>
|
||||||
|
<MultipTags type="success" text="简单" numb="1200" style={{ marginRight: '20px' }}/>
|
||||||
|
<MultipTags type="warning" text="中等" numb="400" style={{ marginRight: '20px' }}/>
|
||||||
|
<MultipTags type="error" text="困难" numb="0"/>
|
||||||
|
</div>
|
||||||
|
<Button type="primary">
|
||||||
|
<Link to="/developer/neworedittask">新建</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={'card-table'}>
|
||||||
|
<Card bordered={false}>
|
||||||
|
<Dropdown className={'dropdonw-style'} placement="bottomCenter" overlay={this.getMenuItems('categoryMenu', this.handleCategoryMenuClick)}>
|
||||||
|
<span className={'dropdown-span'}>分类 <Icon type="down"/></span>
|
||||||
|
</Dropdown>
|
||||||
|
<Dropdown className={'dropdonw-style'} placement="bottomCenter" overlay={this.getMenuItems('hardMenu', this.handleHardMenuClick)}>
|
||||||
|
<span className={'dropdown-span'}>难度 <Icon type="down"/></span>
|
||||||
|
</Dropdown>
|
||||||
|
<Dropdown className={'dropdonw-style'} placement="bottomCenter" overlay={this.getMenuItems('statusMenu', this.handleSatusMenuClick)}>
|
||||||
|
<span className={'dropdown-span'}>状态 <Icon type="down"/></span>
|
||||||
|
</Dropdown>
|
||||||
|
<Dropdown className={'dropdonw-style'} placement="bottomCenter" overlay={this.getMenuItems('originMenu', this.handleOriginMenuClick)}>
|
||||||
|
<span className={'dropdown-span'}>来源 <Icon type="down"/></span>
|
||||||
|
</Dropdown>
|
||||||
|
|
||||||
|
<Search
|
||||||
|
placeholder="输入标题进行搜索"
|
||||||
|
onSearch={value => this.handleInputSearch(value)}
|
||||||
|
style={{ width: 320, float: 'right' }}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card bordered={false} style={{ marginTop: '2px'}}>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
rowKey={record => Math.random()}
|
||||||
|
dataSource={this.state.data}
|
||||||
|
pagination={this.state.pagination}
|
||||||
|
onChange={this.handleTableChange}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {*} state store
|
||||||
|
* @param {*} ownProps DeveloperHome 中的 props
|
||||||
|
*/
|
||||||
|
const mapStateToProps = (state, ownProps) => ({
|
||||||
|
testReducer: state.testReducer
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
handleClick: () => dispatch(actions.toggleTodo())
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(DeveloperHome);
|
||||||
|
// export default DeveloperHome;
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 显示 文字 + number 标签类型
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Date: 2019-11-15 10:41:06
|
||||||
|
* @Last Modified by: tangjiang
|
||||||
|
* @Last Modified time: 2019-11-15 17:15:27
|
||||||
|
*/
|
||||||
|
import './index.scss';
|
||||||
|
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
const numberal = require('numeral')
|
||||||
|
|
||||||
|
export default class MultipTags extends PureComponent {
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { type = 'primary', text, numb, ...props} = this.props;
|
||||||
|
|
||||||
|
if (typeof numb !== 'number' && typeof numb !== 'string') {
|
||||||
|
throw new Error('输入的numb必须为数字或数字类型字符串.');
|
||||||
|
}
|
||||||
|
let result = Number(numb) >= 1000
|
||||||
|
? numberal(Number(numb)).format('0.0a')
|
||||||
|
: Number(numb);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'mul-tag-wrap'} {...props}>
|
||||||
|
<span className={`tag-txt ${type}`}>
|
||||||
|
{ text }
|
||||||
|
</span>
|
||||||
|
<span className={'tag-numb'}>
|
||||||
|
{ result }
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
.mul-tag-wrap{
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
.tag-txt, .tag-numb{
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
padding: 0 10px;
|
||||||
|
// line-height: 20px;
|
||||||
|
// height: 20px;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.tag-txt{
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
&.primary{
|
||||||
|
background: #1890ff;
|
||||||
|
}
|
||||||
|
&.warning{
|
||||||
|
background: #faad14;
|
||||||
|
}
|
||||||
|
&.success{
|
||||||
|
background: #52c41a;
|
||||||
|
}
|
||||||
|
&.error{
|
||||||
|
background: #f5222d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-numb{
|
||||||
|
border: 1px solid rgba(238, 238, 238, 1);
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
border-left-color: transparent;
|
||||||
|
margin-left: -1px;
|
||||||
|
min-width: 40px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
.banner-wrap{
|
||||||
|
width: 100%;
|
||||||
|
height: 300px;
|
||||||
|
background-image: url(/static/media/path.e39ba7de.png);
|
||||||
|
background-color: #000a4f;
|
||||||
|
/* background-size: cover; */
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.developer-list{
|
||||||
|
overflow: hidden;
|
||||||
|
.card-top {
|
||||||
|
border-radius:4px;
|
||||||
|
background:rgba(255,255,255,1);
|
||||||
|
height:56px;
|
||||||
|
padding: 0 30px;
|
||||||
|
margin-top: 20px;
|
||||||
|
.search-params{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-question{
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
// .flex-end{
|
||||||
|
// // float: right;
|
||||||
|
// }
|
||||||
|
.question-level{
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-table{
|
||||||
|
margin-top: 10px;
|
||||||
|
.ant-card-body{
|
||||||
|
padding: 10px 30px;
|
||||||
|
}
|
||||||
|
.dropdown-span{
|
||||||
|
position: relative;
|
||||||
|
top: 5px;
|
||||||
|
}
|
||||||
|
.dropdonw-style{
|
||||||
|
margin-right: 50px;
|
||||||
|
.dropdown-span{
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 新建或编辑任务
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Date: 2019-11-15 16:38:34
|
||||||
|
* @Last Modified by: tangjiang
|
||||||
|
* @Last Modified time: 2019-11-18 23:21:25
|
||||||
|
*/
|
||||||
|
import './index.scss';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import SplitPane from 'react-split-pane';// import { Form } from 'antd';
|
||||||
|
import { Button, Icon } from 'antd';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import RightPane from './rightpane';
|
||||||
|
class NewOrEditTask extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className={'new_add_task_wrap'}>
|
||||||
|
<div className={'task_header'}>
|
||||||
|
<Link to="/" className={'header_btn'} >
|
||||||
|
<Icon type="left" style={{ marginRight: '5px'}}/>后退
|
||||||
|
</Link>
|
||||||
|
<span className={'header_title'}>标题内容</span>
|
||||||
|
<Button className={`header_btn`} type="primary">立即发布</Button>
|
||||||
|
</div>
|
||||||
|
<div className="split-pane-area">
|
||||||
|
<SplitPane split="vertical" minSize={200} maxSize={-200} defaultSize="50%">
|
||||||
|
<div >You can use a div component</div>
|
||||||
|
<SplitPane split="vertical" defaultSize="100%" allowResize={false}>
|
||||||
|
<RightPane />
|
||||||
|
<div />
|
||||||
|
</SplitPane>
|
||||||
|
</SplitPane>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(NewOrEditTask);
|
@ -0,0 +1,80 @@
|
|||||||
|
.new_add_task_wrap{
|
||||||
|
height: 100vh;
|
||||||
|
.task_header{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
// justify-content: space-between;
|
||||||
|
height: 65px;
|
||||||
|
background:rgba(34,34,34,1);
|
||||||
|
padding:0 30px;
|
||||||
|
|
||||||
|
.header_btn,
|
||||||
|
.header_title{
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.header_btn{
|
||||||
|
width: 88px;
|
||||||
|
}
|
||||||
|
.header_title{
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.split-pane-area{
|
||||||
|
position: relative;
|
||||||
|
height: calc(100% - 65px);
|
||||||
|
.left_pane,
|
||||||
|
.right_pane{
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.Resizer {
|
||||||
|
background: #000;
|
||||||
|
opacity: 0.2;
|
||||||
|
z-index: 1;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-moz-background-clip: padding;
|
||||||
|
-webkit-background-clip: padding;
|
||||||
|
background-clip: padding-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Resizer:hover {
|
||||||
|
-webkit-transition: all 2s ease;
|
||||||
|
transition: all 2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Resizer.horizontal {
|
||||||
|
height: 11px;
|
||||||
|
margin: -5px 0;
|
||||||
|
border-top: 5px solid rgba(255, 255, 255, 0);
|
||||||
|
border-bottom: 5px solid rgba(255, 255, 255, 0);
|
||||||
|
cursor: row-resize;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Resizer.horizontal:hover {
|
||||||
|
border-top: 5px solid rgba(0, 0, 0, 0.5);
|
||||||
|
border-bottom: 5px solid rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.Resizer.vertical {
|
||||||
|
width: 11px;
|
||||||
|
margin: 0 -5px;
|
||||||
|
border-left: 5px solid rgba(255, 255, 255, 0);
|
||||||
|
border-right: 5px solid rgba(255, 255, 255, 0);
|
||||||
|
cursor: col-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Resizer.vertical:hover {
|
||||||
|
border-left: 5px solid rgba(0, 0, 0, 0.5);
|
||||||
|
border-right: 5px solid rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
.Resizer.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.Resizer.disabled:hover {
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 右侧代码块
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Date: 2019-11-18 08:42:04
|
||||||
|
* @Last Modified by: tangjiang
|
||||||
|
* @Last Modified time: 2019-11-19 08:39:28
|
||||||
|
*/
|
||||||
|
|
||||||
|
import './index.scss';
|
||||||
|
|
||||||
|
import React, { Fragment, useState } from 'react';
|
||||||
|
import { Icon, Drawer, Tabs, Button } from 'antd';
|
||||||
|
// import MonacoEditor from 'react-monaco-editor';
|
||||||
|
import MonacoEditor from '@monaco-editor/react';
|
||||||
|
import InitTabCtx from './initTabCtx';
|
||||||
|
|
||||||
|
const { TabPane } = Tabs;
|
||||||
|
const tabsArrs = [
|
||||||
|
{
|
||||||
|
title: '自定义测试用例',
|
||||||
|
key: '1',
|
||||||
|
content: '这是自定测试用例内容'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '代码执行结果',
|
||||||
|
key: '2',
|
||||||
|
content: '这是自定代码执行结果'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const RightPaneCode = () => {
|
||||||
|
|
||||||
|
const [showDrawer, setShowDrawer] = useState(false);
|
||||||
|
const [defaultActiveKey, setDefaultActiveKey] = useState('1');
|
||||||
|
const [showTextResult, setShowTextResult] = useState(false);
|
||||||
|
const [editCode, setEditCode] = useState('console.log("test")');
|
||||||
|
|
||||||
|
// 打开设置
|
||||||
|
const handleShowDrawer = () => {
|
||||||
|
setShowDrawer(true);
|
||||||
|
}
|
||||||
|
// 关闭设置
|
||||||
|
const handleDrawerClose = () => {
|
||||||
|
setShowDrawer(false);
|
||||||
|
}
|
||||||
|
// 切换tab
|
||||||
|
const handleTabChange = (key) => {
|
||||||
|
setDefaultActiveKey(key);
|
||||||
|
}
|
||||||
|
// 显示/隐藏tab
|
||||||
|
const handleShowControl = () => {
|
||||||
|
setShowTextResult(!showTextResult);
|
||||||
|
}
|
||||||
|
// 沉浸tab内容
|
||||||
|
const tabs = tabsArrs.map((tab) => (
|
||||||
|
<TabPane tab={tab.title} key={tab.key} style={{ height: '280px', overflowY: 'auto' }}>
|
||||||
|
<InitTabCtx ctx={tab.content} state="loaded" position="center"/>
|
||||||
|
</TabPane>
|
||||||
|
));
|
||||||
|
|
||||||
|
const handleEditorChange = (newValue, e) => {
|
||||||
|
setEditCode(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
const classNames = showTextResult ? 'control_tab active' : 'control_tab';
|
||||||
|
const editorOptions = {
|
||||||
|
selectOnLineNumbers: true,
|
||||||
|
automaticLayout: true,
|
||||||
|
fontSize: '16px',
|
||||||
|
revealHorizontalRightPadding: 0,
|
||||||
|
scrollBeyondLastLine: false,
|
||||||
|
smoothScrolling: true
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<div className={'right_pane_code_wrap'}>
|
||||||
|
<div className={'code-title'}>
|
||||||
|
<span>ts.js</span>
|
||||||
|
<span>已保存</span>
|
||||||
|
<Icon className={'code-icon'} type="setting" onClick={handleShowDrawer}/>
|
||||||
|
</div>
|
||||||
|
{/** 代码编辑器 */}
|
||||||
|
<MonacoEditor
|
||||||
|
height={showTextResult ? 'calc(100% - 382px)' : 'calc(100% - 112px)'}
|
||||||
|
width="100%"
|
||||||
|
language="typescript"
|
||||||
|
value={editCode}
|
||||||
|
options={editorOptions}
|
||||||
|
theme="dark"
|
||||||
|
onChange={handleEditorChange}
|
||||||
|
/>
|
||||||
|
{/* 控制台信息 */}
|
||||||
|
<div className="pane_control_area">
|
||||||
|
<Tabs
|
||||||
|
className={classNames}
|
||||||
|
activeKey={defaultActiveKey}
|
||||||
|
tabBarStyle={{ backgroundColor: '#000', color: '#fff' }}
|
||||||
|
onChange={handleTabChange}
|
||||||
|
>
|
||||||
|
{tabs}
|
||||||
|
</Tabs>
|
||||||
|
<div className="pane_control_opts">
|
||||||
|
<Button type="link" size="small" style={{ color: '#fff' }} onClick={handleShowControl}>控制台 <Icon type={ showTextResult ? "down" : "up" } /></Button>
|
||||||
|
<p>
|
||||||
|
<Button ghost size="small" style={{ marginRight: '10px', color: '#28BD8B', borderColor: '#28BD8B' }}>调试代码</Button>
|
||||||
|
<Button type="primary" size="small">提交</Button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Drawer
|
||||||
|
placement="right"
|
||||||
|
closable={false}
|
||||||
|
onClose={handleDrawerClose}
|
||||||
|
visible={showDrawer}
|
||||||
|
>
|
||||||
|
<p>Some contents...</p>
|
||||||
|
<p>Some contents...</p>
|
||||||
|
<p>Some contents...</p>
|
||||||
|
</Drawer>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RightPaneCode;
|
@ -0,0 +1,71 @@
|
|||||||
|
import './index.scss';
|
||||||
|
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Tabs, Button, Icon } from 'antd';
|
||||||
|
import InitTabCtx from './initTabCtx';
|
||||||
|
|
||||||
|
const { TabPane } = Tabs;
|
||||||
|
|
||||||
|
const tabsArrs = [
|
||||||
|
{
|
||||||
|
title: '自定义测试用例',
|
||||||
|
key: '1',
|
||||||
|
content: '这是自定测试用例内容'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '代码执行结果',
|
||||||
|
key: '2',
|
||||||
|
content: '这是自定代码执行结果'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
class RightPaneControl extends Component {
|
||||||
|
|
||||||
|
state = {
|
||||||
|
defaultActiveKey: '1'
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTabChange = (key) => {
|
||||||
|
this.setState({
|
||||||
|
defaultActiveKey: key,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleShowControl = () => {
|
||||||
|
this.setState((oldState) => ({
|
||||||
|
showTextResult: !oldState.showTextResult
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { defaultActiveKey, showTextResult } = this.state;
|
||||||
|
// tab内容区块
|
||||||
|
const tabs = tabsArrs.map((tab) => (
|
||||||
|
<TabPane tab={tab.title} key={tab.key} style={{ height: '280px', overflowY: 'auto' }}>
|
||||||
|
<InitTabCtx ctx={tab.content} state="loaded" position="center"/>
|
||||||
|
</TabPane>
|
||||||
|
));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="pane_control_area" style={{ height: showTextResult ? '400px' : '56px' }}>
|
||||||
|
{/* <Tabs
|
||||||
|
activeKey={defaultActiveKey}
|
||||||
|
tabBarStyle={{ backgroundColor: '#000', color: '#fff' }}
|
||||||
|
onChange={this.handleTabChange}
|
||||||
|
style={{display: showTextResult ? 'block' : 'none'}}
|
||||||
|
>
|
||||||
|
{tabs}
|
||||||
|
</Tabs> */}
|
||||||
|
<div className="pane_control_opts">
|
||||||
|
<Button type="link" size="small" style={{ color: '#fff' }} onClick={this.handleShowControl}>控制台 <Icon type={ showTextResult ? "down" : "up" } /></Button>
|
||||||
|
<p>
|
||||||
|
<Button ghost size="small" style={{ marginRight: '10px', color: '#28BD8B', borderColor: '#28BD8B' }}>调试代码</Button>
|
||||||
|
<Button type="primary" size="small">提交</Button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RightPaneControl;
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 右侧部分: 包含代码块与控制区
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Date: 2019-11-18 08:42:40
|
||||||
|
* @Last Modified by: tangjiang
|
||||||
|
* @Last Modified time: 2019-11-18 22:58:39
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import RightPaneCode from './RightPaneCode';
|
||||||
|
|
||||||
|
const RightPane = () => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RightPaneCode />
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RightPane;
|
@ -0,0 +1,77 @@
|
|||||||
|
.right_pane_code_wrap{
|
||||||
|
position: relative;
|
||||||
|
// justify-content: center;
|
||||||
|
background-color: #222;
|
||||||
|
height: 100%;
|
||||||
|
// justify-content: ;
|
||||||
|
.code-title,
|
||||||
|
.controller-pane,
|
||||||
|
.pane_control_opts{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
// padding: 0 30px;
|
||||||
|
background: #000;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.code-title,
|
||||||
|
.pane_control_opts{
|
||||||
|
padding: 0 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-title{
|
||||||
|
height: 56px;
|
||||||
|
.code-icon{
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// .controller-pane{
|
||||||
|
// min-height: 56px;
|
||||||
|
// background-color: #222;
|
||||||
|
// }
|
||||||
|
.code-pane-wrap{
|
||||||
|
height: 800px;
|
||||||
|
// position: absolute;
|
||||||
|
// top: 56px;
|
||||||
|
// bottom: 56px;
|
||||||
|
// width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pane_control_area{
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
// height: 56px;
|
||||||
|
.control_tab{
|
||||||
|
position: absolute;
|
||||||
|
bottom: -325px;
|
||||||
|
width: 100%;
|
||||||
|
transition: all .2s;
|
||||||
|
opacity: 0;
|
||||||
|
&.active{
|
||||||
|
bottom: 0;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.pane_control_opts{
|
||||||
|
height: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-tabs-bar{
|
||||||
|
padding: 0 10px;
|
||||||
|
margin: 0px;
|
||||||
|
border-bottom: transparent;
|
||||||
|
}
|
||||||
|
.ant-tabs-ink-bar{
|
||||||
|
bottom: 1px;
|
||||||
|
}
|
||||||
|
// .tab_ctx_area.pos_center{
|
||||||
|
// background: #222;
|
||||||
|
// }
|
||||||
|
.pane_control_opts{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 显示tab中的内容
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Date: 2019-11-18 10:43:03
|
||||||
|
* @Last Modified by: tangjiang
|
||||||
|
* @Last Modified time: 2019-11-18 11:35:12
|
||||||
|
*/
|
||||||
|
import './index.scss';
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import { Icon } from 'antd';
|
||||||
|
|
||||||
|
const tabCtx = (ctx, props) => (<p {...props}>{ctx}</p>);
|
||||||
|
const loadingCtx = (<span className={'ctx_loading'}><Icon className={'ctx_icon'} type="loading"/>加载中...</span>);
|
||||||
|
const loadedCtx = (<span className={'ctx_loaded'}><Icon className={'ctx_icon'} type="loading"/>加载完成</span>);
|
||||||
|
const maps = {
|
||||||
|
// default: (ctx, position) => (<p className={`tab_ctx_area tab_ctx_default pos_${position}`}>{ctx}</p>),
|
||||||
|
// loading: (ctx, position) => (<p className={`tab_ctx_area tab_ctx_loading pos_${position}`}>{ctx}</p>),
|
||||||
|
// loaded: (ctx, position) => (<p className={`tab_ctx_area tab_ctx_loaded pos_${position}`}>{ctx}</p>),
|
||||||
|
// final: (ctx, position) => (<p className={`tab_ctx_area tab_ctx_final pos_${position}`}>{ctx}</p>)
|
||||||
|
default: (ctx, position) => tabCtx(ctx, { className: `tab_ctx_area tab_ctx_default pos_${position}` }),
|
||||||
|
loading: (ctx, position) => tabCtx(loadingCtx, { className: `tab_ctx_area tab_ctx_loading pos_${position}` }),
|
||||||
|
loaded: (ctx, position) => tabCtx(loadedCtx, { className: `tab_ctx_area tab_ctx_loaded pos_${position}` }),
|
||||||
|
final: (ctx, position) => tabCtx(ctx, { className: `tab_ctx_area tab_ctx_final pos_${position}` })
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class InitTabCtx extends PureComponent {
|
||||||
|
|
||||||
|
render () {
|
||||||
|
/**
|
||||||
|
* @param state 当前状态 default: 显示提示信息 init: 加载初始内容 loading: 加载中 loaded: 加载完成 final: 显示最终内容
|
||||||
|
* @param position: start | cetner | end
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const { state, ctx, position = 'start' } = this.props;
|
||||||
|
return(
|
||||||
|
<React.Fragment>
|
||||||
|
{ maps[state](ctx, position) }
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
.tab_ctx_area{
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
&.pos_start{
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
&.pos_center{
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
&.pos_end{
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.ctx_loading,
|
||||||
|
.ctx_loaded{
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
flex-direction: column;
|
||||||
|
top: -20px;
|
||||||
|
color: #1890ff;
|
||||||
|
.ctx_icon{
|
||||||
|
font-size: 40px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* @Description: action类型
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Date: 2019-11-13 20:05:39
|
||||||
|
* @Last Modified by: tangjiang
|
||||||
|
* @Last Modified time: 2019-11-14 09:29:45
|
||||||
|
*/
|
||||||
|
const types = {
|
||||||
|
ADD_TODO: 'ADD_TODO'
|
||||||
|
}
|
||||||
|
|
||||||
|
export default types;
|
@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 全局导出 action 类型
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Date: 2019-11-13 20:12:23
|
||||||
|
* @Last Modified by: tangjiang
|
||||||
|
* @Last Modified time: 2019-11-14 09:55:47
|
||||||
|
*/
|
||||||
|
|
||||||
|
import toggleTodo from './testAction.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
toggleTodo
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
import types from './actionTypes';
|
||||||
|
|
||||||
|
export default function toggleTodo() {
|
||||||
|
return {
|
||||||
|
type: types.ADD_TODO
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 全局导出 reducers
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Date: 2019-11-13 20:12:54
|
||||||
|
* @Last Modified by: tangjiang
|
||||||
|
* @Last Modified time: 2019-11-14 09:55:10
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { combineReducers } from 'redux';
|
||||||
|
import testReducer from './testReducer';
|
||||||
|
|
||||||
|
export default combineReducers({
|
||||||
|
testReducer
|
||||||
|
});
|
@ -0,0 +1,30 @@
|
|||||||
|
import types from '../actions/actionTypes';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
count: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// export default function (state = initialState, action) {
|
||||||
|
// switch (action.type) {
|
||||||
|
// case types.ADD_TODO:
|
||||||
|
// return {
|
||||||
|
// ...state,
|
||||||
|
// count: state.count + 1
|
||||||
|
// };
|
||||||
|
// default:
|
||||||
|
// return state;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
const testReducer = (state = initialState, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case types.ADD_TODO:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
count: state.count + 1
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default testReducer;
|
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 指定容器并绑定 reducers
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Date: 2019-11-13 20:13:21
|
||||||
|
* @Last Modified by: tangjiang
|
||||||
|
* @Last Modified time: 2019-11-14 19:20:44
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createStore, applyMiddleware } from 'redux';
|
||||||
|
import thunk from 'redux-thunk';
|
||||||
|
import rootReducer from '../reducers';
|
||||||
|
|
||||||
|
const configureStore = () => createStore(
|
||||||
|
rootReducer,
|
||||||
|
applyMiddleware(thunk)
|
||||||
|
);
|
||||||
|
|
||||||
|
export default configureStore;
|
Loading…
Reference in new issue