From c2e0a38476de9c65deb4ebaa239136a777192b9c Mon Sep 17 00:00:00 2001 From: LiuChangFreeman Date: Sun, 15 Mar 2020 14:19:48 +0800 Subject: [PATCH 01/15] code refactorings,rename variables --- src/pages/Scenario.js | 46 +++++++++++++++++------------------ src/store/Scenario.js | 56 +++++++++++++++++++++---------------------- src/store/Step.js | 22 +++++++++++------ src/store/Terminal.js | 6 ++--- src/store/index.js | 4 ++-- 5 files changed, 71 insertions(+), 63 deletions(-) diff --git a/src/pages/Scenario.js b/src/pages/Scenario.js index fa29989..e000544 100644 --- a/src/pages/Scenario.js +++ b/src/pages/Scenario.js @@ -18,7 +18,7 @@ function showModal() { class Scenario extends Component { state = { - step_index:0, + stepIndex:0, isDragging: false, loading:false }; @@ -80,13 +80,13 @@ class Scenario extends Component { const store=this.props.store; const index=this.props.match.params.index; const scenario=store.course.scenarios[index]; - const {container_id}=scenario; + const {containerId}=scenario; this.props.form.validateFields( async (error, values) => { if (!error) { this.setState({ loading:true }); - values["containerId"]=container_id; + values["containerId"]=containerId; values["imageFullName"]=`registry.cn-hangzhou.aliyuncs.com/envs/${values["imageFullName"]}`; let url=`http://envmaker.kfcoding.com/api/image/commit`; let response=await fetch(url, { @@ -123,7 +123,7 @@ class Scenario extends Component { }; render() { - const {step_index,isDragging,loading}=this.state; + const {stepIndex,isDragging,loading}=this.state; const {getFieldDecorator} = this.props.form; const store=this.props.store; const index=this.props.match.params.index; @@ -133,15 +133,15 @@ class Scenario extends Component { if (!scenario) { return
} - const step=scenario.steps[step_index]; + const step=scenario.steps[stepIndex]; const environment=scenario.environment; const group=environment.split('/'); const image=group[group.length-1]; - let docker_endpoint=scenario.docker_endpoint===''?scenario.store.docker_endpoint:scenario.docker_endpoint; - let docker_server_version="1.24"; - var matches = docker_endpoint.match(/http:\/\/.+?(?=\/)/mg); + let dockerEndpoint=scenario.docker_endpoint===''?scenario.store.dockerEndpoint:scenario.docker_endpoint; + let dockerServerVersion="1.24"; + var matches = dockerEndpoint.match(/http:\/\/.+?(?=\/)/mg); if (matches && matches.length > 0){ - docker_endpoint=matches[0] + dockerEndpoint=matches[0] } return (
{ - step_index !== scenario.steps.length - 1 &&!compact&& + stepIndex !== scenario.steps.length - 1 &&!compact&&
} { - step_index !== 0 && + stepIndex !== 0 && } { - step_index !== scenario.steps.length - 1 && + stepIndex !== scenario.steps.length - 1 && } { - step_index === scenario.steps.length - 1 &&!compact&& + stepIndex === scenario.steps.length - 1 &&!compact&& } { - step_index === scenario.steps.length - 1 && + stepIndex === scenario.steps.length - 1 && index === store.course.scenarios.length - 1 && showModal() }
- +
{ @@ -369,7 +369,7 @@ class Scenario extends Component { }> { - getFieldDecorator('dockerServerHost',{initialValue: docker_endpoint})( + getFieldDecorator('dockerServerHost',{initialValue: dockerEndpoint})( ) } @@ -384,7 +384,7 @@ class Scenario extends Component { }> { - getFieldDecorator('dockerServerVersion',{initialValue: docker_server_version})( + getFieldDecorator('dockerServerVersion',{initialValue: dockerServerVersion})( ) } diff --git a/src/store/Scenario.js b/src/store/Scenario.js index f010682..f8e6946 100644 --- a/src/store/Scenario.js +++ b/src/store/Scenario.js @@ -24,9 +24,9 @@ export const Scenario = types terminals: types.array(Terminal) }).volatile(self => ({ vscodeUrl:null, - step_index: 0, - container_id: '', - ws_addr: '', + stepIndex: 0, + containerId: '', + wsAddr: '', creating: false, })).views(self => ({ get store() { @@ -43,20 +43,20 @@ export const Scenario = types try { self.setCreated(true); const edit=window.location.search.search("edit=true") !== -1; - const container_mode=edit? {"kfcoding-maker":"true"}: {"kfcoding-auto-delete":"true"}; - const docker_endpoint=self.docker_endpoint===''?self.store.docker_endpoint:self.docker_endpoint; - let exposed_ports={}; - exposed_ports[`${vscodePort}/tcp`]={}; + const containerMode=edit? {"kfcoding-maker":"true"}: {"kfcoding-auto-delete":"true"}; + const dockerEndpoint=self.docker_endpoint===''?self.store.dockerEndpoint:self.docker_endpoint; + let exposedPorts={}; + exposedPorts[`${vscodePort}/tcp`]={}; const steps=self.steps; for(var i=0;i 0){ matches[0]=matches[0].replace('[:',''); - exposed_ports[`${matches[0]}/tcp`]={} + exposedPorts[`${matches[0]}/tcp`]={} } } - let url=`${docker_endpoint}/containers/create`; + let url=`${dockerEndpoint}/containers/create`; let response=yield fetch(url, { headers: { 'Content-Type': 'application/json' @@ -66,13 +66,13 @@ export const Scenario = types body: JSON.stringify({ Image: self.environment, Entrypoint: self.shell, - Labels:container_mode, + Labels:containerMode, AttachStdin: true, AttachStdout: true, AttachStderr: true, Tty: true, OpenStdin: true, - ExposedPorts: exposed_ports, + ExposedPorts: exposedPorts, HostConfig: { Privileged: self.privileged || false, PublishAllPorts: true, @@ -87,10 +87,10 @@ export const Scenario = types console.log("container created:",containerId); self.setContainerId(containerId); self.terminals[0].setContainerId(containerId); - url=`${docker_endpoint}/containers/${containerId}/start`; + url=`${dockerEndpoint}/containers/${containerId}/start`; yield fetch( url, {method: 'POST',mode: 'cors'}); - self.steps[self.step_index].beforeStep(); - url=`ws${docker_endpoint.substr(4)}/containers/${containerId}/attach/ws?logs=1&stream=1&stdin=1&stdout=1&stderr=1`; + self.steps[self.stepIndex].beforeStep(); + url=`ws${dockerEndpoint.substr(4)}/containers/${containerId}/attach/ws?logs=1&stream=1&stdin=1&stdout=1&stderr=1`; let socket = new WebSocket(url); self.terminals[0].terminal.attach(socket, true, true); socket.onopen = () => socket.send("\n"); @@ -110,7 +110,7 @@ export const Scenario = types } } if(scriptExec!==""){ - url=`${docker_endpoint}/containers/${containerId}/exec`; + url=`${dockerEndpoint}/containers/${containerId}/exec`; response=yield fetch( url, { headers: { 'Content-Type': 'application/json' @@ -129,7 +129,7 @@ export const Scenario = types }); data =yield response.json(); const execId=data.Id; - url=`${self.store.docker_endpoint}/exec/${execId}/start`; + url=`${dockerEndpoint}/exec/${execId}/start`; yield fetch(url, { method: 'POST', mode: 'cors', @@ -142,10 +142,10 @@ export const Scenario = types }) }); - url=`${docker_endpoint}/containers/${containerId}/json`; + url=`${dockerEndpoint}/containers/${containerId}/json`; response=yield fetch( url, {method: 'GET',mode:'cors'}); data=yield response.json(); - const host = docker_endpoint.match(/(http:\/\/).+?(?=:)/)[0]; + const host = dockerEndpoint.match(/(http:\/\/).+?(?=:)/)[0]; const vscodeUrlPort=data.NetworkSettings.Ports[`${vscodePort}/tcp`][0].HostPort; const vscodeUrl = `${host}:${vscodeUrlPort}`; let count=0; @@ -153,7 +153,7 @@ export const Scenario = types const show = message.loading("正在启动vscode...",0); const event=setInterval(async function() { try { - url=`${docker_endpoint}/containers/${containerId}/top?ps_args=-a`; + url=`${dockerEndpoint}/containers/${containerId}/top?ps_args=-a`; response=await fetch(url, {method: 'GET',mode:'cors'}); const data=await response.json(); const processes=data["Processes"]; @@ -175,12 +175,12 @@ export const Scenario = types console.log(e); count+=1; } - if(count>15){ + if(count>20){ setTimeout(show, 100); message.error("启动vscode超时"); clearInterval(event); } - }, 2000); + }, 1500); } } else{ @@ -191,7 +191,7 @@ export const Scenario = types const group = self.environment.split(":"); const image = group[0]; const tag = group[1]; - url = `${docker_endpoint}/images/create?fromImage=${image}&tag=${tag}`; + url = `${dockerEndpoint}/images/create?fromImage=${image}&tag=${tag}`; response = yield fetch(url, {method: 'POST', mode: 'cors'}); status = response.status; setTimeout(hide, 100); @@ -215,8 +215,8 @@ export const Scenario = types const removeContainer =flow(function* () { try { - const docker_endpoint=self.docker_endpoint===''?self.store.docker_endpoint:self.docker_endpoint; - let url=`${docker_endpoint}/containers/${self.container_id}?v=true&force=true`; + const dockerEndpoint=self.docker_endpoint===''?self.store.dockerEndpoint:self.docker_endpoint; + let url=`${dockerEndpoint}/containers/${self.containerId}?v=true&force=true`; yield fetch(url, {method: 'DELETE',mode: 'cors'}); } catch (e) { console.log(e) @@ -242,7 +242,7 @@ export const Scenario = types self.description = desc; }, setContainerId(id) { - self.container_id = id; + self.containerId = id; }, addTerminal() { self.terminals.push({}) @@ -251,14 +251,14 @@ export const Scenario = types self.creating = flag }, setWsAddr(addr) { - self.ws_addr = addr; + self.wsAddr = addr; }, setImage(image) { self.environment = image; }, setStepIndex(idx) { - self.step_index = idx; - self.steps[self.step_index].beforeStep(); + self.stepIndex = idx; + self.steps[self.stepIndex].beforeStep(); } } }); diff --git a/src/store/Step.js b/src/store/Step.js index 4990a24..0a05c60 100644 --- a/src/store/Step.js +++ b/src/store/Step.js @@ -33,7 +33,9 @@ export const Step = types } let file = yield self.store.pfs.readFile(`${self.store.dir}/${self.check}`); let script = file.toString(); - let url=`${self.store.docker_endpoint}/containers/${getParent(self, 2).container_id }/exec`; + const docker_endpoint=getParent(self, 2).docker_endpoint; + const dockerEndpoint=docker_endpoint===''?self.store.dockerEndpoint:docker_endpoint; + let url=`${dockerEndpoint}/containers/${getParent(self, 2).containerId }/exec`; let response=yield fetch( url,{ headers: { 'Content-Type': 'application/json' @@ -51,7 +53,7 @@ export const Step = types }) }); let data=yield response.json(); - url=`${self.store.docker_endpoint}/exec/${data.Id}/start`; + url=`${dockerEndpoint}/exec/${data.Id}/start`; response=yield fetch( url, { method: 'POST', headers: { @@ -68,7 +70,9 @@ export const Step = types }); const getHostPort = flow(function* () { - let url=`${self.store.docker_endpoint}/containers/${getParent(self, 2).container_id}/json`; + const docker_endpoint=getParent(self, 2).docker_endpoint; + const dockerEndpoint=docker_endpoint===''?self.store.dockerEndpoint:docker_endpoint; + let url=`${dockerEndpoint}/containers/${getParent(self, 2).containerId}/json`; let response=yield fetch( url, {method: 'GET',mode:'cors'}); let data=yield response.json(); return data.NetworkSettings.Ports; @@ -78,8 +82,10 @@ export const Step = types const extraTab = self.extraTab; const scenario=getParent(self, 2); const path = extraTab.substr(extraTab.indexOf('/')); - const host = self.store.docker_endpoint.match(/(http:\/\/).+?(?=:)/)[0]; - const stepIndex=scenario.step_index; + const docker_endpoint=getParent(self, 2).docker_endpoint; + const dockerEndpoint=docker_endpoint===''?self.store.dockerEndpoint:docker_endpoint; + const host = dockerEndpoint.match(/(http:\/\/).+?(?=:)/)[0]; + const stepIndex=scenario.stepIndex; var matches = extraTab.match(/\[(.+?)]/mg); if (matches && matches.length > 0) { if (matches[0] === "[domain]") { @@ -111,7 +117,9 @@ export const Step = types if (self.program) { let file = yield self.store.pfs.readFile(`${self.store.dir}/${self.program}`); let script = file.toString(); - let url=`${self.store.docker_endpoint}/containers/${getParent(self, 2).container_id}/exec`; + const docker_endpoint=getParent(self, 2).docker_endpoint; + const dockerEndpoint=docker_endpoint===''?self.store.dockerEndpoint:docker_endpoint; + let url=`${dockerEndpoint}/containers/${getParent(self, 2).containerId}/exec`; let response = yield fetch( url, { headers: { 'Content-Type': 'application/json' @@ -129,7 +137,7 @@ export const Step = types }) }); let data =yield response.json(); - url=`${self.store.docker_endpoint}/exec/${data.Id}/start`; + url=`${dockerEndpoint}/exec/${data.Id}/start`; yield fetch(url, { method: 'POST', mode: 'cors', diff --git a/src/store/Terminal.js b/src/store/Terminal.js index fa36f0e..09b99cb 100644 --- a/src/store/Terminal.js +++ b/src/store/Terminal.js @@ -10,7 +10,7 @@ export const Terminal = types .model('Terminal', { id: types.optional(types.identifier, new Date().getTime() + ''), name: 'tt', - container_id: '', + containerId: '', }).volatile(self => ({ terminal: {} })).views(self => ({ @@ -39,9 +39,9 @@ export const Terminal = types return { afterCreate, - setContainerId: id => self.container_id = id, + setContainerId: id => self.containerId = id, resize: (w, h) => { - const url=`${getRoot(self).docker_endpoint}/containers/${self.container_id}/resize?h=${h}&w=${w}`; + const url=`${getRoot(self).dockerEndpoint}/containers/${self.containerId}/resize?h=${h}&w=${w}`; return fetch(url, { method: 'POST',mode: 'cors' }) diff --git a/src/store/index.js b/src/store/index.js index 76e6846..6fc0335 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -6,11 +6,11 @@ import {message} from 'antd'; import {Course} from "./Course"; import {Project} from "./Project"; -let docker_eps = window._env_.DOCKER_ENDPOINT.split(','); +let dockerEndpoints = window._env_.DOCKER_ENDPOINT.split(','); export const Store = types.model('Store', { repo: window.location.hash.substr(1), - docker_endpoint: docker_eps[Math.floor(Math.random()*docker_eps.length)], + dockerEndpoint: dockerEndpoints[Math.floor(Math.random()*dockerEndpoints.length)], course: types.optional(Course, {}), project:types.optional(Project, {}), }).volatile(self => ({ From 6f76e73cc16c9cf0c7add7889a58370df32ffc0a Mon Sep 17 00:00:00 2001 From: LiuChangFreeman Date: Sun, 15 Mar 2020 14:31:40 +0800 Subject: [PATCH 02/15] sync from github --- .github/CODEOWNERS | 7 +++ .github/ISSUE_TEMPLATE/Bug_report.md | 68 ++++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/Proposal.md | 29 ++++++++++++ .github/ISSUE_TEMPLATE/Question.md | 12 +++++ .github/PULL_REQUEST_TEMPLATE.md | 44 ++++++++++++++++++ .travis/deploy.sh | 7 +++ yarn.lock | 61 ++++--------------------- 7 files changed, 176 insertions(+), 52 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/Bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/Proposal.md create mode 100644 .github/ISSUE_TEMPLATE/Question.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .travis/deploy.sh diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..07c194e --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,7 @@ +# This is a comment. +# Each line is a file pattern followed by one or more owners. + +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# @X-Lab will be requested for review when someone opens +# a pull request. diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md new file mode 100644 index 0000000..4b91db9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -0,0 +1,68 @@ +--- +name: Bug report +about: Create a report to help us improve Gitcourse +labels: 'issue: bug report, needs triage' +--- + + + +### Describe the bug + +(Write your answer here.) + +### Did you try recovering your dependencies? + + + +(Write your answer here.) + + +### Environment + + +(paste the output of the command here.) + +### Steps to reproduce + + + +(Write your steps here:) + +1. +2. +3. + +### Expected behavior + + + + +(Write what you thought would happen.) + +### Actual behavior + + + +(Write what happened. It is better to add screenshots!) + +### Reproducible demo + + + +(Paste the link to an example project and exact instructions to reproduce the issue.) + +### Failure Logs + + +(Please include any relevant log snippets or files here.) \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/Proposal.md b/.github/ISSUE_TEMPLATE/Proposal.md new file mode 100644 index 0000000..38bfb0b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Proposal.md @@ -0,0 +1,29 @@ +--- +name: Proposal +about: Suggest an idea for improving Gitcourse +labels: 'issue: proposal, needs triage' +--- + +### Is your proposal related to a problem? + + + +(Write your answer here.) + +### Describe the solution you'd like + + + +(Describe your proposed solution here.) + +### Describe alternatives you've considered + + + +(Write your answer here.) + +### Additional context + + + +(Write your answer here.) \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/Question.md b/.github/ISSUE_TEMPLATE/Question.md new file mode 100644 index 0000000..0b590b7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Question.md @@ -0,0 +1,12 @@ +--- +name: Question +about: Get help with Gitcourse +labels: 'needs triage' +--- + +If you have some questions about Gitcourse, we encourage you to ask the questions in the most related issue. If you can't find the related issue, you can open a new issue or post on our [slack workspace](https://app.slack.com/client/TPD8HAVMW/CPFELS1AA) . The maintainers and other community members can provide help and answer your questions. + + +If you've discovered a bug or would like to propose a change please use one of the other issue templates. + +Thanks a lot! \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..75b64a5 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,44 @@ +# Pull Request Template + +Thank you for your contribution to the gitcourse repo. +Before submitting this PR, please make sure: + +- [ ] Your code builds clean without any errors or warnings +- [ ] You are using approved terminology +- [ ] You have added unit tests + +## Description + +Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. + +Eg. Fixes # (issue) + +## Type of change + +Please delete options that are not relevant. + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +## How has this been tested? + +Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. + + +**Instructions**: + + +**Test Configuration**: + + +## Checklist +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes +- [ ] Any dependent changes have been merged and published in downstream modules \ No newline at end of file diff --git a/.travis/deploy.sh b/.travis/deploy.sh new file mode 100644 index 0000000..389da59 --- /dev/null +++ b/.travis/deploy.sh @@ -0,0 +1,7 @@ +docker build -t $DOCKER_IMAGE . + +echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin + +docker push $DOCKER_IMAGE + +curl $UPDATE_HOOK \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 0c01392..7a2d18c 100755 --- a/yarn.lock +++ b/yarn.lock @@ -1402,11 +1402,6 @@ "@svgr/plugin-svgo" "^4.0.3" loader-utils "^1.1.0" -"@types/anymatch@*": - version "1.3.1" - resolved "https://registry.npm.taobao.org/@types/anymatch/download/@types/anymatch-1.3.1.tgz?cache=0&sync_timestamp=1567532695720&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fanymatch%2Fdownload%2F%40types%2Fanymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" - integrity sha1-M2utwb7sudrMOL6izzKt9ieoQho= - "@types/events@*": version "3.0.0" resolved "https://registry.npm.taobao.org/@types/events/download/@types/events-3.0.0.tgz?cache=0&sync_timestamp=1567532284800&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fevents%2Fdownload%2F%40types%2Fevents-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" @@ -1464,28 +1459,11 @@ "@types/prop-types" "*" csstype "^2.2.0" -"@types/source-list-map@*": - version "0.1.2" - resolved "https://registry.npm.taobao.org/@types/source-list-map/download/@types/source-list-map-0.1.2.tgz?cache=0&sync_timestamp=1567534417883&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fsource-list-map%2Fdownload%2F%40types%2Fsource-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" - integrity sha1-AHiDYGP/rxdBI0m7o2QIfgrALsk= - -"@types/tapable@*": - version "1.0.4" - resolved "https://registry.npm.taobao.org/@types/tapable/download/@types/tapable-1.0.4.tgz?cache=0&sync_timestamp=1567534829650&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Ftapable%2Fdownload%2F%40types%2Ftapable-1.0.4.tgz#b4ffc7dc97b498c969b360a41eee247f82616370" - integrity sha1-tP/H3Je0mMlps2CkHu4kf4JhY3A= - "@types/tapable@1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.2.tgz#e13182e1b69871a422d7863e11a4a6f5b814a4bd" integrity sha512-42zEJkBpNfMEAvWR5WlwtTH22oDzcMjFsL9gDGExwF8X8WvAiw7Vwop7hPw03QT8TKfec83LwbHj6SvpqM4ELQ== -"@types/uglify-js@*": - version "3.0.4" - resolved "https://registry.npm.taobao.org/@types/uglify-js/download/@types/uglify-js-3.0.4.tgz?cache=0&sync_timestamp=1567534690713&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fuglify-js%2Fdownload%2F%40types%2Fuglify-js-3.0.4.tgz#96beae23df6f561862a830b4288a49e86baac082" - integrity sha1-lr6uI99vVhhiqDC0KIpJ6GuqwII= - dependencies: - source-map "^0.6.1" - "@types/unist@*", "@types/unist@^2.0.0": version "2.0.2" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.2.tgz#5dc0a7f76809b7518c0df58689cd16a19bd751c6" @@ -1508,27 +1486,6 @@ "@types/unist" "*" "@types/vfile-message" "*" -"@types/webpack-sources@*": - version "0.1.5" - resolved "https://registry.npm.taobao.org/@types/webpack-sources/download/@types/webpack-sources-0.1.5.tgz?cache=0&sync_timestamp=1567534696188&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fwebpack-sources%2Fdownload%2F%40types%2Fwebpack-sources-0.1.5.tgz#be47c10f783d3d6efe1471ff7f042611bd464a92" - integrity sha1-vkfBD3g9PW7+FHH/fwQmEb1GSpI= - dependencies: - "@types/node" "*" - "@types/source-list-map" "*" - source-map "^0.6.1" - -"@types/webpack@^4.4.19": - version "4.39.5" - resolved "https://registry.npm.taobao.org/@types/webpack/download/@types/webpack-4.39.5.tgz#3671b65928d9e0c6fcd4adff3f8167d48b174681" - integrity sha1-NnG2WSjZ4Mb81K3/P4Fn1IsXRoE= - dependencies: - "@types/anymatch" "*" - "@types/node" "*" - "@types/tapable" "*" - "@types/uglify-js" "*" - "@types/webpack-sources" "*" - source-map "^0.6.0" - "@webassemblyjs/ast@1.7.11": version "1.7.11" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.11.tgz#b988582cafbb2b095e8b556526f30c90d057cace" @@ -8468,17 +8425,17 @@ moment@2.x, moment@^2.24.0: resolved "http://registry.npm.taobao.org/moment/download/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" integrity sha1-DQVdU/UFKqZTyfbraLtdEr9cK1s= -monaco-editor-webpack-plugin@^1.7.0: - version "1.7.0" - resolved "https://registry.npm.taobao.org/monaco-editor-webpack-plugin/download/monaco-editor-webpack-plugin-1.7.0.tgz#920cbeecca25f15d70d568a7e11b0ba4daf1ae83" - integrity sha1-kgy+7Mol8V1w1Win4RsLpNrxroM= +monaco-editor-webpack-plugin@^1.8.1: + version "1.9.0" + resolved "https://registry.npm.taobao.org/monaco-editor-webpack-plugin/download/monaco-editor-webpack-plugin-1.9.0.tgz#5b547281b9f404057dc5d8c5722390df9ac90be6" + integrity sha1-W1Rygbn0BAV9xdjFciOQ35rJC+Y= dependencies: - "@types/webpack" "^4.4.19" + loader-utils "^1.2.3" -monaco-editor@^0.18.1: - version "0.18.1" - resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.18.1.tgz#ced7c305a23109875feeaf395a504b91f6358cfc" - integrity sha512-fmL+RFZ2Hrezy+X/5ZczQW51LUmvzfcqOurnkCIRFTyjdVjzR7JvENzI6+VKBJzJdPh6EYL4RoWl92b2Hrk9fw== +monaco-editor@^0.19.0: + version "0.19.3" + resolved "https://registry.npm.taobao.org/monaco-editor/download/monaco-editor-0.19.3.tgz#1c994b3186c00650dbcd034d5370d46bf56c0663" + integrity sha1-HJlLMYbABlDbzQNNU3DUa/VsBmM= moo@^0.4.3: version "0.4.3" From 85d9b11b4a4308908c0911395f4bffd8239b7222 Mon Sep 17 00:00:00 2001 From: LiuChangFreeman Date: Fri, 27 Mar 2020 19:02:00 +0800 Subject: [PATCH 03/15] fullscreen && hide guide mode --- package.json | 2 +- src/pages/ImageMaker.js | 238 ++++++++++++++++++++ src/pages/Scenario.js | 478 ++++++++++++---------------------------- src/pages/TrainPanel.js | 84 ++++++- src/store/Course.js | 7 +- src/store/Scenario.js | 8 + yarn.lock | 13 ++ 7 files changed, 485 insertions(+), 345 deletions(-) create mode 100644 src/pages/ImageMaker.js diff --git a/package.json b/package.json index 38bfdbc..18655f6 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "react": "^16.8.6", "react-app-rewired": "^2.1.1", "react-dom": "^16.8.6", + "react-full-screen": "^0.2.4", "react-loading": "^2.0.3", "react-markdown": "^4.0.8", "react-monaco-editor": "^0.31.1", @@ -68,4 +69,3 @@ "webpack-dev-server": "^3.8.0" } } - diff --git a/src/pages/ImageMaker.js b/src/pages/ImageMaker.js new file mode 100644 index 0000000..cffa548 --- /dev/null +++ b/src/pages/ImageMaker.js @@ -0,0 +1,238 @@ +import React, {Component} from 'react'; +import {Button,Icon, notification,Form,Spin,Input,Row, Tooltip} from "antd"; +import {inject, observer} from "mobx-react"; +class ImageMaker extends Component { + state = { + loading:false + }; + + handleSubmit = e => { + e.preventDefault(); + const store=this.props.store; + const index=store.course.index; + const scenario=store.course.scenarios[index]; + const {containerId}=scenario; + this.props.form.validateFields( async (error, values) => { + if (!error) { + this.setState({ + loading:true + }); + values["containerId"]=containerId; + values["imageFullName"]=`registry.cn-hangzhou.aliyuncs.com/envs/${values["imageFullName"]}`; + let url=`http://envmaker.kfcoding.com/api/image/commit`; + let response=await fetch(url, { + headers: {'Content-Type': 'application/json'}, + method: 'POST', + body: JSON.stringify(values), + mode:'cors' + }); + const data=await response.json(); + this.setState({ + loading:false + }); + if("error" in data){ + notification['error']({ + message: '创建镜像失败', + description: data["error"], + }); + } + else{ + const btn = ( + + ); + notification['success']({ + message: '创建镜像成功', + description: `请保存您的镜像名:${data["data"]["image"]}`, + }); + } + } + }); + }; + + render() { + const {loading}=this.state; + const {getFieldDecorator} = this.props.form; + const store=this.props.store; + const index=store.course.index; + const scenario = store.course.scenarios[index]; + if (!scenario) { + return
+ } + const environment=scenario.environment; + const group=environment.split('/'); + const image=group[group.length-1]; + let dockerEndpoint=scenario.docker_endpoint===''?scenario.store.dockerEndpoint:scenario.docker_endpoint; + let dockerServerVersion="1.24"; + var matches = dockerEndpoint.match(/http:\/\/.+?(?=\/)/mg); + if (matches && matches.length > 0){ + dockerEndpoint=matches[0] + } + return ( +
+
+
+ 镜像仓库 +
+ + 镜像仓库服务器  + + + + + }> + { + getFieldDecorator('registryServer', { + initialValue: 'registry.cn-hangzhou.aliyuncs.com', + rules: [{ + required: true, + message: '请输入服务器地址!' + }], + }) + () + } + + + + 镜像仓库的账号  + + + + + }> + { + getFieldDecorator('registryAccount', { + rules: [{ + required: true, + message: '请输入账号!' + }], + }) + () + } + + + + + 镜像仓库的密码  + + + + + }> + { + getFieldDecorator('registryPassword', { + rules: [{ + required: true, + message: '请输入密码!' + }], + }) + () + } + + + +
+ Docker服务器 +
+ + Docker服务器地址  + + + + + }> + { + getFieldDecorator('dockerServerHost',{initialValue: dockerEndpoint})( + + ) + } + + + + Docker服务器版本  + + + + + }> + { + getFieldDecorator('dockerServerVersion',{initialValue: dockerServerVersion})( + + ) + } + + +
+ 创建新镜像 +
+ + 新镜像的名称  + + + + + }> + { + getFieldDecorator('imageFullName', { + rules: [{ + required: true, + message: '请输入简称!' + }], + initialValue:image + }) + () + } + + + + + { + loading? + (): + ( + + ) + } + + +
+
+ ) + } +} +const ImageMakerWithForm=Form.create()(ImageMaker); +export default inject('store')(observer(ImageMakerWithForm)); \ No newline at end of file diff --git a/src/pages/Scenario.js b/src/pages/Scenario.js index e000544..4b5387b 100644 --- a/src/pages/Scenario.js +++ b/src/pages/Scenario.js @@ -1,9 +1,10 @@ import React, {Component} from 'react'; import {inject, observer} from "mobx-react"; +import {Button,Icon, notification, Modal} from "antd"; import SplitPane from "react-split-pane"; +import Fullscreen from "react-full-screen"; import Step from "./Step"; -import {Button,message,Icon, notification, Modal,Form,Spin,Input,Row, Tooltip} from "antd"; -import {Tabs} from "antd/lib/tabs"; +import ImageMakerWithForm from "./ImageMaker"; import TrainPanel from "./TrainPanel"; notification.config({ duration: 0, @@ -20,7 +21,9 @@ class Scenario extends Component { state = { stepIndex:0, isDragging: false, - loading:false + loading:false, + isFull: false, + firstPaneSize: 450, }; componentWillUpdate() { @@ -75,369 +78,176 @@ class Scenario extends Component { scenario.afterCreate(); }; - handleSubmit = e => { - e.preventDefault(); + handlePaneSizeChange = currentPaneSize => { const store=this.props.store; const index=this.props.match.params.index; const scenario=store.course.scenarios[index]; - const {containerId}=scenario; - this.props.form.validateFields( async (error, values) => { - if (!error) { - this.setState({ - loading:true - }); - values["containerId"]=containerId; - values["imageFullName"]=`registry.cn-hangzhou.aliyuncs.com/envs/${values["imageFullName"]}`; - let url=`http://envmaker.kfcoding.com/api/image/commit`; - let response=await fetch(url, { - headers: {'Content-Type': 'application/json'}, - method: 'POST', - body: JSON.stringify(values), - mode:'cors' - }); - const data=await response.json(); - this.setState({ - loading:false - }); - if("error" in data){ - notification['error']({ - message: '创建镜像失败', - description: data["error"], - }); - } - else{ - const btn = ( - - ); - notification['success']({ - message: '创建镜像成功', - description: `请保存您的镜像名:${data["data"]["image"]}`, - }); - } - } - }); + const showGuide=scenario.showGuide; + if(showGuide){ + this.setState({ + firstPaneSize: currentPaneSize + }); + } }; render() { - const {stepIndex,isDragging,loading}=this.state; - const {getFieldDecorator} = this.props.form; - const store=this.props.store; - const index=this.props.match.params.index; const edit=window.location.search.search("edit=true") !== -1; const compact=window.location.search.search("compact=true") !== -1; + const store=this.props.store; + const index=this.props.match.params.index; const scenario = store.course.scenarios[index]; + store.course.setIndex(index); if (!scenario) { return
} + const {stepIndex,isDragging,firstPaneSize}=this.state; const step=scenario.steps[stepIndex]; - const environment=scenario.environment; - const group=environment.split('/'); - const image=group[group.length-1]; - let dockerEndpoint=scenario.docker_endpoint===''?scenario.store.dockerEndpoint:scenario.docker_endpoint; - let dockerServerVersion="1.24"; - var matches = dockerEndpoint.match(/http:\/\/.+?(?=\/)/mg); - if (matches && matches.length > 0){ - dockerEndpoint=matches[0] - } return ( - scenario.setIsFull(isFull)} > - { - this.setState({ - isDragging: true, - }); - }} - onDragFinished={() => { - this.setState({ - isDragging: false, - }); - }} +
-
-
- {scenario.title} -
- -
- { - stepIndex !== scenario.steps.length - 1 &&!compact&& -
- -
- } - { - stepIndex !== 0 && - - } - { - stepIndex !== scenario.steps.length - 1 && -
+ +
+ { + stepIndex !== scenario.steps.length - 1 &&!compact&& +
+ +
} - else{ - step.checkStep().then(data => { - if (data === true){ + { + stepIndex !== 0 && + + } + { + stepIndex !== scenario.steps.length - 1 && + } - }}> - - 下一步 - - } - { - stepIndex === scenario.steps.length - 1 &&!compact&& - - } - { - stepIndex === scenario.steps.length - 1 && - index === store.course.scenarios.length - 1 && - showModal() - } -
-
-
- -
- - { - edit&& -
-
-
- 镜像仓库 -
- - 镜像仓库服务器  - - - - - }> { - getFieldDecorator('registryServer', { - initialValue: 'registry.cn-hangzhou.aliyuncs.com', - rules: [{ - required: true, - message: '请输入服务器地址!' - }], - }) - () + stepIndex === scenario.steps.length - 1 &&!compact&& + } - - - - 镜像仓库的账号  - - - - - }> { - getFieldDecorator('registryAccount', { - rules: [{ - required: true, - message: '请输入账号!' - }], - }) - () + stepIndex === scenario.steps.length - 1 && + index === store.course.scenarios.length - 1 && + showModal() } - - - - - 镜像仓库的密码  - - - - - }> - { - getFieldDecorator('registryPassword', { - rules: [{ - required: true, - message: '请输入密码!' - }], - }) - () - } - - - -
- Docker服务器 +
- - Docker服务器地址  - - - - - }> - { - getFieldDecorator('dockerServerHost',{initialValue: dockerEndpoint})( - - ) - } - - - - Docker服务器版本  - - - - - }> - { - getFieldDecorator('dockerServerVersion',{initialValue: dockerServerVersion})( - - ) - } - - -
- 创建新镜像 +
+
- - 新镜像的名称  - - - - - }> - { - getFieldDecorator('imageFullName', { - rules: [{ - required: true, - message: '请输入简称!' - }], - initialValue:image - }) - () - } - - - - - { - loading? - (): - ( - - ) - } - - - -
- } - + + { + edit&& + + } + +
+ ) } } -const ScenarioWithForm=Form.create()(Scenario); -export default inject('store')(observer(ScenarioWithForm)); +export default inject('store')(observer(Scenario)); diff --git a/src/pages/TrainPanel.js b/src/pages/TrainPanel.js index ba44523..11649ec 100644 --- a/src/pages/TrainPanel.js +++ b/src/pages/TrainPanel.js @@ -1,7 +1,7 @@ import React from 'react'; +import {inject, observer} from "mobx-react"; import {Icon, Tabs} from "antd"; import Term from "./Term"; -import {inject, observer} from "mobx-react"; class TrainPanel extends React.Component { state={ @@ -10,9 +10,24 @@ class TrainPanel extends React.Component { }; tabClickHandler = (params) => { - this.setState({ - defaultActiveKey:params - }) + const scenario = this.props.scenario; + if("fullscreen"===params){ + scenario.setIsFull(true); + } + else if("fullscreen-exit"===params){ + scenario.setIsFull(false); + } + else if("menu-unfold"===params){ + scenario.setShowGuide(true); + } + else if("menu-fold"===params){ + scenario.setShowGuide(false); + } + else{ + this.setState({ + defaultActiveKey:params + }) + } }; componentWillUpdate() { @@ -38,12 +53,13 @@ class TrainPanel extends React.Component { render() { let scenario = this.props.scenario; + const isFull=scenario.isFull; + const showGuide=scenario.showGuide; let current = this.props.step; const step=scenario.steps[current]; const {defaultActiveKey}=this.state; return ( this.tabClickHandler(params)} > @@ -56,7 +72,7 @@ class TrainPanel extends React.Component { } key={'term'} closable='false' - style={{height: '100%'}} + style={{height: '100vh'}} forceRender={true} > @@ -73,7 +89,7 @@ class TrainPanel extends React.Component { key={'desktop'} closable='false' > -