diff --git a/public/react/scripts/start.js b/public/react/scripts/start.js
index 5c11cf35f..321148cd3 100644
--- a/public/react/scripts/start.js
+++ b/public/react/scripts/start.js
@@ -1,114 +1,114 @@
-'use strict';
-
-// Do this as the first thing so that any code reading it knows the right env.
-process.env.BABEL_ENV = 'development';
-process.env.NODE_ENV = 'development';
-
-
-// Makes the script crash on unhandled rejections instead of silently
-// ignoring them. In the future, promise rejections that are not handled will
-// terminate the Node.js process with a non-zero exit code.
-process.on('unhandledRejection', err => {
-  throw err;
-});
-
-// Ensure environment variables are read.
-require('../config/env');
-
-const fs = require('fs');
-const chalk = require('chalk');
-const webpack = require('webpack');
-const WebpackDevServer = require('webpack-dev-server');
-const clearConsole = require('react-dev-utils/clearConsole');
-const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
-const {
-  choosePort,
-  createCompiler,
-  prepareProxy,
-  prepareUrls,
-} = require('react-dev-utils/WebpackDevServerUtils');
-const openBrowser = require('react-dev-utils/openBrowser');
-const paths = require('../config/paths');
-const config = require('../config/webpack.config.dev');
-const createDevServerConfig = require('../config/webpackDevServer.config');
-
-const useYarn = fs.existsSync(paths.yarnLockFile);
-const isInteractive = process.stdout.isTTY;
-
-const portSetting = require(paths.appPackageJson).port
-if ( portSetting ) {
-  process.env.port = portSetting
-} 
-
-// Warn and crash if required files are missing
-if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
-  process.exit(1);
-}
-
-// Tools like Cloud9 rely on this.
-const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3007;
-const HOST = process.env.HOST || '0.0.0.0';
-
-if (process.env.HOST) {
-  console.log(
-    chalk.cyan(
-      `Attempting to bind to HOST environment variable: ${chalk.yellow(
-        chalk.bold(process.env.HOST)
-      )}`
-    )
-  );
-  console.log(
-    `If this was unintentional, check that you haven't mistakenly set it in your shell.`
-  );
-  console.log(`Learn more here: ${chalk.yellow('http://bit.ly/2mwWSwH')}`);
-  console.log();
-}
-
-// We attempt to use the default port but if it is busy, we offer the user to
-// run on a different port. `choosePort()` Promise resolves to the next free port.
-choosePort(HOST, DEFAULT_PORT)
-  .then(port => {
-    if (port == null) {
-      // We have not found a port.
-      return;
-    }
-    const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
-    const appName = require(paths.appPackageJson).name;
-    const urls = prepareUrls(protocol, HOST, port);
-    // Create a webpack compiler that is configured with custom messages.
-    const compiler = createCompiler(webpack, config, appName, urls, useYarn);
-    // Load proxy config
-    const proxySetting = require(paths.appPackageJson).proxy;
-    console.log('-------------------------proxySetting:', proxySetting)
-    const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
-    // Serve webpack assets generated by the compiler over a web sever.
-    const serverConfig = createDevServerConfig(
-      proxyConfig,
-      urls.lanUrlForConfig
-    );
-    const devServer = new WebpackDevServer(compiler, serverConfig);
-    // Launch WebpackDevServer.
-    devServer.listen(port, HOST, err => {
-      if (err) {
-        return console.log(err);
-      }
-      if (isInteractive) {
-        clearConsole();
-      }
-      console.log(chalk.cyan('Starting the development server...\n'));
-      openBrowser(urls.localUrlForBrowser);
-    });
-
-    ['SIGINT', 'SIGTERM'].forEach(function(sig) {
-      process.on(sig, function() {
-        devServer.close();
-        process.exit();
-      });
-    });
-  })
-  .catch(err => {
-    if (err && err.message) {
-      console.log(err.message);
-    }
-    process.exit(1);
-  });
+'use strict';
+
+// Do this as the first thing so that any code reading it knows the right env.
+process.env.BABEL_ENV = 'development';
+process.env.NODE_ENV = 'development';
+
+
+// Makes the script crash on unhandled rejections instead of silently
+// ignoring them. In the future, promise rejections that are not handled will
+// terminate the Node.js process with a non-zero exit code.
+process.on('unhandledRejection', err => {
+  throw err;
+});
+
+// Ensure environment variables are read.
+require('../config/env');
+
+const fs = require('fs');
+const chalk = require('chalk');
+const webpack = require('webpack');
+const WebpackDevServer = require('webpack-dev-server');
+const clearConsole = require('react-dev-utils/clearConsole');
+const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
+const {
+  choosePort,
+  createCompiler,
+  prepareProxy,
+  prepareUrls,
+} = require('react-dev-utils/WebpackDevServerUtils');
+const openBrowser = require('react-dev-utils/openBrowser');
+const paths = require('../config/paths');
+const config = require('../config/webpack.config.dev');
+const createDevServerConfig = require('../config/webpackDevServer.config');
+
+const useYarn = fs.existsSync(paths.yarnLockFile);
+const isInteractive = process.stdout.isTTY;
+
+const portSetting = require(paths.appPackageJson).port
+if ( portSetting ) {
+  process.env.port = portSetting
+}
+
+// Warn and crash if required files are missing
+if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
+  process.exit(1);
+}
+
+// Tools like Cloud9 rely on this.
+const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3007;
+const HOST = process.env.HOST || '0.0.0.0';
+
+if (process.env.HOST) {
+  console.log(
+    chalk.cyan(
+      `Attempting to bind to HOST environment variable: ${chalk.yellow(
+        chalk.bold(process.env.HOST)
+      )}`
+    )
+  );
+  console.log(
+    `If this was unintentional, check that you haven't mistakenly set it in your shell.`
+  );
+  console.log(`Learn more here: ${chalk.yellow('http://bit.ly/2mwWSwH')}`);
+  console.log();
+}
+
+// We attempt to use the default port but if it is busy, we offer the user to
+// run on a different port. `choosePort()` Promise resolves to the next free port.
+choosePort(HOST, DEFAULT_PORT)
+  .then(port => {
+    if (port == null) {
+      // We have not found a port.
+      return;
+    }
+    const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
+    const appName = require(paths.appPackageJson).name;
+    const urls = prepareUrls(protocol, HOST, port);
+    // Create a webpack compiler that is configured with custom messages.
+    const compiler = createCompiler(webpack, config, appName, urls, useYarn);
+    // Load proxy config
+    const proxySetting = require(paths.appPackageJson).proxy;
+    console.log('-------------------------proxySetting:', proxySetting)
+    const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
+    // Serve webpack assets generated by the compiler over a web sever.
+    const serverConfig = createDevServerConfig(
+      proxyConfig,
+      urls.lanUrlForConfig
+    );
+    const devServer = new WebpackDevServer(compiler, serverConfig);
+    // Launch WebpackDevServer.
+    devServer.listen(port, HOST, err => {
+      if (err) {
+        return console.log(err);
+      }
+      if (isInteractive) {
+        clearConsole();
+      }
+      console.log(chalk.cyan('Starting the development server...\n'));
+      openBrowser(urls.localUrlForBrowser);
+    });
+
+    ['SIGINT', 'SIGTERM'].forEach(function(sig) {
+      process.on(sig, function() {
+        devServer.close();
+        process.exit();
+      });
+    });
+  })
+  .catch(err => {
+    if (err && err.message) {
+      console.log(err.message);
+    }
+    process.exit(1);
+  });
diff --git a/public/react/src/AppConfig.js b/public/react/src/AppConfig.js
index 7283eafb9..dad5993e9 100644
--- a/public/react/src/AppConfig.js
+++ b/public/react/src/AppConfig.js
@@ -16,7 +16,7 @@ function locationurl(list){
       window.location.href(list)
   }
 }
-let hashTimeout 
+let hashTimeout
 
 // TODO 开发期多个身份切换
 let debugType =""
@@ -224,7 +224,7 @@ export function initAxiosInterceptors(props) {
         return Promise.reject(error);
     });
 // -----------------------------------------------------------------------------------
-    
+
 }
 
 
diff --git a/public/react/src/common/Env.js b/public/react/src/common/Env.js
index c80497509..9830d7725 100644
--- a/public/react/src/common/Env.js
+++ b/public/react/src/common/Env.js
@@ -1,8 +1,8 @@
-export function isDev() {
-	return window.location.port === "3007";
-}
-
-// const isMobile 
-export const isMobile = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()));
-
-// const isWeiXin = (/MicroMessenger/i.test(navigator.userAgent.toLowerCase()));
+export function isDev() {
+	return window.location.port === "3007";
+}
+
+// const isMobile
+export const isMobile = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()));
+
+// const isWeiXin = (/MicroMessenger/i.test(navigator.userAgent.toLowerCase()));
diff --git a/public/react/src/common/UrlTool.js b/public/react/src/common/UrlTool.js
index db97642b7..8daf51db8 100644
--- a/public/react/src/common/UrlTool.js
+++ b/public/react/src/common/UrlTool.js
@@ -85,4 +85,4 @@ export function htmlEncode(str) {
 	s = s.replace(/\'/g, "'");//IE下不支持实体名称
 	s = s.replace(/\"/g, """);
 	return s;
-}
\ No newline at end of file
+}
diff --git a/public/react/src/context/TPIContextProvider.js b/public/react/src/context/TPIContextProvider.js
index dce678480..ed7eb2210 100644
--- a/public/react/src/context/TPIContextProvider.js
+++ b/public/react/src/context/TPIContextProvider.js
@@ -41,17 +41,17 @@ const styles = MUIDialogStyleUtil.getTwoButtonStyle()
 // 主题自定义
 const theme = createMuiTheme({
   palette: {
-    primary: { 
+    primary: {
     	main: '#4CACFF',
     	contrastText: 'rgba(255, 255, 255, 0.87)'
-	}, 
+	},
     secondary: { main: '#4CACFF' }, // This is just green.A700 as hex.
   },
 });
 
-const testSetsExpandedArrayInitVal = [false, false, false, false, false, 
-									 false, false, false, false, false, 
-									 false, false, false, false, false, 
+const testSetsExpandedArrayInitVal = [false, false, false, false, false,
+									 false, false, false, false, false,
+									 false, false, false, false, false,
 									 false, false, false, false, false]
 window.__fetchAllFlag = false;	//  是否调用过fetchAll   TODO 如何多次使用provider?
 
@@ -70,7 +70,7 @@ class TPIContextProvider extends Component {
 
 		this.readGameAnswer = this.readGameAnswer.bind(this)
 		this.praisePlus = this.praisePlus.bind(this)
-		
+
 		this.onGamePassed = this.onGamePassed.bind(this)
 
 		this.onPathChange = this.onPathChange.bind(this)
@@ -80,7 +80,7 @@ class TPIContextProvider extends Component {
 
 		this.onShowUpdateDialog = this.onShowUpdateDialog.bind(this)
 		this.updateDialogClose = this.updateDialogClose.bind(this)
-		
+
 		// this.showEffectDisplay();
 
 		this.state = {
@@ -142,7 +142,7 @@ class TPIContextProvider extends Component {
 		// request
         // var shixunId = this.props.match.params.shixunId;
         var stageId = this.props.match.params.stageId;
-        
+
 		window.__fetchAllFlag = false;
 		this.fetchAll(stageId);
 		this.costTimeInterval = window.setInterval(()=> {
@@ -192,7 +192,7 @@ class TPIContextProvider extends Component {
 	onGamePassed(passed) {
 		const { game } = this.state
 		// 随便给个分,以免重新评测时又出现评星组件(注意:目前game.star没有显示在界面上,如果有则不能这么做)
-		// game.star = 6; 
+		// game.star = 6;
 		this.setState({
 			game: update(game, {star: { $set: 6 }}),
 			currentGamePassed: !!passed
@@ -253,14 +253,14 @@ pop_box_new(htmlvalue, 480, 182);
 			    	const { praise_count, praise } = response.data;
 			    	// challenge.praise_count = praise_tread_count;
 			    	// challenge.user_praise = praise;
-			    	this.setState({ challenge: update(challenge, 
+			    	this.setState({ challenge: update(challenge,
 			    		{
 			    			praise_count: { $set: praise_count },
 			    			user_praise:  { $set: praise },
 			    		})
 			    	})
 			    }
-		    
+
 		  	})
 		  	.catch(function (error) {
 		    	console.log(error);
@@ -286,11 +286,11 @@ pop_box_new(htmlvalue, 480, 182);
 		}
 		const { myshixun } = this.state;
 		// myshixun.system_tip = false;
-		
+
 
 		challenge.path = path;
 		const newChallenge = this.handleChallengePath(challenge);
-		this.setState({ challenge: newChallenge, 
+		this.setState({ challenge: newChallenge,
 			myshixun: update(myshixun, {system_tip: { $set: false }}),
 		})
 	}
@@ -313,7 +313,7 @@ pop_box_new(htmlvalue, 480, 182);
 
 	newResData2OldResData(newResData) {
 		newResData.latest_output = newResData.last_compile_output
-		// newResData.power 
+		// newResData.power
 		newResData.record = newResData.record_onsume_time
 
 		// 老版用的hide_code
@@ -329,7 +329,7 @@ pop_box_new(htmlvalue, 480, 182);
 		newResData.output_sets.test_sets = newResData.test_sets 	// JSON.stringify()
 		newResData.output_sets.test_sets_count = newResData.test_sets_count
 		// newResData.output_sets.had_passed_testsests_error_count = newResData.sets_error_count
-		newResData.output_sets.had_passed_testsests_error_count = newResData.test_sets_count 
+		newResData.output_sets.had_passed_testsests_error_count = newResData.test_sets_count
 				- newResData.sets_error_count
 		//			allowed_hidden_testset
 		//			sets_error_count
@@ -354,8 +354,8 @@ pop_box_new(htmlvalue, 480, 182);
 	    let output_sets = resData.output_sets;
 		if (resData.st === 0) {		// 代码题
 	    	challenge = this.handleChallengePath(challenge)
-	  		
-	  		const mirror_name = (resData.mirror_name && resData.mirror_name.join) 
+
+	  		const mirror_name = (resData.mirror_name && resData.mirror_name.join)
 	  				? resData.mirror_name.join(';') : (resData.mirror_name || '');
 	  		if (mirror_name.indexOf('Html') !== -1) {
 	  			challenge.isHtml = true;
@@ -364,7 +364,7 @@ pop_box_new(htmlvalue, 480, 182);
 	  			challenge.isWeb = true;
 	  		} else if (mirror_name.indexOf('Android') !== -1) {
 	  			challenge.isAndroid = true;
-	  		} 
+	  		}
 
 	  		if (output_sets && output_sets.test_sets && typeof output_sets.test_sets == 'string') {
 	  			const test_sets_array = JSON.parse("[" + output_sets.test_sets + "]");
@@ -378,7 +378,7 @@ pop_box_new(htmlvalue, 480, 182);
 	    	const $ = window.$
 			window.setTimeout(()=>{
 				var lens = $("#choiceRepositoryView textarea").length;
-				
+
 		        for(var i = 1; i <= lens; i++){
 		            window.editormd.markdownToHTML("choose_subject_" + i, {
 		                htmlDecode: "style,script,iframe",  // you can filter tags decode
@@ -404,14 +404,14 @@ pop_box_new(htmlvalue, 480, 182);
 			game.isPassThrough = true
 		}
   		resData.game = game;
-		
+
   		const { tpm_cases_modified, tpm_modified, tpm_script_modified, myshixun } = resData;
   		if (myshixun.system_tip) {
 			// system_tip为true的时候 不弹框提示用户更新
   			resData.showUpdateDialog = false
   		} else {
   			let needUpdateScript = (tpm_modified || tpm_script_modified) && challenge.st === 0;
-  			resData.showUpdateDialog = needUpdateScript || tpm_cases_modified 
+  			resData.showUpdateDialog = needUpdateScript || tpm_cases_modified
   		}
 
 		/**
@@ -458,7 +458,7 @@ pop_box_new(htmlvalue, 480, 182);
 		// const EDU_NORMAL = 7  // 普通用户
 
 
-		/** 
+		/**
 		    EDU_ADMIN = 1       # 超级管理员
 			EDU_BUSINESS = 2    # 运营人员
 			EDU_SHIXUN_MANAGER = 3 # 实训管理员
@@ -467,7 +467,7 @@ pop_box_new(htmlvalue, 480, 182);
 			EDU_GAME_MANAGER = 6     # TPI的创建者
 			EDU_TEACHER = 7 # 平台老师,但是未认证
 			EDU_NORMAL = 8  # 普通用户
-		*/ 
+		*/
 
 		// myshixun_manager power is_teacher
 		resData.power = 0
@@ -495,8 +495,8 @@ pop_box_new(htmlvalue, 480, 182);
 		} else if (resData.user.identity === EDU_TEACHER) {
 			// resData.is_teacher = true
 		} else if (resData.user.identity === EDU_NORMAL) {
-			
-		} 
+
+		}
 		return resData
 	}
 
@@ -524,7 +524,7 @@ pop_box_new(htmlvalue, 480, 182);
 	    	loading: true,
 	    	currentGamePassed: false,  // 切换game时重置passed字段
 	    })
-		
+
 		// test
 		// var data = {"st":0,"discusses_count":0,"game_count":3,"record_onsume_time":0.36,"prev_game":null,"next_game":"7p9xwo2hklqv","praise_count":0,"user_praise":false,"time_limit":20,"tomcat_url":"http://47.96.157.89","is_teacher":false,"myshixun_manager":true,"game":{"id":2192828,"myshixun_id":580911,"user_id":57844,"created_at":"2019-09-03T15:50:49.000+08:00","updated_at":"2019-09-03T15:51:05.000+08:00","status":2,"final_score":0,"challenge_id":10010,"open_time":"2019-09-03T15:50:49.000+08:00","identifier":"hknvz4oaw825","answer_open":0,"end_time":"2019-09-03T15:51:04.000+08:00","retry_status":0,"resubmit_identifier":null,"test_sets_view":false,"picture_path":null,"accuracy":1.0,"modify_time":"2019-09-03T15:23:33.000+08:00","star":0,"cost_time":14,"evaluate_count":1,"answer_deduction":0},"challenge":{"id":10010,"shixun_id":3516,"subject":"1.1 列表操作","position":1,"task_pass":"[TOC]\n\n---\n\n####任务描述\n\n\n数据集a包含1-10共10个整数,请以a为输入数据,编写python程序,实现如下功能:\n①\t用2种方法输出a中所有奇数\n②\t输出大于3,小于7的偶数\n③\t用2种方法输出[1,2,3,…10,11,…20]\n④\t输出a的最大值、最小值。\n⑤\t用2种方法输出[10,9,…2,1]\n⑥\t输出[1,2,3,1,2,3,1,2,3,1,2,3]\n\n\n####相关知识\n\n\n请自行学习相关知识\n\n\n---\n开始你的任务吧,祝你成功!","score":100,"path":"1-1-stu.py","st":0,"web_route":null,"modify_time":"2019-09-03T15:23:33.000+08:00","exec_time":20,"praises_count":0},"shixun":{"id":3516,"name":"作业1——Python程序设计","user_id":77620,"gpid":null,"visits":23,"created_at":"2019-09-03T14:18:17.000+08:00","updated_at":"2019-09-03T15:58:16.000+08:00","status":0,"language":null,"authentication":false,"identifier":"6lzjig58","trainee":1,"major_id":null,"webssh":2,"homepage_show":false,"hidden":false,"fork_from":null,"can_copy":true,"modify_time":"2019-09-03T14:18:17.000+08:00","reset_time":"2019-09-03T14:18:17.000+08:00","publish_time":null,"closer_id":null,"end_time":null,"git_url":null,"vnc":null,"myshixuns_count":3,"challenges_count":3,"use_scope":0,"mirror_script_id":20,"image_text":null,"code_hidden":false,"task_pass":true,"exec_time":20,"test_set_permission":true,"sigle_training":false,"hide_code":false,"multi_webssh":false,"excute_time":null,"repo_name":"p09218567/6lzjig58","averge_star":5.0,"opening_time":null,"users_count":1,"forbid_copy":false,"pod_life":0},"myshixun":{"id":580911,"shixun_id":3516,"is_public":true,"user_id":57844,"gpid":null,"created_at":"2019-09-03T15:50:49.000+08:00","updated_at":"2019-09-03T15:59:04.000+08:00","status":0,"identifier":"k36hm4rwav","commit_id":"f25e1713882156480fc45ce0af57eff395a5037f","modify_time":"2019-09-03T14:18:17.000+08:00","reset_time":"2019-09-03T14:18:17.000+08:00","system_tip":false,"git_url":null,"onclick_time":"2019-09-03T15:50:49.000+08:00","repo_name":"p53276410/k36hm4rwav20190903155049"},"user":{"user_id":57844,"login":"p53276410","name":"文振乾","grade":24624,"identity":1,"image_url":"avatars/User/57844","school":"EduCoder团队"},"tpm_modified":true,"tpm_cases_modified":false,"mirror_name":["Python3.6"],"has_answer":false,"test_sets":[{"is_public":true,"result":true,"input":"","output":"result of a:\n[1, 3, 5, 7, 9]\n[1, 3, 5, 7, 9]\nresult of b:\n[2, 4, 6, 8, 10]\nresult of c:\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]\nresult of d:\nThe minimum is:1\nThe maxium is:10\nresult of e:\n[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\nresult of f:\n[10, 9, 8, 10, 9, 8, 10, 9, 8, 10, 9, 8]\n","actual_output":"result of a:\r\n[1, 3, 5, 7, 9]\r\n[1, 3, 5, 7, 9]\r\nresult of b:\r\n[2, 4, 6, 8, 10]\r\nresult of c:\r\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]\r\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]\r\nresult of d:\r\nThe minimum is:1\r\nThe maxium is:10\r\nresult of e:\r\n[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]\r\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\r\nresult of f:\r\n[10, 9, 8, 10, 9, 8, 10, 9, 8, 10, 9, 8]\r\n","compile_success":1,"ts_time":0.05,"ts_mem":8.77}],"allowed_unlock":true,"last_compile_output":"compile successfully","test_sets_count":1,"sets_error_count":0}
 		// data.test_sets[0].actual_output = data.test_sets[0].actual_output.replace(/\r\n/g, '\n')
@@ -534,7 +534,7 @@ pop_box_new(htmlvalue, 480, 182);
 		// data.vnc_url= "http://47.96.157.89:41158/vnc_lite.html?password=headless"
 
 		// this._handleResponseData(data)
-		// return 
+		// return
 
 		axios.get(url, {
 				// https://stackoverflow.com/questions/48861290/the-value-of-the-access-control-allow-origin-header-in-the-response-must-not-b
@@ -550,7 +550,7 @@ pop_box_new(htmlvalue, 480, 182);
 			    	return;
 			    }
 			    if (response.data.status == 404) {
-					// 如果第一次发生404,则隔1s后再调用一次本接口;(因为ucloud主从同步可能有延迟)	
+					// 如果第一次发生404,则隔1s后再调用一次本接口;(因为ucloud主从同步可能有延迟)
 					if (!noTimeout) {
 						setTimeout(() => {
 							this.fetchAll(stageId, true)
@@ -562,12 +562,12 @@ pop_box_new(htmlvalue, 480, 182);
 			    }
 
 			    this._handleResponseData(response.data)
-		    
+
 		  	})
 		  	.catch(function (error) {
 		    	console.log(error);
 		  	});
-	
+
 	}
 
 	readGameAnswer(resData) {
@@ -583,7 +583,7 @@ pop_box_new(htmlvalue, 480, 182);
 				grade: resData.grade
 			})
 		}
-		
+
 	}
 	closeTaskResultLayer() {
 		this.setState({
@@ -605,7 +605,7 @@ pop_box_new(htmlvalue, 480, 182);
 
 			currentGamePassed = true;
 
-			
+
 			this._updateCostTime(true, true);
 		}
 		this.setState({
@@ -618,14 +618,14 @@ pop_box_new(htmlvalue, 480, 182);
 			currentPassedGameGainGold: gold,
 			currentPassedGameGainExperience: experience,
 		})
-	}	
+	}
 	initDisplayInterval = () => {
 		const challenge = this.state.challenge
 		if (this.showWebDisplayButtonTimeout) {
 			window.clearTimeout(this.showWebDisplayButtonTimeout)
 		}
 		this.showWebDisplayButtonTimeout = window.setTimeout(() => {
-			this.setState({ challenge: update(challenge, 
+			this.setState({ challenge: update(challenge,
 				{
 					showWebDisplayButton: { $set: false },
 				})
@@ -650,7 +650,7 @@ pop_box_new(htmlvalue, 480, 182);
 				this.displayInterval = null
 				return;
 			}
-			
+
 			remain -= 1;
 		}, 1000)
 	}
@@ -716,7 +716,7 @@ pop_box_new(htmlvalue, 480, 182);
 
 		const currentGamePassed = this.props.game !== 2 && status === 2
 
-		
+
 
 		// 评测通过了,立即同步costTime
 		currentGamePassed && this._updateCostTime(true, true);
@@ -738,7 +738,7 @@ pop_box_new(htmlvalue, 480, 182);
   		// 	const test_sets_array = JSON.parse("[" + output_sets.test_sets + "]");
   		// 	output_sets.test_sets_array = test_sets_array;
   		// }
-		
+
 		//   检查是否编译通过
 		let compileSuccess = false;
 		if (test_sets && test_sets.length) {
@@ -754,7 +754,7 @@ pop_box_new(htmlvalue, 480, 182);
 		if (currentGamePassed) {
 			game.status = 2;
 			// game.isPassThrough = true
-			game.next_game = next_game;	
+			game.next_game = next_game;
 		} else {
 			this.showDialog({
 				contentText: <div>
@@ -764,7 +764,7 @@ pop_box_new(htmlvalue, 480, 182);
 				isSingleButton: true
 			})
 		}
-		
+
 
 		this.setState({
 			testSetsExpandedArray: testSetsExpandedArrayInitVal.slice(0), // 重置测试集展开状态
@@ -775,12 +775,12 @@ pop_box_new(htmlvalue, 480, 182);
 			output_sets,
 			game,
 			next_game,
-			
+
 			latest_output: last_compile_output,
 			record: record_consume_time,
 			grade,
 			had_done,
-			
+
 		})
 	}
 	resetTestSetsExpandedArray = () => {
@@ -809,15 +809,15 @@ pop_box_new(htmlvalue, 480, 182);
 					output_sets = Object.assign({}, output_sets);
 					// const test_sets_array = JSON.parse("[" + response.data.test_sets + "]");
 	  				output_sets.test_sets_array = response.data.test_sets;
-			    	this.setState({ 
+			    	this.setState({
 						output_sets: output_sets,
 			    		grade: this.state.grade + deltaScore,
-			    		game : update(game, {test_sets_view: { $set: true }}), 
-			    		testSetsExpandedArray: testSetsExpandedArrayInitVal.slice(0) 
+			    		game : update(game, {test_sets_view: { $set: true }}),
+			    		testSetsExpandedArray: testSetsExpandedArrayInitVal.slice(0)
 			    	})
 			    	this.handleGdialogClose();
 			    }
-		    
+
 		  	})
 		  	.catch(function (error) {
 		    	console.log(error);
@@ -841,10 +841,10 @@ pop_box_new(htmlvalue, 480, 182);
 		})
 	}
 
-	/* 
+	/*
         TODO 写成HOC组件,更好复用
 		全局的Dialog this.props.showDialog调用即可
-		@param contentText  dialog显示的提示文本 
+		@param contentText  dialog显示的提示文本
 		@param callback 	确定按钮回调方法
 		@param moreButtonsRender  除了“确定”、“取消”按钮外的其他按钮
 		@param okButtonText  “确定”按钮显示文本,如 继续查看
@@ -908,13 +908,13 @@ pop_box_new(htmlvalue, 480, 182);
 
 						match: this.props.match
 					}}
-				>	
+				>
 					<Dialog
 						id="tpi-dialog"
 			          	open={this.state.gDialogOpen}
 						disableEscapeKeyDown={true}
 			          	onClose={() => this.handleGdialogClose()}
-			        >	
+			        >
 				       	<DialogTitle id="alert-dialog-title">{"提示"}</DialogTitle>
 			          	<DialogContent id="dialog-content">
 				            <DialogContentText id="alert-dialog-description" style={{textAlign: 'center'}}>
@@ -930,7 +930,7 @@ pop_box_new(htmlvalue, 480, 182);
 													>知道啦</a>
 												</div> :
 										<React.Fragment>
-											<Button onClick={() => this.handleGdialogClose()} color="primary" 
+											<Button onClick={() => this.handleGdialogClose()} color="primary"
 													className={`${classes.button} ${classes.buttonGray} ${classes.borderRadiusNone}`}>
 												关闭
 											</Button>
@@ -938,7 +938,7 @@ pop_box_new(htmlvalue, 480, 182);
 													onClick={() => this.onGdialogOkBtnClick() } color="primary" autoFocus>
 														{ this.okButtonText ? this.okButtonText : '确定' }
 											</Button>
-										</React.Fragment>	}									
+										</React.Fragment>	}
 										{this.moreButtonsRender && this.moreButtonsRender()}
 			          	</DialogActions>
 			        </Dialog>
diff --git a/public/react/src/modules/page/component/monaco/TPIMonaco.js b/public/react/src/modules/page/component/monaco/TPIMonaco.js
index c71f13361..906765056 100644
--- a/public/react/src/modules/page/component/monaco/TPIMonaco.js
+++ b/public/react/src/modules/page/component/monaco/TPIMonaco.js
@@ -23,7 +23,7 @@ import { isThisSecond } from 'date-fns';
 monaco.editor.defineTheme('myCoolTheme', {
       base: 'vs', // vs、vs-dark、hc-black
       inherit: true,
-      rules: [       
+      rules: [
         { token: 'green', background: 'FF0000', foreground: '00FF00', fontStyle: 'italic'},
         { token: 'red', foreground: 'FF0000' , fontStyle: 'bold underline'},
         { background: '#121c23' },
@@ -37,7 +37,7 @@ monaco.editor.defineTheme('myCoolTheme', {
         // 'editor.selectionHighlightBorder': '#ffffff',
         // 'input.border': '#ffffff',
         'editor.lineHighlightBorder': '#222c34',        // .current-line
-        
+
         // 'editor.selectionBackground': '#FFFF0030',
         // 'editor.selectionHighlightBackground' :'#0000FFFF',
       }
@@ -60,7 +60,7 @@ function loadMonacoResouce(callback) {
         // https://github.com/Microsoft/monaco-editor/issues/82
         // window.require = { paths: { 'vs': '../node_modules/monaco-editor/min/vs' } };
         // window.require = { paths: { 'vs': `${_url_origin}${prefix}/js/monaco/vs` } };
-       
+
         // $('head').append($('<link rel="stylesheet" type="text/css" />')
         //     .attr('href', `${_url_origin}${prefix}/js/monaco/vs/editor/editor.main.css`));
 
@@ -68,7 +68,7 @@ function loadMonacoResouce(callback) {
         //     cache: true
         // });
         // $.when(
-            
+
         //     // $.getScript( `${_url_origin}${prefix}/js/monaco/vs/language/typescript/tsMode.js` ),
         //     // $.getScript( `${_url_origin}${prefix}/js/monaco/vs/basic-languages/javascript/javascript.js` ),
         //     $.getScript( `${_url_origin}${prefix}/js/monaco/vs/basic-languages/python/python.js` ),
@@ -79,7 +79,7 @@ function loadMonacoResouce(callback) {
         //     $.getScript( `${_url_origin}${prefix}/js/monaco/vs/editor/editor.main.js` ),
         //     $.Deferred(function( deferred ){
         //         console.log('Deferred')
-                
+
         //         // TODO 暂时放这里
         //         $( deferred.resolve );
         //         checkIfLoaded(callback);
@@ -126,7 +126,7 @@ function checkIfLoaded (callback) {
 }
 
 /*
-    language  
+    language
     javascript css less scss html typescript
     java ruby vb r python php perl go cpp csharp
     sql pgsql mysql
@@ -155,7 +155,7 @@ function checkIfLoaded (callback) {
                 version: 3,
                 singleLineStringErrors: false
     },
-*/ 
+*/
 const mirror2LanguageMap = {
     'JFinal': 'java',
     'Java': 'java',
@@ -194,10 +194,10 @@ function getLanguageByMirrorName(mirror_name) {
 
 let notCallCodeMirrorOnChangeFlag = false;
 
-/** 
-    props : 
+/**
+    props :
         mirror_name  决定语言
-        isEditablePath  
+        isEditablePath
         repositoryCode
 
         codemirrorDidMount
@@ -218,7 +218,7 @@ class TPIMonaco extends Component {
             autoCompleteSwitch: fromStore('autoCompleteSwitch', true),
         }
 	}
-    
+
 	componentDidUpdate(prevProps, prevState, snapshot) {
         const { mirror_name } = this.props
         const editor_monaco = this.editor_monaco;
@@ -234,20 +234,20 @@ class TPIMonaco extends Component {
             } else {
                 editor_monaco.updateOptions({readOnly: true})
             }
-        
-        } else if (editor_monaco && prevProps.codeLoading === true && this.props.codeLoading === false 
+
+        } else if (editor_monaco && prevProps.codeLoading === true && this.props.codeLoading === false
                 ) {
             if (this.props.repositoryCode != editor_monaco.getValue()) {
-                // newProps.repositoryCode !== this.props.repositoryCode && 
+                // newProps.repositoryCode !== this.props.repositoryCode &&
                 notCallCodeMirrorOnChangeFlag = true;
-                
+
                 // 重要:setState(因获取代码、重置代码等接口引起的调用)调用引起的变化才需要setValue
                 editor_monaco.setValue(this.props.repositoryCode)
             }
 
             // 代码没变也需要layout,可能从命令行自动切回了代码tab
             editor_monaco.layout();
-            
+
             // Clears the editor's undo history.
             // TODO
             // extend_editor.clearHistory()
@@ -258,7 +258,7 @@ class TPIMonaco extends Component {
         // 注意销毁,不然会出现不能编辑的bug
         this.editor_monaco && this.editor_monaco.dispose()
     }
-    
+
 	componentDidMount() {
         checkIfLoaded(() => {
             let value = [
@@ -290,7 +290,7 @@ class TPIMonaco extends Component {
                 // http://testeduplus2.educoder.net/react/build/static/node_modules/_monaco-editor@0.15.6@monaco-editor/esm/vs/editor/common/config/commonEditorConfig.js
                 // https://github.com/Microsoft/monaco-editor/issues/29
                 scrollBeyondLastLine: false,
- 
+
                 language: lang,
                 // language: 'css',
 
@@ -304,7 +304,7 @@ class TPIMonaco extends Component {
 
             window.editor_monaco = editor;
             this.editor_monaco = editor
-            
+
             // editor.setPosition({ lineNumber: 2, column: 30 });
 
             // editor.model.onDidChangeContent((event) => {
@@ -323,27 +323,27 @@ class TPIMonaco extends Component {
                 this.props.onRepositoryCodeUpdate && this.props.onRepositoryCodeUpdate(editor.getValue())
             })
             this.props.codemirrorDidMount && this.props.codemirrorDidMount()
-            
+
             if(this.props.shixun && this.props.shixun.forbid_copy == true) {
                 // 禁用粘贴
                 // https://github.com/Microsoft/monaco-editor/issues/100
                 window.editor_monaco.onDidPaste( (a, b, c) => { window.__pastePosition = a })
-                window.addEventListener('paste', (a, b, c) => { 
-                    const selection = window.editor_monaco.getSelection(); 
-                    const range = new monaco.Range( 
-                        window.__pastePosition.startLineNumber || selection.endLineNumber, 
-                        window.__pastePosition.startColumn || selection.endColumn, 
-                        window.__pastePosition.endLineNumber || selection.endLineNumber, 
-                        window.__pastePosition.endColumn || selection.endColumn,); 
+                window.addEventListener('paste', (a, b, c) => {
+                    const selection = window.editor_monaco.getSelection();
+                    const range = new monaco.Range(
+                        window.__pastePosition.startLineNumber || selection.endLineNumber,
+                        window.__pastePosition.startColumn || selection.endColumn,
+                        window.__pastePosition.endLineNumber || selection.endLineNumber,
+                        window.__pastePosition.endColumn || selection.endColumn,);
                     window.editor_monaco.executeEdits('', [{range, text: ''}] )
 
-                })  
+                })
 
                 // 禁用复制
                 window.editor_monaco.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_C, () => null);
                 window.editor_monaco.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_V, () => null);
             }
-             
+
 
             setTimeout(() => {
                 editor.layout();
@@ -352,23 +352,23 @@ class TPIMonaco extends Component {
 
             window.editor_monaco.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S, () => {
                 this.props.doFileUpdateRequestOnCodeMirrorBlur();
-                return false;                
+                return false;
             });
-            window.editor_monaco.onDidBlurEditorWidget(() => { 
+            window.editor_monaco.onDidBlurEditorWidget(() => {
                 this.props.onEditBlur && this.props.onEditBlur();
-            })  
+            })
         })
 
-        // window.document.onkeydown = (e) => { 
+        // window.document.onkeydown = (e) => {
         //     e = window.event || e;
-        //     if(e.keyCode== 83 && e.ctrlKey){ 
+        //     if(e.keyCode== 83 && e.ctrlKey){
         //     /*延迟,兼容FF浏览器  */
         //         // setTimeout(function(){
-        //             // alert('ctrl+s'); 
-        //         // },1); 
+        //             // alert('ctrl+s');
+        //         // },1);
         //         this.props.doFileUpdateRequestOnCodeMirrorBlur();
-        //         return false;      
-        //     }    
+        //         return false;
+        //     }
         // };
     }
     onFontSizeChange = (value) => {
@@ -390,7 +390,7 @@ class TPIMonaco extends Component {
 	render() {
 		const { repositoryCode, showSettingDrawer, settingDrawerOpen } = this.props;
 		const { cmFontSize } = this.state;
-         
+
 	    return (
             <React.Fragment>
                 <Drawer
@@ -400,10 +400,10 @@ class TPIMonaco extends Component {
 
                     width={260}
                     open={settingDrawerOpen}
-                    
+
                     onClose={() => showSettingDrawer( false )}
                 >
-                    <TPICodeSetting {...this.props} {...this.state} 
+                    <TPICodeSetting {...this.props} {...this.state}
                         onFontSizeChange={this.onFontSizeChange}
                         onCodeModeChange={this.onCodeModeChange}
                         onAutoCompleteSwitchChange={this.onAutoCompleteSwitchChange}
@@ -436,7 +436,7 @@ export var EDITOR_DEFAULTS = {
     wordWrap: 'off',
     wordWrapColumn: 80,
     wordWrapMinified: true,
-    wrappingIndent: 1 
+    wrappingIndent: 1
     wordWrapBreakBeforeCharacters: '([{‘“〈《「『【〔([{「£¥$£¥++',
     wordWrapBreakAfterCharacters: ' \t})]?|&,;¢°′″‰℃、。。、¢,.:;?!%・・ゝゞヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻ァィゥェォャュョッー”〉》」』】〕)]}」',
     wordWrapBreakObtrusiveCharacters: '.',
@@ -555,4 +555,4 @@ export var EDITOR_DEFAULTS = {
 };
 
 
- */
\ No newline at end of file
+ */
diff --git a/public/react/src/modules/tpm/TPMDataset.js b/public/react/src/modules/tpm/TPMDataset.js
index bf35744fe..6e908df92 100644
--- a/public/react/src/modules/tpm/TPMDataset.js
+++ b/public/react/src/modules/tpm/TPMDataset.js
@@ -1,12 +1,15 @@
 import React, {Component} from 'react';
 import {Redirect} from 'react-router';
-import {List, Typography, Tag, Modal, Radio, Checkbox, Table, Pagination} from 'antd';
+import {List, Typography, Tag, Modal, Radio, Checkbox, Table, Pagination,Upload,notification} from 'antd';
 import {  NoneData } from 'educoder'
 
 import TPMRightSection from './component/TPMRightSection';
 import TPMNav from './component/TPMNav';
 import axios from 'axios';
 import './tpmmodel/tpmmodel.css'
+import {getUploadActionUrl} from 'educoder';
+
+const confirm = Modal.confirm;
 
 class TPMDataset extends Component {
 	constructor(props) {
@@ -70,6 +73,10 @@ class TPMDataset extends Component {
 			limit: 5,
 			selectedRowKeys: [],
 			mylistansum:30,
+			collaboratorList:[],
+			fileList:[],
+			file:null,
+
 		}
 	}
 
@@ -93,7 +100,23 @@ class TPMDataset extends Component {
 
 
 	getdatas = () => {
+		let id=this.props.match.params.shixunId;
+
+		let collaborators=`/shixuns/${id}/jupyter_data_sets.json`;
+		axios.get(collaborators).then((response)=> {
+			if(response.status===200){
+				if (response.data.status === 403||response.data.status === 401||response.data.status === 500) {
 
+				}else{
+					this.setState({
+						collaboratorList: response.data
+					});
+				}
+
+			}
+		}).catch((error)=>{
+			console.log(error)
+		});
 
 	}
 
@@ -129,9 +152,81 @@ class TPMDataset extends Component {
 		return className;
 	}
 
+	// 附件相关 START
+	handleChange = (info) => {
+		if(info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
+			let {fileList} = this.state;
+
+			if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
+				console.log("handleChange1");
+				// if(fileList.length===0){
+				let fileLists = info.fileList;
+				this.setState({
+					// fileList:appendFileSizeToUploadFileAll(fileList),
+					fileList: fileLists,
+					deleteisnot: false
+				});
+				// }
+			}
+		}
+	}
+
+	onAttachmentRemove = (file) => {
+		if(!file.percent || file.percent == 100){
+			confirm({
+				title: '确定要删除这个附件吗?',
+				okText: '确定',
+				cancelText: '取消',
+				// content: 'Some descriptions',
+				onOk: () => {
+					console.log("665")
+					this.deleteAttachment(file)
+				},
+				onCancel() {
+					console.log('Cancel');
+				},
+			});
+			return false;
+		}
+
+	}
+
+	deleteAttachment = (file) => {
+		console.log(file);
+		let id=file.response ==undefined ? file.id : file.response.id
+		const url = `/attachements/destroy_files.json`
+		axios.delete(url, {
+			id:[id],
+			})
+			.then((response) => {
+				if (response.data) {
+					const { status } = response.data;
+					if (status == 0) {
+						// console.log('--- success')
+
+						this.setState((state) => {
+
+							const index = state.fileList.indexOf(file);
+							const newFileList = state.fileList.slice();
+							newFileList.splice(index, 1);
+							return {
+								fileList: newFileList,
+								deleteisnot:true
+							};
+						});
+					}
+				}
+			})
+			.catch(function (error) {
+				console.log(error);
+			});
+	}
+
+
+
 	render() {
 		const {tpmLoading, shixun, user, match} = this.props;
-		const {columns, datas, page, limit, selectedRowKeys,mylistansum} = this.state;
+		const {columns, datas, page, limit, selectedRowKeys,mylistansum,fileList} = this.state;
 		const rowSelection = {
 			selectedRowKeys,
 			onChange: this.onSelectChange,
@@ -140,6 +235,55 @@ class TPMDataset extends Component {
 		// 	disabled: record.name === 'Disabled User', // Column configuration not to be checked
 		// 	name: record.name,
 		// }),
+		let id=this.props.match.params.shixunId;
+		const uploadProps = {
+			width: 600,
+			fileList,
+			data:{
+				attachtype: 2,
+				container_id:this.props.match.params.shixunId,
+				container_type: "Shixun",
+			},
+			multiple: true,
+			// https://github.com/ant-design/ant-design/issues/15505
+			// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
+			// showUploadList: false,
+			action:  `${getUploadActionUrl()}`,
+			onChange: this.handleChange,
+			onRemove: this.onAttachmentRemove,
+			beforeUpload: (file, fileList) => {
+
+				if (this.state.fileList.length >= 1) {
+					return false
+				}
+				// console.log('beforeUpload', file.name);
+				const isLt150M = file.size / 1024 / 1024 < 50;
+				if (!isLt150M) {
+					// this.props.showNotification(`文件大小必须小于50MB`);
+					notification.open(
+						{
+							message: '提示',
+							description:
+								'文件大小必须小于50MB',
+
+						}
+					)
+				}
+				if(this.state.file !== undefined){
+					console.log("763")
+					this.setState({
+						file:file
+					})
+				}else {
+					this.setState({
+						file:file
+					})
+				}
+
+				console.log("handleChange2");
+				return isLt150M;
+			},
+		}
 		return (
 			<React.Fragment>
 				<div className="tpmComment educontent clearfix mt30 mb80">
@@ -157,7 +301,18 @@ class TPMDataset extends Component {
 							<div className="sortinxdirection">
 								<div className="tpmwidth"><Checkbox onChange={this.mysonChange}>全选</Checkbox></div>
 								<div className="tpmwidth xaxisreverseorder">
-									<div className="deletebuttom intermediatecenter "><p className="deletebuttomtest">上传文件</p></div>
+									<style>
+										{
+											`
+											.ant-upload-list{
+											 display:none
+											}
+											`
+										}
+									</style>
+									<div className="deletebuttom intermediatecenter ">	<Upload {...uploadProps}><p className="deletebuttomtest">
+
+										上传文件</p>		</Upload></div>
 									{
 										mylistansum>0?
 											<div
diff --git a/public/react/src/modules/tpm/TPMIndexHOC.js b/public/react/src/modules/tpm/TPMIndexHOC.js
index 774587865..3ac1c19c8 100644
--- a/public/react/src/modules/tpm/TPMIndexHOC.js
+++ b/public/react/src/modules/tpm/TPMIndexHOC.js
@@ -31,7 +31,7 @@ if(window.location.port === "3007"){
 if (!window['indexHOCLoaded']) {
   window.indexHOCLoaded = true;
   //解决首屏加载问题
-  
+
   // $('head').append($('<link rel="stylesheet" type="text/css" />')
   //     .attr('href', `${_url_origin}/stylesheets/educoder/antd.min.css?1525440977`));
   $('head').append($('<link rel="stylesheet" type="text/css" />')
@@ -51,7 +51,7 @@ if (!window['indexHOCLoaded']) {
   // setTimeout(() => {
   //   $('head').append( $('<link rel="stylesheet" type="text/css" />')
   //     .attr('href', `${_url_origin}/stylesheets/css/edu-common.css?1525440977`) );
-      
+
   //   $('head').append( $('<link rel="stylesheet" type="text/css" />')
   //     .attr('href', `${_url_origin}/stylesheets/educoder/edu-all.css?1525440977`) );
   //   $('head').append( $('<link rel="stylesheet" type="text/css" />')
@@ -60,7 +60,7 @@ if (!window['indexHOCLoaded']) {
 
   $("script").append('<script></script>')
     .attr('src', `${_url_origin}/javascripts/jquery-1.8.3-ui-1.9.2-ujs-2.0.3.js?_t=${versionNum}`);
-  
+
 }
   // `${_url_origin}/javascripts/jquery-1.8.3-ui-1.9.2-ujs-2.0.3.js?_t=${versionNum}`
 // TODO css加载完成后再打开页面,行为和tpm其他页面一致
@@ -146,7 +146,7 @@ export function TPMIndexHOC(WrappedComponent) {
       componentWillUnmount() {
         window.removeEventListener('keyup', this.keyupListener)
       }
-      
+
 	    componentDidMount() {
 	    	// console.log("TPMIndexHOC========");
 	    	// console.log(this.props);
@@ -216,7 +216,7 @@ export function TPMIndexHOC(WrappedComponent) {
        this.getAppdata();
       }
       /**
-        课堂权限相关方法,暂时写这里了 ----------------------------------------START 
+        课堂权限相关方法,暂时写这里了 ----------------------------------------START
         ADMIN = 0       # 超级管理员
         CREATOR = 1     # 课程创建者
         PROFESSOR = 2     # 课程老师
@@ -560,7 +560,7 @@ export function TPMIndexHOC(WrappedComponent) {
       checkIfProfessionalCertification = () => {
         return this.state.current_user && this.state.current_user.professional_certification
       }
-      
+
 
 			ShowOnlinePdf = (url) => {
 				return  axios({
@@ -642,14 +642,14 @@ export function TPMIndexHOC(WrappedComponent) {
             isAdminOrCreator:this.isAdminOrCreator,
 						isClassManagement:this.isClassManagement,
 						isCourseAdmin:this.isCourseAdmin,
-            
+
             isAdmin: this.isAdmin,
             isAdminOrTeacher: this.isAdminOrTeacher,
             isStudent: this.isStudent,
             isAdminOrStudent: this.isAdminOrStudent,
             isNotMember: this.isNotMember,
             isCourseEnd: this.isCourseEnd,
-            
+
             isUserid:this.state.coursedata&&this.state.coursedata.userid,
             fetchUser: this.fetchUser,
 
@@ -660,7 +660,7 @@ export function TPMIndexHOC(WrappedComponent) {
             checkIfProfileCompleted: this.checkIfProfileCompleted,
             checkIfProfessionalCertification: this.checkIfProfessionalCertification,
             showProfessionalCertificationDialog: this.showProfessionalCertificationDialog,
-            
+
 						ShowOnlinePdf:(url)=>this.ShowOnlinePdf(url),
 						DownloadFileA:(title,url)=>this.DownloadFileA(title,url),
 						DownloadOpenPdf:(type,url)=>this.DownloadOpenPdf(type,url),
@@ -671,7 +671,7 @@ export function TPMIndexHOC(WrappedComponent) {
 						yslslowCheckresults:this.yslslowCheckresults,
 						yslslowCheckresultsNo:this.yslslowCheckresultsNo,
 						MdifHasAnchorJustScorll:this.MdifHasAnchorJustScorll
-            
+
           };
           // console.log("this.props.mygetHelmetapi");
           // console.log(this.props.mygetHelmetapi);
@@ -742,7 +742,7 @@ export function TPMIndexHOC(WrappedComponent) {
               }
 							`
 						}</style>
-            
+
 
 						<NewHeader {...this.state} {...this.props}></NewHeader>
             <Spin spinning={this.state.globalLoading} delay={0} className="globalSpin"
@@ -765,9 +765,9 @@ export function TPMIndexHOC(WrappedComponent) {
 							{...this.state} {...this.props}
 								Footerdown={Footerdown}
 						/>
-            
+
 		        </div>
 		    );
 	  	}
 	}
-}
\ No newline at end of file
+}
diff --git a/public/react/src/modules/tpm/shixunchild/Challenges/Challengesjupyter.js b/public/react/src/modules/tpm/shixunchild/Challenges/Challengesjupyter.js
index 3b7abf44b..e43007cca 100644
--- a/public/react/src/modules/tpm/shixunchild/Challenges/Challengesjupyter.js
+++ b/public/react/src/modules/tpm/shixunchild/Challenges/Challengesjupyter.js
@@ -19,9 +19,7 @@ import 'antd/lib/pagination/style/index.css';
 import '../shixunchildCss/Challenges.css'
 import ReactDOM from 'react-dom';
 import axios from 'axios';
-
 import AccountProfile from"../../../user/AccountProfile";
-
 const $ = window.$;
 
 
@@ -134,11 +132,21 @@ class Challengesjupyter extends Component {
 	modifyjupyter=()=>{
 		// var ifr =window.parent.frames["frame"].document;
 		// console.log(ifr);
-
-		var ifr = window.document.getElementById("ifr1");
+		// var data = JSON.stringify({
+		//
+		// 	'location': window.location
+		//
+		// 	// etc
+		//
+		// });
+		// let parentDomain = window.location.hostname;
+		// console.log("domain",parentDomain); //localhost
+		// document.domain = data;
+		// document.domain = 'https://47519.test-jupyter.educoder.net';
+		var ifr = document.getElementById("ifr1");
 		console.log("modifyjupyter");
 		console.log(ifr);
-		const iframe = window.document.getElementById('ifr1');
+		const iframe = document.getElementById('ifr1');
 		console.log("modifyjupyter");
 		const frameWindow = iframe.contentWindow;
 		console.log(frameWindow);
@@ -336,7 +344,7 @@ class Challengesjupyter extends Component {
 							{/*https://48888.jupyter.educoder.net/tree?*/}
 
 							<div className="pb47">
-						<iframe src="http://121.41.4.83:48888/tree?" scrolling="no" id="ifr1" name="frame" width="100%" height="700" frameborder="0"
+						<iframe src="http://121.41.4.83:48888/notebooks/Untitled.ipynb?kernel_name=python3"  sandbox="allow-same-origin allow-scripts allow-top-navigation " scrolling="no" id="ifr1" name="frame" width="100%" height="700" frameborder="0"
 
 
 						></iframe>