Compare commits

...

No commits in common. "master" and "develop" have entirely different histories.

240 changed files with 54841 additions and 2 deletions

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@ -1,3 +1,68 @@
# h5educoder This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
#小程序项目h5页面开发的仓库 ## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.<br />
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br />
You will also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.<br />
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.<br />
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.<br />
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you cant go back!**
If you arent satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point youre on your own.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
### Analyzing the Bundle Size
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
### Making a Progressive Web App
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
### Advanced Configuration
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
### Deployment
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
### `npm run build` fails to minify
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify

BIN
build.zip Normal file

Binary file not shown.

45
config-overrides.js Normal file
View File

@ -0,0 +1,45 @@
const {
override,
addLessLoader,
disableEsLint,
addBundleVisualizer,
fixBabelImports,
} = require("customize-cra");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin")
const myPlugin = [
new UglifyJsPlugin(
{
uglifyOptions: {
warnings: false,
compress: {
drop_debugger: true,
drop_console: true
}
}
}
)
]
module.exports = override(
disableEsLint(),
process.env.BUNDLE_VISUALIZE == 1 && addBundleVisualizer(),
fixBabelImports('import', {
libraryName: 'antd-mobile',
style: 'css',
}),
addLessLoader({
strictMath: true,
noIeCompat: true
}),
(config) => {
if (process.env.NODE_ENV === "production") config.devtool = false;
if (process.env.NODE_ENV !== "development") config.plugins = [...config.plugins, ...myPlugin]
if (process.env.NODE_ENV !== "development") {
config.output.publicPath = `/h5educoderbuild/`;
}
return config
}
);

34524
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

58
package.json Normal file
View File

@ -0,0 +1,58 @@
{
"name": "h5",
"version": "0.1.0",
"private": true,
"dependencies": {
"@types/marked": "^3.0.2",
"ace-builds": "^1.4.8",
"antd-mobile": "^2.3.1",
"axios": "^0.19.2",
"code-prettify": "^0.1.0",
"dompurify": "^2.0.11",
"katex": "^0.11.1",
"marked": "^1.0.0",
"md5": "^2.2.1",
"react": "^16.12.0",
"react-ace": "^8.1.0",
"react-cookie": "^4.0.3",
"react-cookies": "^0.1.1",
"react-dom": "^16.12.0",
"react-router": "^5.1.2",
"react-router-dom": "^5.1.2",
"js-base64": "2.6.4",
"react-scripts": "3.4.0",
"typescript": "^3.9.10",
"uglifyjs-webpack-plugin": "^2.2.0"
},
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build ",
"test": "react-scripts test",
"eject": "react-scripts eject",
"prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/katex": "^0.11.1",
"babel-plugin-import": "^1.13.0",
"customize-cra": "^0.5.0",
"less": "^3.11.1",
"less-loader": "^5.0.0",
"react-app-rewired": "^2.1.5",
"webpack-bundle-analyzer": "^3.6.0"
}
}

1894
public/css/css_min_all.css Executable file

File diff suppressed because one or more lines are too long

BIN
public/favicon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

34
public/index.html Normal file
View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="theme-color" content="#000000" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<meta name="renderer" content="webkit" />
<meta name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
<meta name=”Keywords” Content=”EduCoder,信息技术实践教学,精品课程网,慕课MOOC″>
<meta name=”Keywords” Content=”实践课程,项目实战,java实训,python实战,人工智能技术,后端开发学习,移动开发入门″>
<meta name=”Keywords” Content=”翻转课堂,高效课堂创建,教学模式″>
<meta name=”Keywords” Content=”实训项目,python教程,C语言入门,java书,php后端开发,app前端开发,数据库技术″>
<meta name=”Keywords” Content=”在线竞赛,计算机应用大赛,编程大赛,大学生计算机设计大赛,全国高校绿色计算机大赛″>
<meta name=”Description”
Content=”EduCoder是信息技术类实践教学平台。EduCoder涵盖了计算机、大数据、云计算、人工智能、软件工程、物联网等专业课程。超10000个实训案例及22000个技能评测点建立学、练、评、测一体化实验环境。”>
<meta name=”Description”
Content=”EduCoder实践课程旨在于通过企业级实战实训案例帮助众多程序员提升各项业务能力。解决学生、学员、企业员工等程序设计能力、算法设计能力、问题求解能力、应用开发能力、系统运维能力等。”>
<meta name=”Description”
Content=”EduCoder翻转课堂教学模式颠覆了传统教学模式让教师与学生的关系由“权威”变成了“伙伴”。将学习的主动权转交给学生使学生可个性化化学学生的学习主体得到了彰显。”>
<meta name=”Description” Content=”EduCoder实训项目为单个知识点关卡实践训练帮助学生巩固单一弱点强化学习。 >
<meta name=”Description” Content=”EduCoder实践教学平台各类大赛为进一步提高各类学生综合运用高级语言程序设计能力培养创新意识和实践探索精神发掘优秀软件人才。 >
<title>EduCoder</title>
<script src="/run_prettify.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>

BIN
public/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
public/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

25
public/manifest.json Normal file
View File

@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
public/robots.txt Normal file
View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

64
public/run_prettify.js Normal file
View File

@ -0,0 +1,64 @@
!function(){/*
Copyright (C) 2013 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Copyright (C) 2006 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function(){function aa(g){function r(){try{L.doScroll("left")}catch(ba){k.setTimeout(r,50);return}x("poll")}function x(r){if("readystatechange"!=r.type||"complete"==z.readyState)("load"==r.type?k:z)[B](n+r.type,x,!1),!l&&(l=!0)&&g.call(k,r.type||r)}var X=z.addEventListener,l=!1,E=!0,v=X?"addEventListener":"attachEvent",B=X?"removeEventListener":"detachEvent",n=X?"":"on";if("complete"==z.readyState)g.call(k,"lazy");else{if(z.createEventObject&&L.doScroll){try{E=!k.frameElement}catch(ba){}E&&r()}z[v](n+
"DOMContentLoaded",x,!1);z[v](n+"readystatechange",x,!1);k[v](n+"load",x,!1)}}function T(){U&&aa(function(){var g=M.length;ca(g?function(){for(var r=0;r<g;++r)(function(g){k.setTimeout(function(){k.exports[M[g]].apply(k,arguments)},0)})(r)}:void 0)})}for(var k=window,z=document,L=z.documentElement,N=z.head||z.getElementsByTagName("head")[0]||L,B="",F=z.getElementsByTagName("script"),l=F.length;0<=--l;){var O=F[l],Y=O.src.match(/^[^?#]*\/run_prettify\.js(\?[^#]*)?(?:#.*)?$/);if(Y){B=Y[1]||"";O.parentNode.removeChild(O);
break}}var U=!0,H=[],P=[],M=[];B.replace(/[?&]([^&=]+)=([^&]+)/g,function(g,r,x){x=decodeURIComponent(x);r=decodeURIComponent(r);"autorun"==r?U=!/^[0fn]/i.test(x):"lang"==r?H.push(x):"skin"==r?P.push(x):"callback"==r&&M.push(x)});l=0;for(B=H.length;l<B;++l)(function(){var g=z.createElement("script");g.onload=g.onerror=g.onreadystatechange=function(){!g||g.readyState&&!/loaded|complete/.test(g.readyState)||(g.onerror=g.onload=g.onreadystatechange=null,--S,S||k.setTimeout(T,0),g.parentNode&&g.parentNode.removeChild(g),
g=null)};g.type="text/javascript";g.src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/lang-"+encodeURIComponent(H[l])+".js";N.insertBefore(g,N.firstChild)})(H[l]);for(var S=H.length,F=[],l=0,B=P.length;l<B;++l)F.push("https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/skins/"+encodeURIComponent(P[l])+".css");F.push("https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/prettify.css");(function(g){function r(l){if(l!==x){var k=z.createElement("link");k.rel="stylesheet";
k.type="text/css";l+1<x&&(k.error=k.onerror=function(){r(l+1)});k.href=g[l];N.appendChild(k)}}var x=g.length;r(0)})(F);var ca=function(){"undefined"!==typeof window&&(window.PR_SHOULD_USE_CONTINUATION=!0);var g;(function(){function r(a){function d(e){var a=e.charCodeAt(0);if(92!==a)return a;var c=e.charAt(1);return(a=k[c])?a:"0"<=c&&"7">=c?parseInt(e.substring(1),8):"u"===c||"x"===c?parseInt(e.substring(2),16):e.charCodeAt(1)}function f(e){if(32>e)return(16>e?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);
return"\\"===e||"-"===e||"]"===e||"^"===e?"\\"+e:e}function c(e){var c=e.substring(1,e.length-1).match(RegExp("\\\\u[0-9A-Fa-f]{4}|\\\\x[0-9A-Fa-f]{2}|\\\\[0-3][0-7]{0,2}|\\\\[0-7]{1,2}|\\\\[\\s\\S]|-|[^-\\\\]","g"));e=[];var a="^"===c[0],b=["["];a&&b.push("^");for(var a=a?1:0,h=c.length;a<h;++a){var m=c[a];if(/\\[bdsw]/i.test(m))b.push(m);else{var m=d(m),p;a+2<h&&"-"===c[a+1]?(p=d(c[a+2]),a+=2):p=m;e.push([m,p]);65>p||122<m||(65>p||90<m||e.push([Math.max(65,m)|32,Math.min(p,90)|32]),97>p||122<m||
e.push([Math.max(97,m)&-33,Math.min(p,122)&-33]))}}e.sort(function(e,a){return e[0]-a[0]||a[1]-e[1]});c=[];h=[];for(a=0;a<e.length;++a)m=e[a],m[0]<=h[1]+1?h[1]=Math.max(h[1],m[1]):c.push(h=m);for(a=0;a<c.length;++a)m=c[a],b.push(f(m[0])),m[1]>m[0]&&(m[1]+1>m[0]&&b.push("-"),b.push(f(m[1])));b.push("]");return b.join("")}function g(e){for(var a=e.source.match(RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)",
"g")),b=a.length,d=[],h=0,m=0;h<b;++h){var p=a[h];"("===p?++m:"\\"===p.charAt(0)&&(p=+p.substring(1))&&(p<=m?d[p]=-1:a[h]=f(p))}for(h=1;h<d.length;++h)-1===d[h]&&(d[h]=++r);for(m=h=0;h<b;++h)p=a[h],"("===p?(++m,d[m]||(a[h]="(?:")):"\\"===p.charAt(0)&&(p=+p.substring(1))&&p<=m&&(a[h]="\\"+d[p]);for(h=0;h<b;++h)"^"===a[h]&&"^"!==a[h+1]&&(a[h]="");if(e.ignoreCase&&A)for(h=0;h<b;++h)p=a[h],e=p.charAt(0),2<=p.length&&"["===e?a[h]=c(p):"\\"!==e&&(a[h]=p.replace(/[a-zA-Z]/g,function(a){a=a.charCodeAt(0);
return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var r=0,A=!1,q=!1,I=0,b=a.length;I<b;++I){var t=a[I];if(t.ignoreCase)q=!0;else if(/[a-z]/i.test(t.source.replace(/\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi,""))){A=!0;q=!1;break}}for(var k={b:8,t:9,n:10,v:11,f:12,r:13},u=[],I=0,b=a.length;I<b;++I){t=a[I];if(t.global||t.multiline)throw Error(""+t);u.push("(?:"+g(t)+")")}return new RegExp(u.join("|"),q?"gi":"g")}function l(a,d){function f(a){var b=a.nodeType;if(1==b){if(!c.test(a.className)){for(b=
a.firstChild;b;b=b.nextSibling)f(b);b=a.nodeName.toLowerCase();if("br"===b||"li"===b)g[q]="\n",A[q<<1]=r++,A[q++<<1|1]=a}}else if(3==b||4==b)b=a.nodeValue,b.length&&(b=d?b.replace(/\r\n?/g,"\n"):b.replace(/[ \t\r\n]+/g," "),g[q]=b,A[q<<1]=r,r+=b.length,A[q++<<1|1]=a)}var c=/(?:^|\s)nocode(?:\s|$)/,g=[],r=0,A=[],q=0;f(a);return{a:g.join("").replace(/\n$/,""),c:A}}function k(a,d,f,c,g){f&&(a={h:a,l:1,j:null,m:null,a:f,c:null,i:d,g:null},c(a),g.push.apply(g,a.g))}function z(a){for(var d=void 0,f=a.firstChild;f;f=
f.nextSibling)var c=f.nodeType,d=1===c?d?a:f:3===c?S.test(f.nodeValue)?a:d:d;return d===a?void 0:d}function E(a,d){function f(a){for(var q=a.i,r=a.h,b=[q,"pln"],t=0,A=a.a.match(g)||[],u={},e=0,l=A.length;e<l;++e){var D=A[e],w=u[D],h=void 0,m;if("string"===typeof w)m=!1;else{var p=c[D.charAt(0)];if(p)h=D.match(p[1]),w=p[0];else{for(m=0;m<n;++m)if(p=d[m],h=D.match(p[1])){w=p[0];break}h||(w="pln")}!(m=5<=w.length&&"lang-"===w.substring(0,5))||h&&"string"===typeof h[1]||(m=!1,w="src");m||(u[D]=w)}p=t;
t+=D.length;if(m){m=h[1];var C=D.indexOf(m),G=C+m.length;h[2]&&(G=D.length-h[2].length,C=G-m.length);w=w.substring(5);k(r,q+p,D.substring(0,C),f,b);k(r,q+p+C,m,F(w,m),b);k(r,q+p+G,D.substring(G),f,b)}else b.push(q+p,w)}a.g=b}var c={},g;(function(){for(var f=a.concat(d),q=[],k={},b=0,t=f.length;b<t;++b){var n=f[b],u=n[3];if(u)for(var e=u.length;0<=--e;)c[u.charAt(e)]=n;n=n[1];u=""+n;k.hasOwnProperty(u)||(q.push(n),k[u]=null)}q.push(/[\0-\uffff]/);g=r(q)})();var n=d.length;return f}function v(a){var d=
[],f=[];a.tripleQuotedStrings?d.push(["str",/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""]):a.multiLineStrings?d.push(["str",/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"]):d.push(["str",/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"]);a.verbatimStrings&&
f.push(["str",/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null]);var c=a.hashComments;c&&(a.cStyleComments?(1<c?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"]),f.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,null])):d.push(["com",/^#[^\r\n]*/,null,"#"]));a.cStyleComments&&(f.push(["com",/^\/\/[^\r\n]*/,null]),f.push(["com",/^\/\*[\s\S]*?(?:\*\/|$)/,
null]));if(c=a.regexLiterals){var g=(c=1<c?"":"\n\r")?".":"[\\S\\s]";f.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+("/(?=[^/*"+c+"])(?:[^/\\x5B\\x5C"+c+"]|\\x5C"+g+"|\\x5B(?:[^\\x5C\\x5D"+c+"]|\\x5C"+g+")*(?:\\x5D|$))+/")+")")])}(c=a.types)&&f.push(["typ",c]);c=(""+a.keywords).replace(/^ | $/g,"");c.length&&f.push(["kwd",
new RegExp("^(?:"+c.replace(/[\s,]+/g,"|")+")\\b"),null]);d.push(["pln",/^\s+/,null," \r\n\t\u00a0"]);c="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(c+="(?!s*/)");f.push(["lit",/^@[a-z_$][a-z_$@0-9]*/i,null],["typ",/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],["pln",/^[a-z_$][a-z_$@0-9]*/i,null],["lit",/^(?:0x[a-f0-9]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+\-]?\d+)?)[a-z]*/i,null,"0123456789"],["pln",/^\\[\s\S]?/,null],["pun",new RegExp(c),null]);return E(d,f)}function B(a,d,f){function c(a){var b=
a.nodeType;if(1==b&&!r.test(a.className))if("br"===a.nodeName.toLowerCase())g(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)c(a);else if((3==b||4==b)&&f){var e=a.nodeValue,d=e.match(n);d&&(b=e.substring(0,d.index),a.nodeValue=b,(e=e.substring(d.index+d[0].length))&&a.parentNode.insertBefore(q.createTextNode(e),a.nextSibling),g(a),b||a.parentNode.removeChild(a))}}function g(a){function c(a,b){var e=b?a.cloneNode(!1):a,p=a.parentNode;if(p){var p=c(p,1),d=a.nextSibling;
p.appendChild(e);for(var f=d;f;f=d)d=f.nextSibling,p.appendChild(f)}return e}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;a=c(a.nextSibling,0);for(var e;(e=a.parentNode)&&1===e.nodeType;)a=e;b.push(a)}for(var r=/(?:^|\s)nocode(?:\s|$)/,n=/\r\n?|\n/,q=a.ownerDocument,k=q.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var b=[k],t=0;t<b.length;++t)c(b[t]);d===(d|0)&&b[0].setAttribute("value",d);var l=q.createElement("ol");l.className="linenums";d=Math.max(0,d-1|0)||0;for(var t=
0,u=b.length;t<u;++t)k=b[t],k.className="L"+(t+d)%10,k.firstChild||k.appendChild(q.createTextNode("\u00a0")),l.appendChild(k);a.appendChild(l)}function n(a,d){for(var f=d.length;0<=--f;){var c=d[f];V.hasOwnProperty(c)?Q.console&&console.warn("cannot override language handler %s",c):V[c]=a}}function F(a,d){a&&V.hasOwnProperty(a)||(a=/^\s*</.test(d)?"default-markup":"default-code");return V[a]}function H(a){var d=a.j;try{var f=l(a.h,a.l),c=f.a;a.a=c;a.c=f.c;a.i=0;F(d,c)(a);var g=/\bMSIE\s(\d+)/.exec(navigator.userAgent),
g=g&&8>=+g[1],d=/\n/g,r=a.a,k=r.length,f=0,q=a.c,n=q.length,c=0,b=a.g,t=b.length,v=0;b[t]=k;var u,e;for(e=u=0;e<t;)b[e]!==b[e+2]?(b[u++]=b[e++],b[u++]=b[e++]):e+=2;t=u;for(e=u=0;e<t;){for(var x=b[e],z=b[e+1],w=e+2;w+2<=t&&b[w+1]===z;)w+=2;b[u++]=x;b[u++]=z;e=w}b.length=u;var h=a.h;a="";h&&(a=h.style.display,h.style.display="none");try{for(;c<n;){var m=q[c+2]||k,p=b[v+2]||k,w=Math.min(m,p),C=q[c+1],G;if(1!==C.nodeType&&(G=r.substring(f,w))){g&&(G=G.replace(d,"\r"));C.nodeValue=G;var Z=C.ownerDocument,
W=Z.createElement("span");W.className=b[v+1];var B=C.parentNode;B.replaceChild(W,C);W.appendChild(C);f<m&&(q[c+1]=C=Z.createTextNode(r.substring(w,m)),B.insertBefore(C,W.nextSibling))}f=w;f>=m&&(c+=2);f>=p&&(v+=2)}}finally{h&&(h.style.display=a)}}catch(y){Q.console&&console.log(y&&y.stack||y)}}var Q="undefined"!==typeof window?window:{},J=["break,continue,do,else,for,if,return,while"],K=[[J,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,restrict,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],R=[K,"alignas,alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,noexcept,noreturn,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],L=[K,"abstract,assert,boolean,byte,extends,finally,final,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"],
M=[K,"abstract,add,alias,as,ascending,async,await,base,bool,by,byte,checked,decimal,delegate,descending,dynamic,event,finally,fixed,foreach,from,get,global,group,implicit,in,interface,internal,into,is,join,let,lock,null,object,out,override,orderby,params,partial,readonly,ref,remove,sbyte,sealed,select,set,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,value,var,virtual,where,yield"],K=[K,"abstract,async,await,constructor,debugger,enum,eval,export,from,function,get,import,implements,instanceof,interface,let,null,of,set,undefined,var,with,yield,Infinity,NaN"],
N=[J,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],O=[J,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],J=[J,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],P=/^(DIR|FILE|array|vector|(de|priority_)?queue|(forward_)?list|stack|(const_)?(reverse_)?iterator|(unordered_)?(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,
S=/\S/,T=v({keywords:[R,M,L,K,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",N,O,J],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),V={};n(T,["default-code"]);n(E([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",
/^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),"default-markup htm html mxml xhtml xml xsl".split(" "));n(E([["pln",/^[\s]+/,null," \t\r\n"],["atv",/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
["pun",/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);n(E([],[["atv",/^[\s\S]+/]]),["uq.val"]);n(v({keywords:R,hashComments:!0,cStyleComments:!0,types:P}),"c cc cpp cxx cyc m".split(" "));n(v({keywords:"null,true,false"}),["json"]);n(v({keywords:M,hashComments:!0,cStyleComments:!0,
verbatimStrings:!0,types:P}),["cs"]);n(v({keywords:L,cStyleComments:!0}),["java"]);n(v({keywords:J,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);n(v({keywords:N,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);n(v({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),
["perl","pl","pm"]);n(v({keywords:O,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);n(v({keywords:K,cStyleComments:!0,regexLiterals:!0}),["javascript","js","ts","typescript"]);n(v({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);n(E([],[["str",/^[\s\S]+/]]),
["regex"]);var U=Q.PR={createSimpleLexer:E,registerLangHandler:n,sourceDecorator:v,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:function(a,d,f){f=f||!1;d=d||null;var c=document.createElement("div");c.innerHTML="<pre>"+a+"</pre>";c=c.firstChild;f&&B(c,f,!0);H({j:d,m:f,h:c,l:1,a:null,i:null,c:null,g:null});
return c.innerHTML},prettyPrint:g=function(a,d){function f(){for(var c=Q.PR_SHOULD_USE_CONTINUATION?b.now()+250:Infinity;t<r.length&&b.now()<c;t++){for(var d=r[t],k=h,n=d;n=n.previousSibling;){var q=n.nodeType,l=(7===q||8===q)&&n.nodeValue;if(l?!/^\??prettify\b/.test(l):3!==q||/\S/.test(n.nodeValue))break;if(l){k={};l.replace(/\b(\w+)=([\w:.%+-]+)/g,function(a,b,c){k[b]=c});break}}n=d.className;if((k!==h||u.test(n))&&!e.test(n)){q=!1;for(l=d.parentNode;l;l=l.parentNode)if(w.test(l.tagName)&&l.className&&
u.test(l.className)){q=!0;break}if(!q){d.className+=" prettyprinted";q=k.lang;if(!q){var q=n.match(v),A;!q&&(A=z(d))&&D.test(A.tagName)&&(q=A.className.match(v));q&&(q=q[1])}if(x.test(d.tagName))l=1;else var l=d.currentStyle,y=g.defaultView,l=(l=l?l.whiteSpace:y&&y.getComputedStyle?y.getComputedStyle(d,null).getPropertyValue("white-space"):0)&&"pre"===l.substring(0,3);y=k.linenums;(y="true"===y||+y)||(y=(y=n.match(/\blinenums\b(?::(\d+))?/))?y[1]&&y[1].length?+y[1]:!0:!1);y&&B(d,y,l);H({j:q,h:d,m:y,
l:l,a:null,i:null,c:null,g:null})}}}t<r.length?Q.setTimeout(f,250):"function"===typeof a&&a()}for(var c=d||document.body,g=c.ownerDocument||document,c=[c.getElementsByTagName("pre"),c.getElementsByTagName("code"),c.getElementsByTagName("xmp")],r=[],k=0;k<c.length;++k)for(var n=0,l=c[k].length;n<l;++n)r.push(c[k][n]);var c=null,b=Date;b.now||(b={now:function(){return+new Date}});var t=0,v=/\blang(?:uage)?-([\w.]+)(?!\S)/,u=/\bprettyprint\b/,e=/\bprettyprinted\b/,x=/pre|xmp/i,D=/^code$/i,w=/^(?:pre|code|xmp)$/i,
h={};f()}},R=Q.define;"function"===typeof R&&R.amd&&R("google-code-prettify",[],function(){return U})})();return g}();S||k.setTimeout(T,0)})();}()

37
src/App.js Normal file
View File

@ -0,0 +1,37 @@
import React, { Suspense, lazy } from 'react'
import {
BrowserRouter as Router,
Route,
Switch
} from 'react-router-dom'
import ErrorBoundary from './components/error-boundary'
import { initAxiosInterceptors } from './util'
const TaskPanel = lazy(() => import('./routes/task-panel'))
const HackPanel = lazy(() => import('./routes/hack-panel'))
const NoticeDetail = lazy(() => import('./routes/notice'))
initAxiosInterceptors()
function App() {
return (
<div className="app">
{/* <ErrorBoundary> */}
<Router basename="/h5educoderbuild" >
<Suspense fallback={<div></div>}>
<Switch>
<Route path='/educodermd/Notice/:courseid/:infromid/:educoder_session/:autologin_trustie' component={NoticeDetail} />
<Route path='/educodermd/hack/:identifier/:educoder_session/:autologin_trustie' component={HackPanel} />
<Route path='/educodermd/:identifier/:educoder_session/:autologin_trustie' component={TaskPanel} />
</Switch>
</Suspense>
</Router>
{/* </ErrorBoundary> */}
</div>
);
}
export default App;

9
src/App.test.js Normal file
View File

@ -0,0 +1,9 @@
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

View File

@ -0,0 +1,115 @@
import React, { useEffect, useRef, useMemo } from 'react'
import 'katex/dist/katex.min.css'
import marked, { getTocContent, cleanToc, getMathExpressions, resetMathExpressions, lines } from './marked'
import 'code-prettify'
// import dompurify from 'dompurify'
import { renderToString } from 'katex'
const preRegex = /<pre[^>]*>/g
function _unescape(str: string) {
let div = document.createElement('div')
div.innerHTML = str
return div.childNodes.length === 0 ? "" : div.childNodes[0].nodeValue
}
interface IProps {
value: string
className?: string
style?: any,
showLines?: number,
showTextOnly?: boolean
}
export default ({
value = '',
className,
showTextOnly,
showLines,
style = {},
}: IProps) => {
let str = String(value);
const html = useMemo(() => {
// let proxy = "http://test-data.educoder.net"
let proxy='https://data.educoder.net'
console.log("str:", str)
try {
const reg = /\(\s+\/api\/attachments\/|\(\/api\/attachments\/|\(\/attachments\/download\//g;
const reg2 = /\"\/api\/attachments\/|\"\/attachments\/download\//g;
const reg3 = /\(\s+\/files\/uploads\/|\(\/files\/uploads\//g;
str = str.replace(reg, "(" + proxy + "/api/attachments/")
.replace(reg2, '"' + proxy+ "/api/attachments/")
.replace(reg3, '"' + proxy + "/files/uploads/")
} catch (e) { };
let rs = marked(str)
console.log("rs:", rs)
const math_expressions:any = getMathExpressions()
if (str.match(/\[TOC\]/)) {
rs = rs.replace("<p>[TOC]</p>", getTocContent())
cleanToc()
}
rs = rs.replace(/(__special_katext_id_\d+__)/g, (_match: any, capture: string | number) => {
const { type, expression } = math_expressions[capture]
return renderToString(_unescape(expression) || '', { displayMode: type === 'block', throwOnError: false, output: 'html' })
})
rs = rs.replace(/▁/g, "▁▁▁")
resetMathExpressions()
// return dompurify.sanitize(rs)
if (showTextOnly) {
const dom = document.createElement("div")
dom.innerHTML = rs
return dom.innerText;
}
return rs;
}, [str])
const el = useRef<HTMLDivElement>()
lines["WebkitLineClamp"] = showLines;
if(showLines){
style = {...style,...lines}
};
function onAncherHandler(e: any) {
let target = e.target
if (target.tagName.toUpperCase() === 'A') {
let ancher = target.getAttribute('href')
if (ancher.startsWith('#')) {
e.preventDefault()
let viewEl = document.getElementById(ancher.replace('#', ''))
if (viewEl) {
viewEl.scrollIntoView(true)
}
}
}
}
useEffect(() => {
if (el.current && html) {
if (html.match(preRegex)) {
(window as any).PR.prettyPrint()
}
}
if (el.current) {
el.current.addEventListener('click', onAncherHandler)
return () => {
el.current.removeEventListener('click', onAncherHandler)
resetMathExpressions()
cleanToc()
}
}
}, [html, el.current, onAncherHandler])
return (
<>
<div
ref={el}
style={{...style}}
className={`${className ? className : ''} markdown-body`}
dangerouslySetInnerHTML={{ __html: html }}
></div>
</>
)
}

View File

@ -0,0 +1,181 @@
import marked from 'marked'
import { escape } from 'marked/src/helpers'
function indentCodeCompensation(raw, text) {
const matchIndentToCode = raw.match(/^(\s+)(?:```)/)
if (matchIndentToCode === null) {
return text
}
const indentToCode = matchIndentToCode[1]
return text
.split('\n')
.map(node => {
const matchIndentInNode = node.match(/^\s+/)
if (matchIndentInNode === null) {
return node
}
const [indentInNode] = matchIndentInNode
if (indentInNode.length >= indentToCode.length) {
return node.slice(indentToCode.length)
}
return node
})
.join('\n')
}
// interface IToc {
// anchor: string
// level: number
// text: string
// }
// ##
let toc = []
let ctx= ["<ul>"]
const renderer = new marked.Renderer()
const headingRegex = /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/
export function cleanToc() {
toc.length = 0
ctx = ["<ul>"]
}
export const lines = {
overflow: "hidden",
WebkitBoxOrient: "vertical",
display: "-webkit-box",
WebkitLineClamp:2,
}
function buildToc(coll, k, level, ctx) {
if (k >= coll.length || coll[k].level <= level) { return k }
var node = coll[k]
ctx.push("<li><a href='#" + node.anchor + "'>" + node.text + "</a>")
k++
var childCtx = []
k = buildToc(coll, k, node.level, childCtx)
if (childCtx.length > 0) {
ctx.push("<ul>")
childCtx.forEach(function (idm) {
ctx.push(idm)
})
ctx.push("</ul>")
}
ctx.push("</li>")
k = buildToc(coll, k, level, ctx)
return k
}
export function getTocContent() {
buildToc(toc, 0, 0, ctx)
ctx.push("</ul>")
return ctx.join("")
}
const tokenizer = {
heading(src) {
const cap = headingRegex.exec(src)
if (cap) {
return {
type: 'heading',
raw: cap[0],
depth: cap[1].length,
text: cap[2]
}
}
},
fences(src) {
const cap = this.rules.block.fences.exec(src)
if (cap) {
const raw = cap[0]
let text = indentCodeCompensation(raw, cap[3] || '')
const lang = cap[2] ? cap[2].trim() : cap[2]
if (['latex', 'katex', 'math'].indexOf(lang) >= 0) {
const id = next_id()
const expression = text
text = id
math_expressions[id] = { type: 'block', expression }
}
return {
type: 'code',
raw,
lang,
text
}
}
}
}
const latexRegex = /(?:\${2})([^\n`]+?)(?:\${2})/gi
let katex_count = 0
const next_id = () => `__special_katext_id_${katex_count++}__`
let math_expressions = {}
export function getMathExpressions() {
return math_expressions
}
export function resetMathExpressions() {
katex_count = 0
math_expressions = {}
}
function replace_math_with_ids(text) {
text = text.replace(latexRegex, (_match, expression) => {
const id = next_id()
math_expressions[id] = { type: 'inline', expression }
return id
})
return text
}
const original_listitem = renderer.listitem
renderer.listitem = function (text) {
return original_listitem(replace_math_with_ids(text))
}
const original_paragraph = renderer.paragraph
renderer.paragraph = function (text) {
return original_paragraph(replace_math_with_ids(text))
}
const original_tablecell = renderer.tablecell
renderer.tablecell = function (content, flags) {
return original_tablecell(replace_math_with_ids(content), flags)
}
renderer.code = function (code, infostring, escaped) {
const lang = (infostring || '').match(/\S*/)[0]
if (!lang) {
return '<pre class="prettyprint linenums"><code>'
+ (escaped ? code : escape(code, true))
+ '</code></pre>'
}
if (['latex', 'katex', 'math'].indexOf(lang) >= 0) {
return `<p class='editormd-tex'>${code}</p>`
} else {
return `<pre class="prettyprint linenums"><code class="language-${infostring}">${escaped ? code : escape(code, true)}</code></pre>\n`
}
}
renderer.heading = function (text, level, raw) {
let anchor = this.options.headerPrefix + raw.toLowerCase().replace(/[^\w\\u4e00-\\u9fa5]]+/g, '-')
toc.push({
anchor,
level,
text
})
return '<h' + level + ' id="' + anchor + '">' + text + '</h' + level + '>'
}
marked.setOptions({
silent: true,
gfm: true,
pedantic: false
})
marked.use({ tokenizer, renderer })
export default marked

View File

@ -0,0 +1,26 @@
import React from 'react'
export default class ErrorBoundary extends React.Component {
constructor(props) {
super(props)
this.state = { hasError: false }
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true }
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
console.log(error, errorInfo)
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>
}
return this.props.children
}
}

View File

@ -0,0 +1,10 @@
import React from 'react'
import './img-preview.less'
export default ({ url, onCancel }) => {
return (
<div className="img-preview-wrapper" onClick={onCancel}>
<img src={url} alt='大图预览' />
</div>
)
}

View File

@ -0,0 +1,18 @@
.img-preview-wrapper {
position: fixed;
z-index: 204;
top: 0;
right: 0;
left: 0;
bottom: 0;
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, .7);
img {
display: block;
max-width: 100%;
}
}

View File

@ -0,0 +1,9 @@
.loading-tip {
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
height: 100%;
background: #213857;
color: #fff;
}

View File

@ -0,0 +1,8 @@
import React from 'react'
import './loading.css'
export default () => {
return <div className="loading-tip">
<p>loading ...</p>
</div>
}

21
src/components/modal.jsx Normal file
View File

@ -0,0 +1,21 @@
import React from 'react'
import { createPortal } from 'react-dom'
export default class Dialog extends React.Component {
constructor(props) {
super(props)
const doc = window.document
this.node = doc.createElement('div')
doc.body.appendChild(this.node)
}
render() {
const { children } = this.props
return createPortal(children, this.node)
}
componentWillUnmount() {
window.document.body.removeChild(this.node);
}
}

View File

@ -0,0 +1,48 @@
import React, { useEffect, useRef } from 'react'
import 'katex/dist/katex.min.css';
import marked, { getTocContent, cleanToc } from '../util/marked'
let preRegex = /<pre[^>]*>/g
export default ({ value = '', is_md = true, className, style = {} }) => {
let str = String(value)
let html = is_md ? marked(str) : value
if (str.match(/\[TOC\]/)) {
html = html.replace("<p>[TOC]</p>", getTocContent())
cleanToc()
}
html = html.replace(/▁/g, "▁▁▁")
const el = useRef()
function onAncherHandler(e) {
let target = e.target
if (target.tagName.toUpperCase() === 'A') {
let ancher = target.getAttribute('href')
if (ancher.startsWith('#')) {
e.preventDefault()
let viewEl = document.getElementById(ancher.replace('#', ''))
if (viewEl) {
viewEl.parentNode.scrollTop = viewEl.offsetTop
}
}
}
}
useEffect(() => {
if (el.current && html) {
if (html.match(preRegex)) {
window.PR.prettyPrint()
}
}
if (el.current) {
el.current.addEventListener('click', onAncherHandler)
return () => {
el.current.removeEventListener('click', onAncherHandler)
}
}
}, [html, el.current, onAncherHandler])
return (<div ref={el} style={style} className={`${className ? className : ''} markdown-body`} dangerouslySetInnerHTML={{ __html: html }}></div>)
}

View File

@ -0,0 +1,68 @@
import React, { useEffect, useState, useRef } from 'react'
import mediator from '../../util/mediator'
function padLeft(s) {
return s.toString().padStart(2, '0')
}
function getTimer(sec) {
const minuts = Math.floor(sec / 60)
const second = sec % 60
const hour = Math.floor(minuts / 60)
return `${padLeft(hour)}:${padLeft(minuts - hour * 60)}:${padLeft(second)}`
}
//
//identifier taskId
export default ({ identifier, onUpdateCostTime, status, defaultTime = 0,type }) => {
const [costTime, setCostTime] = useState(defaultTime)
const lastV = useRef(defaultTime)
useEffect(() => {
lastV.current = defaultTime
setCostTime(defaultTime)
//
if (status !== 2) {
let timeHandler = function () {
setCostTime(costTime => costTime + 1)
lastV.current = lastV.current + 1
}
let tid = window.setInterval(timeHandler, 1000)
let unSub = mediator.subscribe('update-cost-time', () => {
onUpdateCostTime(identifier, { time: lastV.current }).then(() => {
mediator.publish('reset-game')
})
})
return () => {
clearInterval(tid)
unSub()
if(type===1){
let bodys = {
answer_long_time: lastV.current*1000,
};
let headers = {
type: 'application/json',
};
let blob = new Blob([JSON.stringify(bodys)], headers);
window.navigator.sendBeacon(
`https://data.educoder.net/api/myproblems/${identifier}/save_long_time.json?answer_long_time=${lastV.current*1000}`,
blob,
);
mediator.publish('reset-game')
return
}
onUpdateCostTime(identifier, { time: lastV.current }).then(() => {
mediator.publish('reset-game')
})
}
}
}, [identifier, onUpdateCostTime, status, defaultTime])
return (
<span>{getTimer(costTime)}</span>
)
}

21
src/hooks/useInterval.jsx Normal file
View File

@ -0,0 +1,21 @@
import React, { useEffect, useRef } from 'react';
export default function useInterval(callback, delay) {
const savedCallback = useRef();
//
useEffect(() => {
savedCallback.current = callback;
});
// interval
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}

56
src/hooks/useTouch.jsx Normal file
View File

@ -0,0 +1,56 @@
import React, { useRef, useEffect } from 'react'
export default ({ onLeft, onRight, children, cls }) => {
const ref = useRef()
const width = document.documentElement.clientWidth || document.body.clientWidth
let startX
, startY
, distX
, distY
, threshold = width / 2
, elapsedTime
, startTime
, quickTime = 320
function ontouchstart(e) {
let obj = e.changedTouches[0]
startX = obj.pageX
startY = obj.pageY
startTime = new Date().getTime()
}
function ontouchend(e) {
let obj = e.changedTouches[0]
distY = obj.pageY - startY
distX = obj.pageX - startX
elapsedTime = new Date().getTime() - startTime
if ((Math.abs(distX) > threshold || elapsedTime <= quickTime) && Math.abs(distX) > Math.abs(distY)) {
if (distX < 0) {
if (onLeft) {
onLeft()
}
} else {
if (onRight) {
onRight()
}
}
}
}
useEffect(() => {
let el = ref.current
if (el) {
el.addEventListener("touchstart", ontouchstart)
el.addEventListener("touchend", ontouchend)
}
return () => {
el.removeEventListener('touchstart', ontouchstart)
el.removeEventListener('touchend', ontouchend)
}
}, [ref.current, ontouchstart, ontouchend])
return (
<div ref={ref} className={cls} style={{ width: '100%', height: '100%' }}>
{children}
</div>
)
}

329
src/iconfont.css Normal file

File diff suppressed because one or more lines are too long

593
src/index.css Normal file
View File

@ -0,0 +1,593 @@
*,
*:before,
*:after {
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
font-family: sans-serif;
overflow-y: scroll;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.ant-popover-buttons {
text-align: center !important;
}
#root,
.app {
width: 100%;
height: 100%;
}
p,
h2,
h3,
h4 {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
.markdown-body ul,
.mardown-body ol {
list-style-type: decimal !important;
}
.markdown-body p {
font-size: 16px;
line-height: 25px;
white-space: pre-wrap;
}
.markdown-body hr:after,
.markdown-body hr:before {
content: "";
display: table
}
/*! github-markdown-css | The MIT License (MIT) | Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com) | https://github.com/sindresorhus/github-markdown-css */
@font-face {
font-family: octicons-anchor;
src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAYcAA0AAAAACjQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABMAAAABwAAAAca8vGTk9TLzIAAAFMAAAARAAAAFZG1VHVY21hcAAAAZAAAAA+AAABQgAP9AdjdnQgAAAB0AAAAAQAAAAEACICiGdhc3AAAAHUAAAACAAAAAj//wADZ2x5ZgAAAdwAAADRAAABEKyikaNoZWFkAAACsAAAAC0AAAA2AtXoA2hoZWEAAALgAAAAHAAAACQHngNFaG10eAAAAvwAAAAQAAAAEAwAACJsb2NhAAADDAAAAAoAAAAKALIAVG1heHAAAAMYAAAAHwAAACABEAB2bmFtZQAAAzgAAALBAAAFu3I9x/Nwb3N0AAAF/AAAAB0AAAAvaoFvbwAAAAEAAAAAzBdyYwAAAADP2IQvAAAAAM/bz7t4nGNgZGFgnMDAysDB1Ml0hoGBoR9CM75mMGLkYGBgYmBlZsAKAtJcUxgcPsR8iGF2+O/AEMPsznAYKMwIkgMA5REMOXicY2BgYGaAYBkGRgYQsAHyGMF8FgYFIM0ChED+h5j//yEk/3KoSgZGNgYYk4GRCUgwMaACRoZhDwCs7QgGAAAAIgKIAAAAAf//AAJ4nHWMMQrCQBBF/0zWrCCIKUQsTDCL2EXMohYGSSmorScInsRGL2DOYJe0Ntp7BK+gJ1BxF1stZvjz/v8DRghQzEc4kIgKwiAppcA9LtzKLSkdNhKFY3HF4lK69ExKslx7Xa+vPRVS43G98vG1DnkDMIBUgFN0MDXflU8tbaZOUkXUH0+U27RoRpOIyCKjbMCVejwypzJJG4jIwb43rfl6wbwanocrJm9XFYfskuVC5K/TPyczNU7b84CXcbxks1Un6H6tLH9vf2LRnn8Ax7A5WQAAAHicY2BkYGAA4teL1+yI57f5ysDNwgAC529f0kOmWRiYVgEpDgYmEA8AUzEKsQAAAHicY2BkYGB2+O/AEMPCAAJAkpEBFbAAADgKAe0EAAAiAAAAAAQAAAAEAAAAAAAAKgAqACoAiAAAeJxjYGRgYGBhsGFgYgABEMkFhAwM/xn0QAIAD6YBhwB4nI1Ty07cMBS9QwKlQapQW3VXySvEqDCZGbGaHULiIQ1FKgjWMxknMfLEke2A+IJu+wntrt/QbVf9gG75jK577Lg8K1qQPCfnnnt8fX1NRC/pmjrk/zprC+8D7tBy9DHgBXoWfQ44Av8t4Bj4Z8CLtBL9CniJluPXASf0Lm4CXqFX8Q84dOLnMB17N4c7tBo1AS/Qi+hTwBH4rwHHwN8DXqQ30XXAS7QaLwSc0Gn8NuAVWou/gFmnjLrEaEh9GmDdDGgL3B4JsrRPDU2hTOiMSuJUIdKQQayiAth69r6akSSFqIJuA19TrzCIaY8sIoxyrNIrL//pw7A2iMygkX5vDj+G+kuoLdX4GlGK/8Lnlz6/h9MpmoO9rafrz7ILXEHHaAx95s9lsI7AHNMBWEZHULnfAXwG9/ZqdzLI08iuwRloXE8kfhXYAvE23+23DU3t626rbs8/8adv+9DWknsHp3E17oCf+Z48rvEQNZ78paYM38qfk3v/u3l3u3GXN2Dmvmvpf1Srwk3pB/VSsp512bA/GG5i2WJ7wu430yQ5K3nFGiOqgtmSB5pJVSizwaacmUZzZhXLlZTq8qGGFY2YcSkqbth6aW1tRmlaCFs2016m5qn36SbJrqosG4uMV4aP2PHBmB3tjtmgN2izkGQyLWprekbIntJFing32a5rKWCN/SdSoga45EJykyQ7asZvHQ8PTm6cslIpwyeyjbVltNikc2HTR7YKh9LBl9DADC0U/jLcBZDKrMhUBfQBvXRzLtFtjU9eNHKin0x5InTqb8lNpfKv1s1xHzTXRqgKzek/mb7nB8RZTCDhGEX3kK/8Q75AmUM/eLkfA+0Hi908Kx4eNsMgudg5GLdRD7a84npi+YxNr5i5KIbW5izXas7cHXIMAau1OueZhfj+cOcP3P8MNIWLyYOBuxL6DRylJ4cAAAB4nGNgYoAALjDJyIAOWMCiTIxMLDmZedkABtIBygAAAA==)format("woff")
}
.markdown-body {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
color: #333;
overflow: hidden;
font-family: "微软雅黑", "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, "华文细黑", STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, "宋体", Heiti, "黑体", sans-serif;
font-size: 16px;
line-height: 1.6;
word-wrap: break-word
}
.markdown-body strong {
font-weight: 700
}
.markdown-body h1 {
margin: .67em 0
}
.markdown-body img {
border: 0
}
.markdown-body hr {
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0
}
.markdown-body input {
color: inherit;
margin: 0;
line-height: normal;
font: 13px/1.4 Helvetica, arial, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol"
}
.markdown-body html input[disabled] {
cursor: default
}
.markdown-body input[type=checkbox] {
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 0
}
.markdown-body * {
-moz-box-sizing: border-box;
box-sizing: border-box
}
.markdown-body a {
background: 0 0;
color: #4183c4 !important;
text-decoration: none
}
.markdown-body a:active,
.markdown-body a:hover {
outline: 0;
text-decoration: underline
}
.markdown-body hr {
margin: 15px 0;
overflow: hidden;
background: 0 0;
border: 0;
border-bottom: 1px solid #ddd
}
.markdown-body h1,
.markdown-body h2 {
padding-bottom: .3em;
border-bottom: 1px solid #eee
}
.markdown-body blockquote {
margin: 0
}
.markdown-body ol ol,
.markdown-body ul ol {
list-style-type: lower-roman
}
.markdown-body ol ol ol,
.markdown-body ol ul ol,
.markdown-body ul ol ol,
.markdown-body ul ul ol {
list-style-type: lower-alpha
}
.markdown-body dd {
margin-left: 0
}
.markdown-body code {
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace
}
.markdown-body pre {
font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
word-wrap: normal
}
.markdown-body .octicon {
font: normal normal 16px octicons-anchor;
line-height: 1;
display: inline-block;
text-decoration: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
user-select: none
}
.markdown-body .octicon-link:before {
content: '\f05c'
}
.markdown-body>:first-child {
margin-top: 0 !important
}
.markdown-body>:last-child {
margin-bottom: 0 !important
}
.markdown-body .anchor {
position: absolute;
top: 0;
left: 0;
display: block;
padding-right: 6px;
padding-left: 30px;
margin-left: -30px
}
.markdown-body .anchor:focus {
outline: 0
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
position: relative;
margin-top: 1em;
margin-bottom: 16px;
font-weight: 700;
line-height: 1.4
}
.markdown-body h1 .octicon-link,
.markdown-body h2 .octicon-link,
.markdown-body h3 .octicon-link,
.markdown-body h4 .octicon-link,
.markdown-body h5 .octicon-link,
.markdown-body h6 .octicon-link {
display: none;
color: #000;
vertical-align: middle
}
.markdown-body h1:hover .anchor,
.markdown-body h2:hover .anchor,
.markdown-body h3:hover .anchor,
.markdown-body h4:hover .anchor,
.markdown-body h5:hover .anchor,
.markdown-body h6:hover .anchor {
padding-left: 8px;
margin-left: -30px;
text-decoration: none
}
.markdown-body h1:hover .anchor .octicon-link,
.markdown-body h2:hover .anchor .octicon-link,
.markdown-body h3:hover .anchor .octicon-link,
.markdown-body h4:hover .anchor .octicon-link,
.markdown-body h5:hover .anchor .octicon-link,
.markdown-body h6:hover .anchor .octicon-link {
display: inline-block
}
.markdown-body h1 {
font-size: 2.25em;
line-height: 1.2
}
.markdown-body h1 .anchor {
line-height: 1
}
.markdown-body h2 {
font-size: 1.75em;
line-height: 1.225
}
.markdown-body h2 .anchor {
line-height: 1
}
.markdown-body h3 {
font-size: 1.5em;
line-height: 1.43
}
.markdown-body h3 .anchor,
.markdown-body h4 .anchor {
line-height: 1.2
}
.markdown-body h4 {
font-size: 1.25em
}
.markdown-body h5 .anchor,
.markdown-body h6 .anchor {
line-height: 1.1
}
.markdown-body h5 {
font-size: 1em
}
.markdown-body h6 {
font-size: 1em;
color: #777
}
.markdown-body blockquote,
.markdown-body dl,
.markdown-body ol,
.markdown-body p,
.markdown-body pre,
.markdown-body table,
.markdown-body ul {
margin-top: 0;
margin-bottom: 16px
}
.markdown-body ol,
.markdown-body ul {
padding-left: 2em
}
.markdown-body ol ol,
.markdown-body ol ul,
.markdown-body ul ol,
.markdown-body ul ul {
margin-top: 0;
margin-bottom: 0
}
.markdown-body li>p {
margin-top: 16px
}
.markdown-body dl {
padding: 0
}
.markdown-body dl dt {
padding: 0;
margin-top: 16px;
font-size: 1em;
font-style: italic;
font-weight: 700
}
.markdown-body dl dd {
padding: 0 16px;
margin-bottom: 16px
}
.markdown-body blockquote {
padding: 0 15px;
color: #777;
border-left: 4px solid #ddd
}
.markdown-body blockquote>:first-child {
margin-top: 0
}
.markdown-body blockquote>:last-child {
margin-bottom: 0
}
.markdown-body table {
border-collapse: collapse;
border-spacing: 0;
display: block;
width: 100%;
overflow: auto;
word-break: normal;
word-break: keep-all
}
.markdown-body table th {
font-weight: 700
}
.markdown-body table td,
.markdown-body table th {
padding: 6px 13px;
border: 1px solid #ddd
}
.markdown-body table tr {
background-color: #fff;
border-top: 1px solid #ccc
}
.markdown-body table tr:nth-child(2n) {
background-color: #f8f8f8
}
.markdown-body img {
max-width: 100%;
-moz-box-sizing: border-box;
box-sizing: border-box
}
.markdown-body code {
padding: .2em 0;
margin: 0;
font-size: 85%;
background-color: rgba(0, 0, 0, .04);
border-radius: 3px
}
.markdown-body code:after,
.markdown-body code:before {
letter-spacing: -.2em;
content: "\00a0"
}
.markdown-body pre>code {
padding: 0;
margin: 0;
font-size: 100%;
word-break: normal;
white-space: pre;
background: 0 0;
border: 0
}
.markdown-body .highlight {
margin-bottom: 16px
}
.markdown-body .highlight pre,
.markdown-body pre {
padding: 16px;
overflow: auto;
font-size: 85%;
background-color: #f7f7f7;
border-radius: 3px
}
.markdown-body .highlight pre {
margin-bottom: 0;
word-break: normal
}
.markdown-body pre code {
display: inline;
max-width: initial;
padding: 0;
margin: 0;
overflow: initial;
line-height: inherit;
word-wrap: normal;
background-color: transparent;
border: 0
}
.markdown-body pre code:after,
.markdown-body pre code:before {
content: normal
}
.markdown-body .pl-c {
color: #969896
}
.markdown-body .pl-c1,
.markdown-body .pl-mdh,
.markdown-body .pl-mm,
.markdown-body .pl-mp,
.markdown-body .pl-mr,
.markdown-body .pl-s1 .pl-v,
.markdown-body .pl-s3,
.markdown-body .pl-sc,
.markdown-body .pl-sv {
color: #0086b3
}
.markdown-body .pl-e,
.markdown-body .pl-en {
color: #795da3
}
.markdown-body .pl-s1 .pl-s2,
.markdown-body .pl-smi,
.markdown-body .pl-smp,
.markdown-body .pl-stj,
.markdown-body .pl-vo,
.markdown-body .pl-vpf {
color: #333
}
.markdown-body .pl-ent {
color: #63a35c
}
.markdown-body .pl-k,
.markdown-body .pl-s,
.markdown-body .pl-st {
color: #a71d5d
}
.markdown-body .pl-pds,
.markdown-body .pl-s1,
.markdown-body .pl-s1 .pl-pse .pl-s2,
.markdown-body .pl-sr,
.markdown-body .pl-sr .pl-cce,
.markdown-body .pl-sr .pl-sra,
.markdown-body .pl-sr .pl-sre,
.markdown-body .pl-src {
color: #df5000
}
.markdown-body .pl-mo,
.markdown-body .pl-v {
color: #1d3e81
}
.markdown-body .pl-id {
color: #b52a1d
}
.markdown-body .pl-ii {
background-color: #b52a1d;
color: #f8f8f8
}
.markdown-body .pl-sr .pl-cce {
color: #63a35c;
font-weight: 700
}
.markdown-body .pl-ml {
color: #693a17
}
.markdown-body .pl-mh,
.markdown-body .pl-mh .pl-en,
.markdown-body .pl-ms {
color: #1d3e81;
font-weight: 700
}
.markdown-body .pl-mq {
color: teal
}
.markdown-body .pl-mi {
color: #333;
font-style: italic
}
.markdown-body .pl-mb {
color: #333;
font-weight: 700
}
.markdown-body .pl-md,
.markdown-body .pl-mdhf {
background-color: #ffecec;
color: #bd2c00
}
.markdown-body .pl-mdht,
.markdown-body .pl-mi1 {
background-color: #eaffea;
color: #55a532
}
.markdown-body .pl-mdr {
color: #795da3;
font-weight: 700
}
.markdown-body kbd {
display: inline-block;
padding: 3px 5px;
font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
line-height: 10px;
color: #555;
vertical-align: middle;
background-color: #fcfcfc;
border: 1px solid #ccc;
border-bottom-color: #bbb;
border-radius: 3px;
box-shadow: inset 0 -1px 0 #bbb
}
.markdown-body .task-list-item+.task-list-item {
margin-top: 3px
}
.markdown-body .task-list-item input {
float: left;
margin: .3em 0 .25em -1.6em;
vertical-align: middle
}
.markdown-body :checked+.radio-label {
z-index: 1;
position: relative;
border-color: #4183c4
}

16
src/index.js Normal file
View File

@ -0,0 +1,16 @@
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import './iconfont.css'
import App from './App'
import * as serviceWorker from './serviceWorker'
ReactDOM.render(<App />, document.getElementById('root'))
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister()

7
src/logo.svg Normal file
View File

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
<g fill="#61DAFB">
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
<circle cx="420.9" cy="296.5" r="45.7"/>
<path d="M520.5 78.1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

1
src/react-app-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="react-scripts" />

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,151 @@
.wx-pt-btn {
margin-top: 0.2rem;
border-radius: 22px;
width: 72px;
height: 36px;
line-height: 36px;
font-size: 16px;
text-align: center;
color: #fff;
background: #2EA4FF;
}
.inputBox {
visibility: hidden;
}
.challengestitle {
font-size: 1.3rem;
font-weight: 800;
color: rgba(48, 49, 51, 1);
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.am-modal-content {
border-radius: 1rem 1rem 0rem 0rem;
}
.am-list-header {
height: 2.75rem;
}
.Jumpoff {
font-size: 0.8rem;
font-weight: 400;
color: rgba(144, 147, 153, 1);
}
.JumpoffFF9C3D {
color: #FF9C3D;
}
.closebutton {
font-size: 0.8rem;
font-weight: 400;
color: rgba(48, 49, 51, 1);
}
.am-list-line::after {
background-color: #fff !important;
}
.am-list-item .am-list-line .am-list-content {
background: #fafafa;
border-radius: 0.5rem;
margin-bottom: 0.625rem;
padding: 0.625rem;
font-weight: 400;
color: rgba(144, 147, 153, 1);
font-size: 1rem;
position: relative;
}
.am-list-body {
padding-top: 0.625rem;
}
.conlistfont {
max-width: 16rem;
font-size: 1rem;
color: rgba(48, 49, 51, 1);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap
}
.conlistfontclose {
max-width: 16rem;
font-size: 1rem;
color: #C0C4CC !important;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap
}
.colorC0C4CC {
color: #C0C4CC;
}
.am-list-item.am-list-item-active {
background-color: #fff !important;
}
.iconwanchengjiaobiao {
position: absolute;
top: -0.625rem;
right: 0.0625rem;
color: #19CB70;
font-size: 2.5rem;
}
.am-modal-title {
font-size: 1rem !important;
line-height: 1.5rem !important;
}
.am-modal-transparent {
width: 20rem !important;
}
.color51 {
color: rgb(51, 163, 244);
}
.am-modal-button-group-v .am-modal-button {
color: rgb(51, 163, 244);
font-size: 1rem;
}
.inputBox {
width: 0px;
height: 0px;
position: fixed;
bottom: 0px;
}
.wx-code-area {
width: 100%;
overflow: hidden;
height: 100%;
}
pre.prettyprint {
overflow: auto;
}
.fl {
float: left;
}
.fr {
float: right;
}
.ml10 {
margin-left: 10px;
}

View File

@ -0,0 +1,349 @@
import './index.css';
import './katex.min.css';
import React, { useState, useEffect, useRef } from 'react';
import axios from 'axios';
import { WingBlank, WhiteSpace, TabBar, Modal, List, Toast } from 'antd-mobile';
import { setCookie } from '../../util';
import { useParams, useLocation, useHistory, useRouteMatch } from 'react-router-dom'
import mediator from '../../util/mediator';
// import RenderHtml from '../../components/render-html';
import RenderHtml from '../../components/RenderHtml/index';
axios.defaults.withCredentials = true;
export default (props) => {
const { identifier, educoder_session, autologin_trustie } = useParams()
const history = useHistory()
const location = useLocation()
const routeMatch = useRouteMatch()
const [showmode, setshowmode] = useState(false);
const [game_challenges, setgame_challenges] = useState(null);
const [getchallenges, setchallenges] = useState([]);
const [identitytype, setidentitytype] = useState(false);
const [challengeposition, setchallengeposition] = useState(0);
const [buttonarray, setbuttonarray] = useState(['关卡', '', '', '代码']);
const [daimaodal, setdaimaodal] = useState(false);
const [myshixun_id, setmyshixun_id] = useState(null);
const [task_pass, settask_pass] = useState("null");
const isPlay = useRef(false)
useEffect(() => {
isPlay.current = false
}, [myshixun_id])
const copyAccessKey = () => {
//inputbutton input
var copyDOM = document.querySelector(".inputBox input"); //
var range = document.createRange(); //range
window.getSelection().removeAllRanges(); //selection
range.selectNode(copyDOM); //
window.getSelection().addRange(range); //
var successful = document.execCommand('copy'); // copy
if (successful) {
Toast.info('复制成功!')
} else {
Toast.info('复制失败,请手动复制!')
}
//
window.getSelection().removeAllRanges();
}
const challenges = (identifier, position, task_passtype) => {
// const url = `https://data.educoder.net/api/myshixuns/${identifier}/challenges.json`;
const url = `/myshixuns/${identifier}/challenges.json`;
axios.get(url, {}).then((response) => {
if (response.data) {
let type = 0
response.data.map((item, key) => {
if (key === position - 1) {
type = item.status;
}
})
let newbuttonarray = buttonarray;
if (position != 1 && response.data.length != 1) {
newbuttonarray[1] = '上一关';
} else {
newbuttonarray[1] = '';
}
if (task_passtype === true) {
if (position != response.data.length) {
newbuttonarray[2] = '下一关';
} else {
newbuttonarray[2] = '';
}
}
if (task_passtype === false) {
if (position != response.data.length && type === 2) {
newbuttonarray[2] = '下一关';
} else {
newbuttonarray[2] = '';
}
}
setbuttonarray(newbuttonarray);
setchallenges(response.data);
}
})
}
const getTaskInfo = (game_identifier) => {
if (!game_identifier) {
Toast.info("该实训不支持跳关");
return
}
isPlay.current = false
const url = `/tasks/${game_identifier}.json`;
axios.get(url, {}).then((response) => {
let task_passtype = false;
setshowmode(false)
if (response.data.challenge) {
let task_pass = response.data.challenge.task_pass
settask_pass(task_pass)
setchallengeposition(response.data.challenge.position)
setgame_challenges(response.data.challenge)
setmyshixun_id(response.data.challenge.id)
}
props.onGetTaskInfo(response.data)
PR.prettyPrint()
if (response.data.shixun) {
if (response.data.user) {
if (response.data.user.identity) {
let identity = response.data.user.identity;
if (identity === 1) {
task_passtype = true
} else if (identity === 2) {
task_passtype = true
} else if (identity === 3) {
task_passtype = true
} else if (identity === 4) {
task_passtype = true
} else if (identity === 5) {
//
task_passtype = true
} else {
if (response.data.shixun.task_pass === true) {
task_passtype = true
} else {
task_passtype = false
}
}
setidentitytype(task_passtype)
if (response.data.myshixun.identifier) {
challenges(response.data.myshixun.identifier, response.data.challenge.position, task_passtype)
}
}
}
}
})
}
function changeTask(taskId, delCode = true) {
let rex = new RegExp(identifier, 'g');
let url = location.pathname.replace(rex, taskId)
if (delCode) {
if (!routeMatch.isExact) {
url = url.replace('/code', '')
}
}
history.replace(url)
}
useEffect(() => {
let cookies = [];
cookies.push({ id: "educoder_session", value: educoder_session })
cookies.push({ id: "autologin_trustie", value: autologin_trustie })
setCookie(cookies)
}, [educoder_session, autologin_trustie])
useEffect(() => {
function init() {
getTaskInfo(identifier)
}
init()
}, [identifier]);
const useCurrentValue = (val) => {
setshowmode(val);
}
const postcheckpoint = (type) => {
if (getchallenges) {
if (getchallenges.length > 0) {
getchallenges.map((item, key) => {
if (type === 0) {
if (key === challengeposition - 2) {
changeTask(item.identifier)
}
} else if (type === 1) {
if (key === challengeposition) {
changeTask(item.identifier)
}
}
})
}
}
}
const renderContent = (pageText) => {
if (pageText === "关卡") {
useCurrentValue(true)
} else if (pageText === "代码") {
weplay(myshixun_id)
} else if (pageText === "上一关") {
postcheckpoint(0)
} else if (pageText === "下一关") {
postcheckpoint(1)
}
}
const weplay = (myshixun_id) => {
if (myshixun_id) {
const url = `/weapps/challenges/${myshixun_id}/is_play.json`;
axios.get(url, {}).then((response) => {
if (response.data) {
if (response.data.status === 0) {
isPlay.current = true
if (routeMatch.isExact) {
let gameurl = `/educodermd/${identifier}/${educoder_session}/${autologin_trustie}/code`;
props.history.push(gameurl);
}
props.callback && props.callback()
} else {
if (response.data.status === -5) {
setdaimaodal(true)
}
}
}
})
}
}
useEffect(() => {
function toTask() {
if (!isPlay.current) {
weplay(myshixun_id)
} else {
props.callback()
}
}
function resetGame() {
getTaskInfo(identifier)
}
function onNextGame(id) {
changeTask(id, false)
}
let unSub = mediator.subscribe('to-task', toTask)
let unSubReset = mediator.subscribe('reset-game', resetGame)
let unSubNextGame = mediator.subscribe('next-game', onNextGame)
return () => {
unSub()
unSubReset()
unSubNextGame()
}
}, [myshixun_id, weplay, isPlay.current]);
return (
<div className="wx-code-flex">
<div style={{ position: 'fixed', height: '100%', width: '100%', top: 0, overflow: 'hidden' }}>
<TabBar
unselectedTintColor="#949494"
tintColor="#33A3F4"
barTintColor="white"
tabBarPosition="bottom"
prerenderingSiblingsNumber={0}
>
{buttonarray.map((item, key) => {
return (
<TabBar.Item
title={item === "代码" ? "" : item}
key={key}
selectedIcon={<div>
{item === "关卡" ? <i className={"iconfont iconguankamulu"} /> :
item === "上一关" ? <i className={"iconfont iconshangyiguan"} /> :
item === "下一关" ? <i className={"iconfont iconxiayiguan"} /> :
item === "代码" ? <div className="wx-pt-btn">代码</div> : ""}
</div>
}
selected={true}
onPress={() => renderContent(item)}
>
<WhiteSpace size="lg" />
<WingBlank>
{game_challenges === null ? "" : <div className="challengestitle">{game_challenges && game_challenges.position}: {game_challenges && game_challenges.subject}</div>}
</WingBlank>
<WhiteSpace size="lg" />
<WingBlank>
{game_challenges === null ? "" : <RenderHtml value={task_pass} style={{ height: 'calc(100vh - 88px)', paddingBottom: '50px' }} />}
</WingBlank>
<div className='inputBox'>
<input
className={'copyInput apiInput'}
value={`https://www.educoder.net/tasks/${identifier}`}
readOnly={true}
/>
</div>
</TabBar.Item>
)
})}
</TabBar>
</div>
<Modal
visible={daimaodal}
transparent
maskClosable={false}
onClose={() => setdaimaodal(false)}
title={`该项目近期将提供小程序入口,请利用下面的网址在电脑端操作:`}
footer={[{ text: '我知道了', onPress: () => setdaimaodal(false) }]}
wrapProps={{ onTouchStart: null }}
>
<div style={{ height: 100, overflow: 'scroll' }}>
<div className="mt10">实训地址https://www.educoder.ne</div>
<div>t/tasks/{identifier}</div>
<div className="mt10 color51 ml10" onClick={() => { copyAccessKey() }}>复制链接
<i className="iconfont iconfuzhi1 ml5 color51"></i>
</div>
</div>
</Modal>
<Modal
popup
visible={showmode}
onClose={() => setshowmode(false)}
animationType="slide-up"
>
<List renderHeader={() => {
return (
<div>
<div className="fl Jumpoff">{getchallenges && getchallenges.length} {identitytype === true ? "" : <span className="JumpoffFF9C3D">该实训不支持跳关</span>}</div>
<div className="fr closebutton" onClick={() => setshowmode(false)}>关闭</div>
</div>
)
}
} className="popup-list">
{getchallenges.map((i, index) => (
<List.Item key={index} onClick={() => changeTask(i.identifier)}><div className="fl colorC0C4CC" >{i.position}</div><div className={i.identifier ? "conlistfont fl ml10" : "conlistfontclose fl ml10"}>
{i.name}
</div>
{i.status === 2 ? <i className="iconfont iconwanchengjiaobiao"></i> : ""}</List.Item>
))}
</List>
</Modal>
</div>
);
}

1
src/routes/educoder-md/katex.min.css vendored Executable file

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More