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