Commit f77d1327 by zhanghaozhe

ml小课学习页-小程序

parent f3398a90
...@@ -1847,9 +1847,9 @@ ...@@ -1847,9 +1847,9 @@
} }
}, },
"ace-builds": { "ace-builds": {
"version": "1.4.7", "version": "1.4.11",
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.7.tgz", "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.11.tgz",
"integrity": "sha512-gwQGVFewBopRLho08BfahyvRa9FlB43JUig5ItAKTYc9kJJsbA9QNz75p28QtQomoPQ9rJx82ymL21x4ZSZmdg==" "integrity": "sha512-keACH1d7MvAh72fE/us36WQzOFQPJbHphNpj33pXwVZOM84pTWcdFzIAvngxOGIGLTm7gtUP2eJ4Ku6VaPo8bw=="
}, },
"acorn": { "acorn": {
"version": "5.7.3", "version": "5.7.3",
...@@ -15818,6 +15818,11 @@ ...@@ -15818,6 +15818,11 @@
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
}, },
"xterm": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/xterm/-/xterm-4.5.0.tgz",
"integrity": "sha512-4t12tsvtYnv13FBJwewddxdI/j4kSonmbQQv50j34R/rPIFbUNGtptbprmuUlTDAKvHLMDZ/Np2XcpNimga/HQ=="
},
"y18n": { "y18n": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
"@babel/runtime": "^7.7.7", "@babel/runtime": "^7.7.7",
"@loadable/component": "^5.10.1", "@loadable/component": "^5.10.1",
"@svgr/webpack": "4.1.0", "@svgr/webpack": "4.1.0",
"ace-builds": "^1.4.11",
"antd-mobile": "^2.3.1", "antd-mobile": "^2.3.1",
"autoprefixer": "^9.6.0", "autoprefixer": "^9.6.0",
"axios": "^0.19.0", "axios": "^0.19.0",
...@@ -90,7 +91,8 @@ ...@@ -90,7 +91,8 @@
"webpack": "4.28.3", "webpack": "4.28.3",
"webpack-dev-server": "3.1.14", "webpack-dev-server": "3.1.14",
"webpack-manifest-plugin": "2.0.4", "webpack-manifest-plugin": "2.0.4",
"workbox-webpack-plugin": "3.6.3" "workbox-webpack-plugin": "3.6.3",
"xterm": "^4.5.0"
}, },
"scripts": { "scripts": {
"start": "node scripts/start.js", "start": "node scripts/start.js",
......
...@@ -360,6 +360,9 @@ class App extends Component { ...@@ -360,6 +360,9 @@ class App extends Component {
alt=""/> alt=""/>
</div> </div>
} }
<div style={{backgroundColor: '#fff', color: '#000', position: 'fixed', bottom: '50px', left: '0'}}>
<Link to={'/interactive-study'}>编辑器</Link>
</div>
</> </>
} }
} }
......
import React, { Component } from 'react';
import './index.scss'
class Container extends Component {
render() {
const {user, content} = this.props
return (
<div className={'container'}>
{
user &&
<div className="user">
<img className={'avatar'} src={user} alt=""/>
</div>
}
<div className="divide"></div>
<div className="content">{content}</div>
</div>
);
}
}
export default Container;
\ No newline at end of file
.container {
display: flex;
padding-right: 10px;
margin-bottom: 21.5px;
.user {
width: 39px;
flex: 0 0 auto;
padding-left: 9px;
.avatar {
width: 22px;
height: 22px;
border-radius: 50%;
}
}
.divide {
flex: 0 0 auto;
border-right: 1px solid #FFD667;
//background-color: #FFD667;
opacity: .3;
}
.content {
padding-left: 10.5px;
color: rgba(207, 219, 229, .8);
font-size: 15px;
}
}
\ No newline at end of file
import React, { Component } from 'react';
import './index.scss'
import Container from './container'
import SingleAnswerQuestion from "@components/interactive-study/single-answer-question"
import Program from "@components/interactive-study/program"
import { WithFullSize } from "@/HOCs"
class InteractiveStudy extends Component {
singleIcon = 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/interactive_tutorial/study/single-answer-icon.png'
programIcon = 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/interactive_tutorial/study/program-icon.png'
state = {
options: [
'A. 程序向屏幕输出信息的过程,是人与计算机的单向沟通。',
'B. 程序向内部输入信息的过程。',
'C. 程序向内部输入信息的过程。',
],
img: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/weekend/bigdata/project/07.png'
}
componentDidMount() {
}
render() {
return (
<div id={'interactive-study'}>
<Container user={'https://julyedu-img-public.oss-cn-beijing.aliyuncs.com/Public/Image/home/avatar_20191104.png'}
content={'Hi!初次见面,我叫小柒,是你在七月在线的课程导师。从现在开始,你就要跟着我一起学Python啦,请敲回车键继续课程。'}/>
<SingleAnswerQuestion user={this.singleIcon} topic={'请问,你会推荐哪个物品给用户C?'} options={this.state.options}
img={this.state.img}/>
<Program user={this.programIcon} code={"week = ['星期一', '星期二', '星期三']\nprint(123)"}/>
</div>
);
}
}
export default WithFullSize(InteractiveStudy);
\ No newline at end of file
#interactive-study {
background-color: #252529;
min-height: 100%;
}
\ No newline at end of file
import React, { Component } from 'react';
import './index.scss'
import Container from '../container'
import AceEditor from 'react-ace'
import 'ace-builds/src-min-noconflict/theme-dracula'
import 'ace-builds/src-min-noconflict/mode-python'
import StatusBar from './status-bar'
const {First, Normal} = StatusBar
class Program extends Component {
editor = null
state = {
editorWidth: '',
}
componentDidMount() {
const content = document.querySelector('.container .content')
const contentStyles = window.getComputedStyle(content)
const contentWidth = content.clientWidth
const contentPaddingLeft = parseFloat(contentStyles.getPropertyValue('padding-left'))
this.setState({
editorWidth: `${contentWidth - contentPaddingLeft}px`
})
}
onLoad = editor => {
this.editor = editor
}
render() {
const {editorWidth} = this.state
const {user, code} = this.props
return (
<Container
user={user}
content={
<div className="program">
<AceEditor
name={'ace-editor'}
mode={'python'}
theme={'dracula'}
readOnly={true}
value={code}
width={editorWidth}
height={'141px'}
onLoad={this.onLoad}
/>
<Normal/>
</div>
}
/>
);
}
}
export default Program;
\ No newline at end of file
.program{
background-color: #1f1f24;
padding-top: 12px;
.ace-dracula {
background: rgba(31, 31, 36, 1);
.ace_keyword {
color: rgba(0, 153, 255, 1);
}
.ace_gutter {
background: transparent;
}
.ace_identifier {
color: rgba(0, 207, 230, 1);
}
.ace_string {
color: rgba(219, 88, 131, 1);
}
.ace_marker-layer .ace_active-line {
background: rgba(207, 219, 229, .1);
}
}
.ace_scrollbar {
&.ace_scrollbar-v::-webkit-scrollbar {
width: 6px;
background: transparent;
}
&-v::-webkit-scrollbar-thumb {
height: 50px;
background: rgba(207, 219, 229, .1);
opacity: 0.1;
border-radius: 3px;
}
&.ace_scrollbar-h::-webkit-scrollbar {
height: 6px;
background: transparent;
}
&-h::-webkit-scrollbar-thumb {
width: 8px;
background: rgba(207, 219, 229, .1);
border-radius: 3px;
}
}
.ace_dark.ace_editor.ace_autocomplete {
background: rgba(35, 35, 41, 1);
border-radius: 4px;
}
.ace_dark.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {
background: rgba(0, 153, 255, .1);
}
.ace_dark.ace_editor.ace_autocomplete .ace_marker-layer .ace_line-hover {
background: rgba(0, 153, 255, .1);
border: none;
}
.ace_dark.ace_editor.ace_autocomplete .ace_completion-highlight {
color: #09f;
}
.ace-dracula {
.ace_gutter {
background: transparent;
}
.ace_gutter-active-line {
background: rgba(207, 219, 229, .1);
}
}
}
\ No newline at end of file
import React from 'react';
import './index.scss'
import classnames from "classnames";
const Base = ({children, className}) => {
return (
<div className={classnames(['base-status-bar', className])}>
{children}
</div>
);
};
const First = ({execute}) => {
return <Base className={'first'}>
<div className="hint">当前环境暂不支持编写程序,可前往PC端练习或点击按钮</div>
<button className={'btn'} onClick={execute}>运行并查看结果</button>
</Base>
}
const Normal = ({execute, checkAnswer}) => {
return <Base className={'normal'}>
<button className={'check-answer'} onClick={checkAnswer}>查看参考答案</button>
<button className={'btn'} onClick={execute}>运行</button>
</Base>
}
const Pass = () => {
return <Base className={'pass'}>
<i className={'iconfont icondanseshixintubiao-3'}></i>
<span>运行通过</span>
</Base>
}
export default {
First,
Normal
};
\ No newline at end of file
$primaryColor: rgba(0, 153, 255, .8);
.base-status-bar {
display: flex;
justify-content: space-between;
height: 44px;
padding: 8px 10px;
background: #2B2B33;
color: #CFDBE5;
font-size: 12px;
button {
font-size: 12px;
color: #fff;
background-color: $primaryColor;
border: 0;
border-radius: 4px;
-webkit-appearance: none;
outline: 0;
}
}
.first {
.hint {
width: 169px;
}
.am-button {
font-size: 12px;
}
}
.normal {
justify-content: flex-end;
.check-answer {
margin-right: 18px;
border: 1px solid $primaryColor;
border-radius: 4px;
font-size: 12px;
color: $primaryColor;
background-color: transparent;
}
.btn {
padding-left: 12px;
padding-right: 12px;
}
}
.pass{
justify-content: flex-start;
align-content: center;
color: #74c27c;
.iconfont{
font-size: 18px;
}
}
\ No newline at end of file
import React, { Component } from 'react';
import './index.scss'
import Container from '../container'
import classnames from 'classnames'
class SingleAnswerQuestion extends Component {
state = {
correct: '',
wrong: ''
}
handleSelect = index => {
this.setState({
wrong: index
})
}
render() {
const {correct, wrong} = this.state
const {user, topic, img, options} = this.props
return (
<Container
user={user}
content={
<div className={'single-answer-question'}>
<div className="topic">{topic}</div>
{img && <img src={img} alt=""/>}
<ul>
{
options && !!options.length && options.map((item, index) => {
return <li
className={classnames(['option', {'correct': index === correct, 'wrong': index === wrong}])}
key={index}
onClick={this.handleSelect.bind(this, index)}>{item}</li>
})
}
</ul>
</div>
}
/>
);
}
}
export default SingleAnswerQuestion;
\ No newline at end of file
.single-answer-question {
img {
width: 100%;
margin: 10px 0 5px;
}
.option {
padding: 10px 0 10px 15px;
margin-top: 10px;
background: rgba(46, 46, 51, 1);
border-radius: 4px;
border: 1px solid transparent;
&.correct {
border: 1px solid #74C17C;
}
&.wrong {
border: 1px solid #C24E55;
}
}
}
\ No newline at end of file
...@@ -12,19 +12,19 @@ import App from './App' ...@@ -12,19 +12,19 @@ import App from './App'
const reduxDevToolOptions = process.env.REACT_APP_BUILD_ENV === 'production' ? {} : {trace: true, traceLimit: 25} const reduxDevToolOptions = process.env.REACT_APP_BUILD_ENV === 'production' ? {} : {trace: true, traceLimit: 25}
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ && const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__(reduxDevToolOptions) || compose; window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__(reduxDevToolOptions) || compose;
const store = createStore( const store = createStore(
rootReducers, rootReducers,
composeEnhancers( composeEnhancers(
applyMiddleware(thunk, logger) applyMiddleware(thunk, logger)
) )
) )
ReactDOM.render( ReactDOM.render(
<Provider store={store}> <Provider store={store}>
<Router> <Router>
<App/> <App/>
</Router> </Router>
</Provider>, </Provider>,
document.getElementById('root')); document.getElementById('root'));
\ No newline at end of file \ No newline at end of file
...@@ -301,7 +301,7 @@ export default [ ...@@ -301,7 +301,7 @@ export default [
}, },
//限时免费落地页 //限时免费落地页
{ {
path:'/free', path: '/free',
exact: true, exact: true,
component: loadable(() => import(/*limit-free*/'@/components/limit-free')) component: loadable(() => import(/*limit-free*/'@/components/limit-free'))
}, },
...@@ -330,4 +330,9 @@ export default [ ...@@ -330,4 +330,9 @@ export default [
path: '/mlShare', path: '/mlShare',
component: loadable(() => import('@/components/mlShare')) component: loadable(() => import('@/components/mlShare'))
}, },
{
path: '/interactive-study',
component: loadable(() => import(/* webpackChunkName: 'interactive-study' */'@/components/interactive-study'))
}
] ]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment